Merge commit 'f8bcb983ce3a414b76eff58d842e649142c6223d' as 'third_party/googletest'
Change-Id: I2e339370ce7ab07a85787d647fd8bb6bf3e1f55e
diff --git a/.clang-format b/.clang-format
new file mode 100644
index 0000000..11973e3
--- /dev/null
+++ b/.clang-format
@@ -0,0 +1 @@
+BasedOnStyle: 'google'
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..0d20b64
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+*.pyc
diff --git a/README.txt b/README.txt
new file mode 100644
index 0000000..89fa70a
--- /dev/null
+++ b/README.txt
@@ -0,0 +1,7 @@
+This is the README.txt file for git. If you are looking at this file and you got it through SVN, somebody messed up and it should be fixed.
+
+This is team 971's code. We only keep code in this git repository. For details about how we use git, see doc/gitsetup.txt.
+
+You can get to this code in a web browser through <https://robotics.mvla.net/git971> (useful for ie sending links). You have to use your SVN credentials to log in there.
+
+aos/README.txt has some more details about what's where.
diff --git a/aos/README.txt b/aos/README.txt
new file mode 100644
index 0000000..e0f9377
--- /dev/null
+++ b/aos/README.txt
@@ -0,0 +1,34 @@
+see ../README.txt for overall information
+
+[FILES]
+config/ has some configuration files
+ aos.conf has directions for setting up resource limits so you can run the code on any linux machine (the directions are there to keep them with the file)
+ setup_rc_caps.sh is a shell script (you have to run as root) that lets you run the realtime code without installing aos.conf for an individual file
+ starter is an init.d file
+ install it by putting it in /etc/init.d an running "update-rc.d starter defaults"
+ restart it by running "invoke-rc.d starter restart" (doesn't always work very well...)
+ the .config files are for building linux kernels
+
+linux/ has code that only runs on linux systems
+
+common/ is where stuff that runs on all platforms is
+common/input/ is where the framework code for reading stuff into the queues system is
+common/output/ is where the framework for writing things out of the queues system is
+common/messages is where the c++ wrappers for "queues" are
+
+[NOTES]
+Some functions need to be in separate translation units in order for them to be guaranteed to work. As the C standard says,
+ Alternatively, an implementation might perform various optimizations within each translation unit, such
+ that the actual semantics would agree with the abstract semantics only when making function calls across
+ translation unit boundaries. In such an implementation, at the time of each function entry and function
+ return where the calling function and the called function are in different translation units, the values of all
+ externally linked objects and of all objects accessible via pointers therein would agree with the abstract
+ semantics. Furthermore, at the time of each such function entry the values of the parameters of the called
+ function and of all objects accessible via pointers therein would agree with the abstract semantics. In this
+ type of implementation, objects referred to by interrupt service routines activated by the signal function
+ would require explicit specification of volatile storage, as well as other implementation-defined
+ restrictions.
+
+The C++ namespace aos is used for all of the aos code. The crio and linux_code namespaces are for APIs that only make sense on one platform or the other.
+
+Almost everything in aos/ is thread-safe. Files with a main() might not be, and some pieces of code explicitly say at their declaration that they aren't. Also, code which shares non-const pointers (including this) is not generally thread-safe. Other than those exceptions, everything is assumed to be thread-safe so the comments don't say so explicitly.
diff --git a/aos/build/aos.gyp b/aos/build/aos.gyp
new file mode 100644
index 0000000..648718c
--- /dev/null
+++ b/aos/build/aos.gyp
@@ -0,0 +1,63 @@
+# This file has all of the aos targets.
+{
+ 'targets': [
+ # A target for things used by the logging implementation (except die) to
+ # depend on that allows linking successfully with logging calls. However,
+ # executables containing targets that depend on this still need a dependency
+ # on logging somewhere or else they won't link.
+ {
+ 'target_name': 'logging_interface',
+ 'type': 'static_library',
+ 'sources': [
+ '<(AOS)/common/logging/logging_interface.cc',
+ ],
+ 'conditions': [
+ ['OS=="linux"', {
+ 'sources': [
+ '<(AOS)/linux_code/logging/linux_interface.cc',
+ ],
+ 'dependencies': [
+ '<(AOS)/linux_code/linux_code.gyp:complex_thread_local',
+ ],
+ }],
+ ],
+ 'dependencies': [
+ '<(AOS)/common/common.gyp:die',
+ '<(AOS)/common/libc/libc.gyp:aos_strerror',
+ ],
+ 'export_dependent_settings': [
+ '<(AOS)/common/libc/libc.gyp:aos_strerror',
+ ],
+ },
+ {
+ 'target_name': 'logging',
+ 'type': 'static_library',
+ 'sources': [
+ '<(AOS)/common/logging/logging_impl.cc',
+ ],
+ 'conditions': [
+ ['OS=="linux"', {
+ 'sources': [
+ '<(AOS)/linux_code/logging/linux_logging.cc',
+ ],
+ 'dependencies': [
+ '<(AOS)/linux_code/ipc_lib/ipc_lib.gyp:queue',
+ '<(AOS)/common/common.gyp:time',
+ ],
+ 'export_dependent_settings': [
+ '<(AOS)/linux_code/ipc_lib/ipc_lib.gyp:queue',
+ ],
+ }],
+ ],
+ 'dependencies': [
+ '<(AOS)/common/common.gyp:time',
+ '<(AOS)/common/common.gyp:once',
+ 'logging_interface',
+ '<(AOS)/common/common.gyp:queue_types',
+ ],
+ 'export_dependent_settings': [
+ 'logging_interface',
+ ],
+ },
+ ],
+}
diff --git a/aos/build/aos.gypi b/aos/build/aos.gypi
new file mode 100644
index 0000000..8bddd0e
--- /dev/null
+++ b/aos/build/aos.gypi
@@ -0,0 +1,310 @@
+# This file gets passed to gyp with -I so that it gets included everywhere.
+{
+ 'variables': {
+ 'AOS': '<(DEPTH)/aos',
+# A directory with everything in it ignored from source control.
+ 'TMPDIR': '<(DEPTH)/aos/build/temp',
+ 'aos_abs': '<!(readlink -f <(DEPTH)/aos)', # for use in non-path contexts
+# The .gyp file that has targets for the various external libraries.
+ 'EXTERNALS': '<(AOS)/build/externals.gyp',
+# The directory that gets rsynced to the target.
+ 'rsync_dir': '<(PRODUCT_DIR)/outputs',
+# The directory for executables that don't get rsynced to the target.
+ 'other_outputs_dir': '<(PRODUCT_DIR)/other_outputs',
+# The directory that executables that depend on <(EXTERNALS):gtest get put into.
+ 'test_dir': '<(PRODUCT_DIR)/tests',
+
+# Stuck into a variable (with a space on the end) to make disabling it easy.
+ 'ccache': '<!(which ccache) ',
+
+ 'disable_sanitizers': [
+ # Bad alignment is just slow on x86 and traps on ARM, so we'll find
+ # it other ways, and some x86 code does it on purpose.
+ 'alignment',
+ ],
+ },
+ 'conditions': [
+ ['PLATFORM=="linux-arm_frc-gcc"', {
+ 'make_global_settings': [
+ ['CC', '<(ccache)<!(which arm-frc-linux-gnueabi-gcc-4.9)'],
+ ['CXX', '<(ccache)<!(which arm-frc-linux-gnueabi-g++-4.9)'],
+ ['LINK_wrapper', '<!(realpath -s <(AOS)/build/strip_debuglink) <!(which arm-frc-linux-gnueabi-objcopy)'],
+ ],
+ },
+ ], ['PLATFORM=="linux-arm_frc-clang"', {
+ 'variables': {
+ 'arm-clang-symlinks': '<!(realpath -s <(AOS)/build/arm-clang-symlinks)',
+ 'arm-clang-sysroot': '<(arm-clang-symlinks)/sysroot',
+# Flags that should be passed to all compile/link/etc commands.
+ 'platflags': [
+ '-target', 'armv7a-frc-linux-gnueabi',
+ '-mfloat-abi=softfp',
+ '--sysroot=<(arm-clang-sysroot)',
+
+ # TODO(brians): See if it will run with this enabled.
+ #-mhwdiv=arm,thumb
+ ],
+ },
+ 'make_global_settings': [
+ ['CC', '<(ccache)<(arm-clang-symlinks)/bin/clang'],
+ ['CXX', '<(ccache)<(arm-clang-symlinks)/bin/clang++'],
+ ['LINK_wrapper', '<!(realpath -s <(AOS)/build/strip_debuglink) <!(which arm-frc-linux-gnueabi-objcopy)'],
+ ],
+ 'target_defaults': {
+ 'cflags': [
+ '<@(platflags)',
+ ],
+ 'cflags_cc': [
+ '-isystem', '<(arm-clang-sysroot)/include/c++/4.9.1',
+ '-isystem', '<(arm-clang-sysroot)/include/c++/4.9.1/arm-frc-linux-gnueabi',
+ ],
+ 'ldflags': [
+ '<@(platflags)',
+ '-L/usr/lib/x86_64-linux-gnu/gcc/arm-frc-linux-gnueabi/4.9.1',
+ '-L<(arm-clang-symlinks)/more_libs',
+ '-B/usr/lib/x86_64-linux-gnu/gcc/arm-frc-linux-gnueabi/4.9.1',
+ ],
+ },
+ },
+ ], ['PLATFORM=="linux-amd64-clang"', {
+ 'make_global_settings': [
+ ['CC', '<(ccache)<!(which clang)'],
+ ['CXX', '<(ccache)<!(which clang++)'],
+ ],
+ },
+ ], ['PLATFORM=="linux-amd64-gcc_4.8"', {
+ 'make_global_settings': [
+ ['CC', '<(ccache)/opt/clang-3.5/bin/gcc'],
+ ['CXX', '<(ccache)/opt/clang-3.5/bin/g++'],
+ ],
+ },
+ ], ['SANITIZER!="none"', {
+ 'target_defaults': {
+ 'cflags': [
+ '-fsanitize=<(SANITIZER)',
+ ],
+ 'ldflags': [
+ '-fsanitize=<(SANITIZER)',
+ ],
+ 'defines': [
+# GCC doesn't have __has_feature, so we have to use this instead.
+ 'AOS_SANITIZER_<(SANITIZER)',
+ ],
+ },
+ },
+ ], ['SANITIZER!="none" and COMPILER!="gcc"', {
+ 'target_defaults': {
+ 'cflags': [
+ '-fno-sanitize-recover',
+ '-fno-sanitize=<!(echo <(disable_sanitizers) | sed "s/ /,/g")',
+ ],
+ },
+ },
+ ], ['SANITIZER!="thread"', {
+ 'libraries': [
+ '<!(readlink -f <(AOS)/../output/compiled-<(ARCHITECTURE)<(EXTERNALS_EXTRA)/gperftools-2.3-prefix/lib/libtcmalloc.a)',
+ '<!(readlink -f <(AOS)/../output/compiled-<(ARCHITECTURE)<(EXTERNALS_EXTRA)/libunwind-1.1-prefix/lib/libunwind.a)',
+ ],
+ 'defines': [
+ 'TCMALLOC',
+ ],
+ },
+ ], ['EXTERNALS_EXTRA=="-fPIE"', {
+ 'target_defaults': {
+ 'cflags': [
+ '-fPIE',
+ ],
+ 'ldflags': [
+ '-fPIE',
+ ],
+ 'link_settings': {
+ 'ldflags': [
+ '-pie',
+ ],
+ },
+ },
+ },
+ ], ['SANITIZER=="memory"', {
+ 'target_defaults': {
+ 'cflags': [
+ '-fsanitize-memory-track-origins',
+ ],
+ 'ldflags': [
+ '-fsanitize-memory-track-origins',
+ ],
+ },
+ },
+ ],
+ ],
+ 'target_defaults': {
+ 'defines': [
+ '__STDC_FORMAT_MACROS',
+ '__STDC_CONSTANT_MACROS',
+ '__STDC_LIMIT_MACROS',
+ 'AOS_COMPILER_<!(echo <(FULL_COMPILER) | sed \'s/\./_/g\')',
+ 'AOS_ARCHITECTURE_<(ARCHITECTURE)',
+ '_FILE_OFFSET_BITS=64',
+ ],
+ 'ldflags': [
+ '-pipe',
+ '-pthread',
+ ],
+ 'libraries': [
+ '-lm',
+ '-lrt',
+ ],
+ 'cflags': [
+ '-pipe',
+
+ '-pthread',
+
+ '-Wall',
+ '-Wextra',
+ '-Wswitch-enum',
+ '-Wpointer-arith',
+ '-Wstrict-aliasing=2',
+ '-Wcast-qual',
+ '-Wcast-align',
+ '-Wwrite-strings',
+ '-Wtype-limits',
+ '-Wsign-compare',
+ '-Wformat=2',
+ '-Werror',
+
+ '-ggdb3',
+ # Generate debugging info that gdb 7.4.1 can understand (Wheezy).
+ # TODO(Brian): Remove this once we upgrade to Jessie.
+ '-gdwarf-3',
+ ],
+ 'cflags_c': [
+ '-std=gnu99',
+ ],
+ 'cflags_cc': [
+ '-std=gnu++11',
+ ],
+ 'include_dirs': [
+ '<(DEPTH)',
+ ],
+ # These have to be here because apparently gyp evaluates target_conditions
+ # even if the target is never used.
+ 'variables': {
+ # Set this to 1 to disable rsyncing the file to the target.
+ 'no_rsync%': 0,
+ # Set this to 1 if this file is a test that should not be run by
+ # `build.py tests`.
+ 'is_special_test%': 0,
+ },
+ 'conditions': [
+ ['DEBUG=="yes"', {
+ 'defines': [
+ 'AOS_DEBUG=1',
+ ],
+ 'conditions': [
+ ['COMPILER!="clang"', {
+ 'cflags': [
+ '-Og',
+ ],
+ },
+ ], ['COMPILER=="clang" and SANITIZER=="none"', {
+ 'cflags': [
+ '-O0',
+ ],
+ },
+ ], ['COMPILER=="clang" and SANITIZER!="none"', {
+ 'cflags': [
+ '-O1',
+ ],
+ }
+ ]
+ ],
+ 'cflags': [
+ '-fno-omit-frame-pointer',
+ ],
+ }, { # 'DEBUG=="no"'
+ 'defines': [
+ 'AOS_DEBUG=0',
+ '_FORTIFY_SOURCE=2',
+ ],
+ 'conditions': [
+ # TODO(Brian): Remove this special case once we get a new enough
+ # GCC here that it supports -Oz.
+ ['COMPILER=="gcc" and ARCHITECTURE=="arm_frc"', {
+ 'cflags': [
+ '-Os',
+ ],
+ 'ldflags': [
+ '-Os',
+ ],
+ }, {
+ 'cflags': [
+ '-Oz',
+ ],
+ 'ldflags': [
+ '-Oz',
+ ],
+ }
+ ], ['ARCHITECTURE=="amd64"', {
+ 'cflags': [
+ '-fstack-protector-all',
+ ],
+ }
+ ],
+ ],
+ 'cflags': [
+ '-fomit-frame-pointer',
+ ],
+ }
+ ], ['OS=="linux" and ARCHITECTURE=="arm" and COMPILER=="gcc" and DEBUG=="yes"', {
+ 'cflags': [
+ # GCC doesn't like letting us use r7 (which is also the frame
+ # pointer) to pass the syscall number to the kernel even when
+ # it's marked as clobbered.
+ # See <https://bugzilla.mozilla.org/show_bug.cgi?id=633436> for
+ # some more discussion.
+ '-fomit-frame-pointer',
+ ],
+ }
+ ],
+ ['ARCHITECTURE=="arm"', {
+ 'cflags': [
+ '-mcpu=cortex-a9',
+ '-mfpu=neon',
+ '-mfloat-abi=softfp',
+ ],
+ 'ldflags': [
+ '-mcpu=cortex-a9',
+ '-mfpu=neon',
+ '-mfloat-abi=softfp',
+ ],
+ }],
+ ['COMPILER=="gcc"', {
+ 'cflags': [
+ '-Wunused-local-typedefs',
+ ],
+ 'defines': [
+ '__has_feature(n)=0'
+ ],
+ }], ['COMPILER=="clang"', {
+ 'cflags': [
+ '-fcolor-diagnostics',
+ '-fmessage-length=80',
+ '-fmacro-backtrace-limit=0',
+ ],
+ 'defines': [
+ # This tells clang's optimizer the same thing.
+ '__builtin_assume_aligned(p, a)=({ const typeof(p) my_p_ = (p); ((((uintptr_t)my_p_ % (a)) == 0u) ? my_p_ : (__builtin_unreachable(), (my_p_))); })',
+ ],
+ }],
+ ],
+ 'target_conditions': [
+# Default to putting outputs into rsync_dir.
+ ['no_rsync==0 and _type!="static_library"', {
+ 'product_dir': '<(rsync_dir)',
+ },
+ ], ['no_rsync==1 and _type!="static_library"', {
+ 'product_dir': '<(other_outputs_dir)',
+ },
+ ],
+ ],
+ },
+}
diff --git a/aos/build/aos_all.gyp b/aos/build/aos_all.gyp
new file mode 100644
index 0000000..195997a
--- /dev/null
+++ b/aos/build/aos_all.gyp
@@ -0,0 +1,54 @@
+# This file has the executables etc that AOS builds.
+# User .gyp files for the prime should depend on :Prime.
+{
+ 'targets': [
+ {
+ 'target_name': 'Prime',
+ 'type': 'none',
+ 'variables': {
+ 'no_rsync': 1,
+ },
+ 'dependencies': [
+ 'Common',
+ '<(AOS)/linux_code/linux_code.gyp:core',
+ '<(AOS)/linux_code/logging/logging.gyp:binary_log_writer',
+ '<(AOS)/linux_code/logging/logging.gyp:log_streamer',
+ '<(AOS)/linux_code/logging/logging.gyp:log_displayer',
+ '<(AOS)/linux_code/ipc_lib/ipc_lib.gyp:raw_queue_test',
+ '<(AOS)/linux_code/ipc_lib/ipc_lib.gyp:ipc_stress_test',
+ '<(AOS)/linux_code/starter/starter.gyp:starter_exe',
+ '<(AOS)/linux_code/linux_code.gyp:complex_thread_local_test',
+ '<(AOS)/common/common.gyp:stl_mutex_test',
+ '<(AOS)/linux_code/linux_code.gyp:dump_rtprio',
+ '<(AOS)/common/actions/actions.gyp:action_test',
+ ],
+ },
+ {
+ 'target_name': 'Common',
+ 'type': 'none',
+ 'variables': {
+ 'no_rsync': 1,
+ },
+ 'dependencies': [
+ '<(AOS)/common/common.gyp:type_traits_test',
+ '<(AOS)/common/common.gyp:time_test',
+ '<(AOS)/common/common.gyp:mutex_test',
+ '<(AOS)/common/common.gyp:condition_test',
+ '<(AOS)/common/common.gyp:once_test',
+ '<(AOS)/common/common.gyp:event_test',
+ '<(AOS)/common/common.gyp:queue_testutils_test',
+ '<(AOS)/common/logging/logging.gyp:logging_impl_test',
+ '<(AOS)/common/util/util.gyp:options_test',
+ '<(AOS)/common/common.gyp:queue_test',
+ '<(AOS)/common/common.gyp:die_test',
+ '<(AOS)/common/common.gyp:queue_types_test',
+ '<(AOS)/common/util/util.gyp:trapezoid_profile_test',
+ '<(AOS)/common/util/util.gyp:wrapping_counter_test',
+ '<(AOS)/common/libc/libc.gyp:dirname_test',
+ '<(AOS)/common/libc/libc.gyp:aos_strerror_test',
+ '<(AOS)/common/libc/libc.gyp:aos_strsignal_test',
+ '<(AOS)/common/util/util.gyp:run_command_test',
+ ],
+ },
+ ],
+}
diff --git a/aos/build/arm-clang-symlinks/README b/aos/build/arm-clang-symlinks/README
new file mode 100644
index 0000000..75210a5
--- /dev/null
+++ b/aos/build/arm-clang-symlinks/README
@@ -0,0 +1,11 @@
+This directory has symlinks to various other files to help clang figure out how
+to cross compile for arm with FIRST's toolchain setup.
+
+sysroot/ has symlinks to various places so it can be used as a sysroot when
+ compiling.
+bin/ has symlinks to the various tools so clang can find the native toolchain.
+more_libs/ has modified files of several psuedo-shared-objects which are
+ actually linker scripts as distributed with the FIRST toolchain. For some
+ reason, I can't get the linker to follow the documented behavior of
+ prefixing the absolute paths contained in them with the sysroot, so we have
+ copies which don't have any paths.
diff --git a/aos/build/arm-clang-symlinks/bin/as b/aos/build/arm-clang-symlinks/bin/as
new file mode 120000
index 0000000..f5bd4f8
--- /dev/null
+++ b/aos/build/arm-clang-symlinks/bin/as
@@ -0,0 +1 @@
+/usr/bin/arm-frc-linux-gnueabi-as
\ No newline at end of file
diff --git a/aos/build/arm-clang-symlinks/bin/clang b/aos/build/arm-clang-symlinks/bin/clang
new file mode 120000
index 0000000..c456250
--- /dev/null
+++ b/aos/build/arm-clang-symlinks/bin/clang
@@ -0,0 +1 @@
+/usr/bin/clang
\ No newline at end of file
diff --git a/aos/build/arm-clang-symlinks/bin/clang++ b/aos/build/arm-clang-symlinks/bin/clang++
new file mode 120000
index 0000000..2932265
--- /dev/null
+++ b/aos/build/arm-clang-symlinks/bin/clang++
@@ -0,0 +1 @@
+/usr/bin/clang++
\ No newline at end of file
diff --git a/aos/build/arm-clang-symlinks/bin/ld b/aos/build/arm-clang-symlinks/bin/ld
new file mode 120000
index 0000000..a12d1ed
--- /dev/null
+++ b/aos/build/arm-clang-symlinks/bin/ld
@@ -0,0 +1 @@
+/usr/bin/arm-frc-linux-gnueabi-ld
\ No newline at end of file
diff --git a/aos/build/arm-clang-symlinks/more_libs/libc.so b/aos/build/arm-clang-symlinks/more_libs/libc.so
new file mode 100644
index 0000000..714bcfe
--- /dev/null
+++ b/aos/build/arm-clang-symlinks/more_libs/libc.so
@@ -0,0 +1,5 @@
+/* GNU ld script
+ Use the shared library, but some functions are only in
+ the static library, so try that secondarily. */
+OUTPUT_FORMAT(elf32-littlearm)
+GROUP ( libc.so.6 libc_nonshared.a AS_NEEDED ( ld-linux.so.3 ) )
diff --git a/aos/build/arm-clang-symlinks/more_libs/libpthread.so b/aos/build/arm-clang-symlinks/more_libs/libpthread.so
new file mode 100644
index 0000000..71f034f
--- /dev/null
+++ b/aos/build/arm-clang-symlinks/more_libs/libpthread.so
@@ -0,0 +1,5 @@
+/* GNU ld script
+ Use the shared library, but some functions are only in
+ the static library, so try that secondarily. */
+OUTPUT_FORMAT(elf32-littlearm)
+GROUP ( libpthread.so.0 libpthread_nonshared.a )
diff --git a/aos/build/arm-clang-symlinks/sysroot/include b/aos/build/arm-clang-symlinks/sysroot/include
new file mode 120000
index 0000000..f4878ac
--- /dev/null
+++ b/aos/build/arm-clang-symlinks/sysroot/include
@@ -0,0 +1 @@
+/usr/arm-frc-linux-gnueabi/include
\ No newline at end of file
diff --git a/aos/build/arm-clang-symlinks/sysroot/lib b/aos/build/arm-clang-symlinks/sysroot/lib
new file mode 120000
index 0000000..5a01f30
--- /dev/null
+++ b/aos/build/arm-clang-symlinks/sysroot/lib
@@ -0,0 +1 @@
+/usr/arm-frc-linux-gnueabi/lib
\ No newline at end of file
diff --git a/aos/build/arm-clang-symlinks/sysroot/usr b/aos/build/arm-clang-symlinks/sysroot/usr
new file mode 120000
index 0000000..5d4ffd7
--- /dev/null
+++ b/aos/build/arm-clang-symlinks/sysroot/usr
@@ -0,0 +1 @@
+/usr/arm-frc-linux-gnueabi/usr
\ No newline at end of file
diff --git a/aos/build/bin-ld.gold/ld b/aos/build/bin-ld.gold/ld
new file mode 120000
index 0000000..af8591b
--- /dev/null
+++ b/aos/build/bin-ld.gold/ld
@@ -0,0 +1 @@
+/usr/bin/ld.gold
\ No newline at end of file
diff --git a/aos/build/build.py b/aos/build/build.py
new file mode 100755
index 0000000..613dedb
--- /dev/null
+++ b/aos/build/build.py
@@ -0,0 +1,1010 @@
+#!/usr/bin/python3
+
+import sys
+import subprocess
+import re
+import os
+import os.path
+import string
+import shutil
+import errno
+import queue
+import threading
+import pty
+import signal
+
+class TestThread(threading.Thread):
+ """Runs 1 test and keeps track of its current state.
+
+ A TestThread is either waiting to start the test, actually running it, done,
+ running it, or stopped. The first 3 always happen in that order and can
+ change to stopped at any time.
+
+ It will finish (ie join() will return) once the process has exited, at which
+ point accessing process to see the status is OK.
+
+ Attributes:
+ executable: The file path of the executable to run.
+ args: A tuple of arguments to give the executable.
+ env: The environment variables to set.
+ done_queue: A queue.Queue to place self on once done running the test.
+ start_semaphore: A threading.Semaphore to wait on before starting.
+ process_lock: A lock around process.
+ process: The currently executing test process or None. Synchronized by
+ process_lock.
+ stopped: True if we're stopped.
+ output: A queue of lines of output from the test.
+ """
+
+ class OutputCopier(threading.Thread):
+ """Copies the output of a test from its output pty into a queue.
+
+ This is necessary because otherwise everything locks up if the test writes
+ too much output and fills up the pty's buffer.
+ """
+
+ def __init__(self, name, fd, queue):
+ super(TestThread.OutputCopier, self).__init__(
+ name=(name + '.OutputCopier'))
+
+ self.fd = fd
+ self.queue = queue
+
+ def run(self):
+ with os.fdopen(self.fd) as to_read:
+ try:
+ for line in to_read:
+ self.queue.put(line)
+ except IOError as e:
+# An EIO from the master side of the pty means we hit the end.
+ if e.errno == errno.EIO:
+ return
+ else:
+ raise e
+
+ def __init__(self, executable, args, env, done_queue, start_semaphore):
+ super(TestThread, self).__init__(
+ name=os.path.split(executable)[-1])
+
+ self.executable = executable
+ self.args = args
+ self.env = env
+ self.done_queue = done_queue
+ self.start_semaphore = start_semaphore
+
+ self.output = queue.Queue()
+
+ self.process_lock = threading.Lock()
+ self.process = None
+ self.stopped = False
+ self.returncode = None
+ self.output_copier = None
+
+ def run(self):
+ def setup_test_process():
+# Shove it into its own process group so we can kill any subprocesses easily.
+ os.setpgid(0, 0)
+
+ with self.start_semaphore:
+ with self.process_lock:
+ if self.stopped:
+ return
+ test_output('Starting test %s...' % self.name)
+ output_to_read, subprocess_output = pty.openpty()
+ self.output_copier = TestThread.OutputCopier(self.name, output_to_read,
+ self.output)
+ self.output_copier.start()
+ try:
+ with self.process_lock:
+ self.process = subprocess.Popen((self.name,) + self.args,
+ executable=self.executable,
+ env=self.env,
+ stderr=subprocess.STDOUT,
+ stdout=subprocess_output,
+ stdin=open(os.devnull, 'r'),
+ preexec_fn=setup_test_process)
+ finally:
+ os.close(subprocess_output)
+ self.process.wait()
+ with self.process_lock:
+ self.returncode = self.process.returncode
+ self.process = None
+ if not self.stopped:
+ self.output_copier.join()
+ self.done_queue.put(self)
+
+ def kill_process(self):
+ """Forcibly terminates any running process."""
+ with self.process_lock:
+ if not self.process:
+ return
+ try:
+ os.killpg(self.process.pid, signal.SIGKILL)
+ except OSError as e:
+ if e.errno == errno.ESRCH:
+ # We don't really care if it's already gone.
+ pass
+ else:
+ raise e
+ def stop(self):
+ """Changes self to the stopped state."""
+ with self.process_lock:
+ self.stopped = True
+
+def aos_path():
+ """Returns:
+ A relative path to the aos directory.
+ """
+ return os.path.join(os.path.dirname(__file__), '..')
+
+def get_ip():
+ """Retrieves the IP address to download code to."""
+ FILENAME = os.path.normpath(os.path.join(aos_path(), '..',
+ 'output', 'ip_address.txt'))
+ if not os.access(FILENAME, os.R_OK):
+ os.makedirs(os.path.dirname(FILENAME), exist_ok=True)
+ with open(FILENAME, 'w') as f:
+ f.write('roboRIO-971.local')
+ with open(FILENAME, 'r') as f:
+ return f.readline().strip()
+
+def get_temp_dir():
+ """Retrieves the temporary directory to use when downloading."""
+ return '/home/admin/tmp/aos_downloader'
+
+def get_target_dir():
+ """Retrieves the tempory deploy directory for downloading code."""
+ return '/home/admin/robot_code'
+
+def user_output(message):
+ """Prints message to the user."""
+ print('build.py: ' + message, file=sys.stderr)
+
+# A lock to avoid making a mess intermingling test-related messages.
+test_output_lock = threading.RLock()
+def test_output(message):
+ """Prints message to the user. Intended for messages related to tests."""
+ with test_output_lock:
+ print('tests: ' + message, file=sys.stdout)
+
+def call_download_externals(argument):
+ """Calls download_externals.sh for a given set of externals.
+
+ Args:
+ argument: The argument to pass to the shell script to tell it what to
+ download.
+ """
+ subprocess.check_call(
+ (os.path.join(aos_path(), 'build', 'download_externals.sh'),
+ argument),
+ stdin=open(os.devnull, 'r'))
+
+class Processor(object):
+ """Represents a processor architecture we can build for."""
+
+ class UnknownPlatform(Exception):
+ def __init__(self, message):
+ super(Processor.UnknownPlatform, self).__init__()
+ self.message = message
+
+ class Platform(object):
+ """Represents a single way to build the code."""
+
+ def outdir(self):
+ """Returns:
+ The path of the directory build outputs get put in to.
+ """
+ return os.path.join(
+ aos_path(), '..', 'output', self.outname())
+ def build_ninja(self):
+ """Returns:
+ The path of the build.ninja file.
+ """
+ return os.path.join(self.outdir(), 'build.ninja')
+
+ def do_deploy(self, dry_run, command):
+ """Helper for subclasses to implement deploy.
+
+ Args:
+ dry_run: If True, prints the command instead of actually running it.
+ command: A tuple of command-line arguments.
+ """
+ real_command = (('echo',) + command) if dry_run else command
+ subprocess.check_call(real_command, stdin=open(os.devnull, 'r'))
+
+ def deploy(self, dry_run):
+ """Downloads the compiled code to the target computer."""
+ raise NotImplementedError('deploy should be overriden')
+ def outname(self):
+ """Returns:
+ The name of the directory the code will be compiled to.
+ """
+ raise NotImplementedError('outname should be overriden')
+ def os(self):
+ """Returns:
+ The name of the operating system this platform is for.
+
+ This will be used as the value of the OS gyp variable.
+ """
+ raise NotImplementedError('os should be overriden')
+ def gyp_platform(self):
+ """Returns:
+ The platform name the .gyp files know.
+
+ This will be used as the value of the PLATFORM gyp variable.
+ """
+ raise NotImplementedError('gyp_platform should be overriden')
+ def architecture(self):
+ """Returns:
+ The processor architecture for this platform.
+
+ This will be used as the value of the ARCHITECTURE gyp variable.
+ """
+ raise NotImplementedError('architecture should be overriden')
+ def compiler(self):
+ """Returns:
+ The compiler used for this platform.
+
+ Everything before the first _ will be used as the value of the
+ COMPILER gyp variable and the whole thing will be used as the value
+ of the FULL_COMPILER gyp variable.
+ """
+ raise NotImplementedError('compiler should be overriden')
+ def sanitizer(self):
+ """Returns:
+ The sanitizer used on this platform.
+
+ This will be used as the value of the SANITIZER gyp variable.
+
+ "none" if there isn't one.
+ """
+ raise NotImplementedError('sanitizer should be overriden')
+ def debug(self):
+ """Returns:
+ Whether or not this platform compiles with debugging information.
+
+ The DEBUG gyp variable will be set to "yes" or "no" based on this.
+ """
+ raise NotImplementedError('debug should be overriden')
+ def build_env(self):
+ """Returns:
+ A map of environment variables to set while building this platform.
+ """
+ raise NotImplementedError('build_env should be overriden')
+ def priority(self):
+ """Returns:
+ A relative priority for this platform relative to other ones.
+
+ Higher priority platforms will get built, tested, etc first. Generally,
+ platforms which give higher-quality compiler errors etc should come first.
+ """
+ return 0
+
+ def check_installed(self, platforms, is_deploy):
+ """Makes sure that everything necessary to build platforms are installed."""
+ raise NotImplementedError('check_installed should be overriden')
+ def parse_platforms(self, platforms_string):
+ """Args:
+ string: A user-supplied string saying which platforms to select.
+
+ Returns:
+ A tuple of Platform objects.
+
+ Raises:
+ Processor.UnknownPlatform: Parsing string didn't work out.
+ """
+ raise NotImplementedError('parse_platforms should be overriden')
+ def extra_gyp_flags(self):
+ """Returns:
+ A tuple of extra flags to pass to gyp (if any).
+ """
+ return ()
+ def modify_ninja_file(self, ninja_file):
+ """Modifies a freshly generated ninja file as necessary.
+
+ Args:
+ ninja_file: Path to the file to modify.
+ """
+ pass
+ def download_externals(self, platforms):
+ """Calls download_externals as appropriate to build platforms.
+
+ Args:
+ platforms: A list of platforms to download external libraries for.
+ """
+ raise NotImplementedError('download_externals should be overriden')
+
+ def do_check_installed(self, other_packages):
+ """Helper for subclasses to implement check_installed.
+
+ Args:
+ other_packages: A tuple of platform-specific packages to check for."""
+ all_packages = other_packages
+ # Necessary to build stuff.
+ all_packages += ('ccache', 'make')
+ # Necessary to download stuff to build.
+ all_packages += ('wget', 'git', 'subversion', 'patch', 'unzip', 'bzip2')
+ # Necessary to build externals stuff.
+ all_packages += ('python', 'gcc', 'g++')
+ not_found = []
+ try:
+ # TODO(brians): Check versions too.
+ result = subprocess.check_output(
+ ('dpkg-query',
+ r"--showformat='${binary:Package}\t${db:Status-Abbrev}\n'",
+ '--show') + all_packages,
+ stdin=open(os.devnull, 'r'),
+ stderr=subprocess.STDOUT)
+ for line in result.decode('utf-8').rstrip().splitlines(True):
+ match = re.match('^([^\t]+)\t[^i][^i]$', line)
+ if match:
+ not_found.append(match.group(1))
+ except subprocess.CalledProcessError as e:
+ output = e.output.decode('utf-8').rstrip()
+ for line in output.splitlines(True):
+ match = re.match(r'dpkg-query: no packages found matching (.*)',
+ line)
+ if match:
+ not_found.append(match.group(1))
+ if not_found:
+ user_output('Some packages not installed: %s.' % ', '.join(not_found))
+ user_output('Try something like `sudo apt-get install %s`.' %
+ ' '.join(not_found))
+ exit(1)
+
+class PrimeProcessor(Processor):
+ """A Processor subclass for building prime code."""
+
+ class Platform(Processor.Platform):
+ def __init__(self, architecture, folder, compiler, debug, sanitizer):
+ super(PrimeProcessor.Platform, self).__init__()
+
+ self.__architecture = architecture
+ self.__folder = folder
+ self.__compiler = compiler
+ self.__debug = debug
+ self.__sanitizer = sanitizer
+
+ def __repr__(self):
+ return 'PrimeProcessor.Platform(architecture=%s, compiler=%s, debug=%s' \
+ ', sanitizer=%s)' \
+ % (self.architecture(), self.compiler(), self.debug(),
+ self.sanitizer())
+ def __str__(self):
+ return '%s-%s-%s%s-%s' % (self.architecture(), self.folder(),
+ self.compiler(),
+ '-debug' if self.debug() else '',
+ self.sanitizer())
+
+ def os(self):
+ return 'linux'
+ def gyp_platform(self):
+ return '%s-%s-%s' % (self.os(), self.architecture(), self.compiler())
+ def architecture(self):
+ return self.__architecture
+ def folder(self):
+ return self.__folder
+ def compiler(self):
+ return self.__compiler
+ def sanitizer(self):
+ return self.__sanitizer
+ def debug(self):
+ return self.__debug
+
+ def outname(self):
+ return str(self)
+
+ def priority(self):
+ r = 0
+ if self.compiler() == 'clang':
+ r += 100
+ if self.sanitizer() != 'none':
+ r -= 50
+ elif self.debug():
+ r -= 10
+ if self.architecture() == 'amd64':
+ r += 5
+ return r
+
+ def deploy(self, dry_run):
+ """Downloads code to the prime in a way that avoids clashing too badly with
+ starter (like the naive download everything one at a time)."""
+ if not self.architecture().endswith('_frc'):
+ raise Exception("Don't know how to download code to a %s." %
+ self.architecture())
+ SUM = 'md5sum'
+ TARGET_DIR = get_target_dir()
+ TEMP_DIR = get_temp_dir()
+ TARGET = 'admin@' + get_ip()
+
+ from_dir = os.path.join(self.outdir(), 'outputs')
+ sums = subprocess.check_output((SUM,) + tuple(os.listdir(from_dir)),
+ stdin=open(os.devnull, 'r'),
+ cwd=from_dir)
+ to_download = subprocess.check_output(
+ ('ssh', TARGET,
+ """rm -rf {TMPDIR} && mkdir -p {TMPDIR} && \\
+ mkdir -p {TO_DIR} && cd {TO_DIR} \\
+ && echo '{SUMS}' | {SUM} -c \\
+ |& grep -F FAILED | sed 's/^\\(.*\\): FAILED.*$/\\1/g'""".
+ format(TMPDIR=TEMP_DIR, TO_DIR=TARGET_DIR, SUMS=sums.decode('utf-8'),
+ SUM=SUM)))
+ if not to_download:
+ user_output("Nothing to download")
+ return
+ self.do_deploy(
+ dry_run,
+ ('scp', '-o', 'Compression yes')
+ + tuple([os.path.join(from_dir, f) for f in to_download.decode('utf-8').split('\n')[:-1]])
+ + (('%s:%s' % (TARGET, TEMP_DIR)),))
+ if not dry_run:
+ mv_cmd = ['mv {TMPDIR}/* {TO_DIR} ']
+ mv_cmd.append('&& chmod u+s {TO_DIR}/starter_exe ')
+ mv_cmd.append('&& echo \'Done moving new executables into place\' ')
+ mv_cmd.append('&& bash -c \'sync && sync && sync\'')
+ subprocess.check_call(
+ ('ssh', TARGET,
+ ''.join(mv_cmd).format(TMPDIR=TEMP_DIR, TO_DIR=TARGET_DIR)))
+
+ def build_env(self):
+ OTHER_SYSROOT = '/usr/lib/llvm-3.5/'
+ SYMBOLIZER_PATH = OTHER_SYSROOT + 'bin/llvm-symbolizer'
+ r = {}
+ if self.sanitizer() == 'address':
+ r['ASAN_SYMBOLIZER_PATH'] = SYMBOLIZER_PATH
+ r['ASAN_OPTIONS'] = \
+ 'detect_leaks=1:check_initialization_order=1:strict_init_order=1' \
+ ':detect_stack_use_after_return=1:detect_odr_violation=2' \
+ ':allow_user_segv_handler=1'
+ elif self.sanitizer() == 'memory':
+ r['MSAN_SYMBOLIZER_PATH'] = SYMBOLIZER_PATH
+ elif self.sanitizer() == 'thread':
+ r['TSAN_OPTIONS'] = 'external_symbolizer_path=' + SYMBOLIZER_PATH
+ # This is apparently the default for newer versions, which disagrees
+ # with documentation, so just turn it on explicitly.
+ r['TSAN_OPTIONS'] += ':detect_deadlocks=1'
+ # Print more useful stacks for mutex locking order problems.
+ r['TSAN_OPTIONS'] += ':second_deadlock_stack=1'
+
+ r['CCACHE_COMPRESS'] = 'yes'
+ r['CCACHE_DIR'] = os.path.abspath(os.path.join(aos_path(), '..', 'output',
+ 'ccache_dir'))
+ r['CCACHE_HASHDIR'] = 'yes'
+ if self.compiler().startswith('clang'):
+ # clang doesn't like being run directly on the preprocessed files.
+ r['CCACHE_CPP2'] = 'yes'
+ # Without this, ccache slows down because of the generated header files.
+ # The race condition that this opens up isn't a problem because the build
+ # system finishes modifying header files before compiling anything that
+ # uses them.
+ r['CCACHE_SLOPPINESS'] = 'include_file_mtime'
+
+ if self.architecture() == 'amd64':
+ r['PATH'] = os.path.join(aos_path(), 'build', 'bin-ld.gold') + \
+ ':' + os.environ['PATH']
+
+ return r
+
+ ARCHITECTURES = ('arm_frc', 'amd64')
+ COMPILERS = ('clang', 'gcc')
+ SANITIZERS = ('address', 'undefined', 'integer', 'memory', 'thread', 'none')
+ SANITIZER_TEST_WARNINGS = {
+ 'memory': (True,
+"""We don't have all of the libraries instrumented which leads to lots of false
+ errors with msan (especially stdlibc++).
+ TODO(brians): Figure out a way to deal with it."""),
+ }
+ PIE_SANITIZERS = ('memory', 'thread')
+
+ def __init__(self, folder, is_test, is_deploy):
+ super(PrimeProcessor, self).__init__()
+
+ self.__folder = folder
+
+ platforms = []
+ for architecture in PrimeProcessor.ARCHITECTURES:
+ for compiler in PrimeProcessor.COMPILERS:
+ for debug in [True, False]:
+ if architecture == 'amd64' and compiler == 'gcc':
+ # We don't have a compiler to use here.
+ continue
+ platforms.append(
+ self.Platform(architecture, folder, compiler, debug, 'none'))
+ for sanitizer in PrimeProcessor.SANITIZERS:
+ for compiler in ('clang',):
+ if compiler == 'gcc_4.8' and (sanitizer == 'undefined' or
+ sanitizer == 'integer' or
+ sanitizer == 'memory'):
+ # GCC 4.8 doesn't support these sanitizers.
+ continue
+ if sanitizer == 'none':
+ # We already added sanitizer == 'none' above.
+ continue
+ platforms.append(
+ self.Platform('amd64', folder, compiler, True, sanitizer))
+ self.__platforms = frozenset(platforms)
+
+ if is_test:
+ default_platforms = self.select_platforms(architecture='amd64',
+ debug=True)
+ for sanitizer, warning in PrimeProcessor.SANITIZER_TEST_WARNINGS.items():
+ if warning[0]:
+ default_platforms -= self.select_platforms(sanitizer=sanitizer)
+ elif is_deploy:
+ default_platforms = self.select_platforms(architecture='arm_frc',
+ compiler='gcc',
+ debug=False)
+ else:
+ default_platforms = self.select_platforms(debug=False)
+ self.__default_platforms = frozenset(default_platforms)
+
+ def folder(self):
+ return self.__folder
+ def platforms(self):
+ return self.__platforms
+ def default_platforms(self):
+ return self.__default_platforms
+
+ def download_externals(self, platforms):
+ to_download = set()
+ for architecture in PrimeProcessor.ARCHITECTURES:
+ pie_sanitizers = set()
+ for sanitizer in PrimeProcessor.PIE_SANITIZERS:
+ pie_sanitizers.update(self.select_platforms(architecture=architecture,
+ sanitizer=sanitizer))
+ if platforms & pie_sanitizers:
+ to_download.add(architecture + '-fPIE')
+
+ if platforms & (self.select_platforms(architecture=architecture) -
+ pie_sanitizers):
+ to_download.add(architecture)
+
+ for download_target in to_download:
+ call_download_externals(download_target)
+
+ def parse_platforms(self, platform_string):
+ if platform_string is None:
+ return self.default_platforms()
+ r = self.default_platforms()
+ for part in platform_string.split(','):
+ if part == 'all':
+ r = self.platforms()
+ elif part[0] == '+':
+ r = r | self.select_platforms_string(part[1:])
+ elif part[0] == '-':
+ r = r - self.select_platforms_string(part[1:])
+ elif part[0] == '=':
+ r = self.select_platforms_string(part[1:])
+ else:
+ selected = self.select_platforms_string(part)
+ r = r - (self.platforms() - selected)
+ if not r:
+ r = selected
+ return r
+
+ def select_platforms(self, architecture=None, compiler=None, debug=None,
+ sanitizer=None):
+ r = []
+ for platform in self.platforms():
+ if architecture is None or platform.architecture() == architecture:
+ if compiler is None or platform.compiler() == compiler:
+ if debug is None or platform.debug() == debug:
+ if sanitizer is None or platform.sanitizer() == sanitizer:
+ r.append(platform)
+ return set(r)
+
+ def select_platforms_string(self, platforms_string):
+ architecture, compiler, debug, sanitizer = None, None, None, None
+ for part in platforms_string.split('-'):
+ if part in PrimeProcessor.ARCHITECTURES:
+ architecture = part
+ elif part in PrimeProcessor.COMPILERS:
+ compiler = part
+ elif part in ['debug', 'dbg']:
+ debug = True
+ elif part in ['release', 'nodebug', 'ndb']:
+ debug = False
+ elif part in PrimeProcessor.SANITIZERS:
+ sanitizer = part
+ elif part == 'all':
+ architecture = compiler = debug = sanitizer = None
+ elif part == self.folder():
+ pass
+ else:
+ raise Processor.UnknownPlatform(
+ '"%s" not recognized as a platform string component.' % part)
+ return self.select_platforms(
+ architecture=architecture,
+ compiler=compiler,
+ debug=debug,
+ sanitizer=sanitizer)
+
+ def check_installed(self, platforms, is_deploy):
+ packages = set(('lzip', 'm4', 'realpath'))
+ packages.add('ruby')
+ packages.add('clang-3.5')
+ packages.add('clang-format-3.5')
+ for platform in platforms:
+ if platform.compiler() == 'clang' or platform.compiler() == 'gcc_4.8':
+ packages.add('clang-3.5')
+ if platform.compiler() == 'gcc_4.8':
+ packages.add('libcloog-isl3:amd64')
+ if is_deploy:
+ packages.add('openssh-client')
+ elif platform.architecture == 'arm_frc':
+ packages.add('gcc-4.9-arm-frc-linux-gnueabi')
+ packages.add('g++-4.9-arm-frc-linux-gnueabi')
+
+ self.do_check_installed(tuple(packages))
+
+def strsignal(num):
+ # It ends up with SIGIOT instead otherwise, which is weird.
+ if num == signal.SIGABRT:
+ return 'SIGABRT'
+ # SIGCLD is a weird way to spell it.
+ if num == signal.SIGCHLD:
+ return 'SIGCHLD'
+
+ SIGNALS_TO_NAMES = dict((getattr(signal, n), n)
+ for n in dir(signal) if n.startswith('SIG')
+ and '_' not in n)
+ return SIGNALS_TO_NAMES.get(num, 'Unknown signal %d' % num)
+
+def main():
+ sys.argv.pop(0)
+ exec_name = sys.argv.pop(0)
+ def print_help(exit_status=None, message=None):
+ if message:
+ print(message)
+ sys.stdout.write(
+"""Usage: {name} [-j n] [action] [-n] [platform] [target|extra_flag]...
+Arguments:
+ -j, --jobs Explicitly specify how many jobs to run at a time.
+ Defaults to the number of processors + 2.
+ -n, --dry-run Don't actually do whatever.
+ Currently only meaningful for deploy.
+ action What to do. Defaults to build.
+ build: Build the code.
+ clean: Remove all the built output.
+ tests: Build and then run tests.
+ deploy: Build and then download.
+ environment: Dump the environment for building.
+ platform What variants of the code to build.
+ Defaults to something reasonable.
+ See below for details.
+ target... Which targets to build/test/etc.
+ Defaults to everything.
+ extra_flag... Extra flags associated with the targets.
+ --gtest_*: Arguments to pass on to tests.
+ --print_logs, --log_file=*: More test arguments.
+
+Specifying targets:
+ Targets are combinations of architecture, compiler, and debug flags. Which
+ ones actually get run is built up as a set. It defaults to something
+ reasonable for the action (specified below).
+ The platform specification (the argument given to this script) is a comma-
+ separated sequence of hyphen-separated platforms, each with an optional
+ prefix.
+ Each selector (the things separated by commas) selects all of the platforms
+ which match all of its components. Its effect on the set of current platforms
+ depends on the prefix character.
+ Here are the prefix characters:
+ + Adds the selected platforms.
+ - Removes the selected platforms.
+ = Sets the current set to the selected platforms.
+ [none] Removes all non-selected platforms.
+ If this makes the current set empty, acts like =.
+ There is also the special psuedo-platform "all" which selects all platforms.
+ All of the available platforms:
+ {all_platforms}
+ Default platforms for deploying:
+ {deploy_platforms}
+ Default platforms for testing:
+ {test_platforms}
+ Default platforms for everything else:
+ {default_platforms}
+
+Examples of specifying targets:
+ build everything: "all"
+ only build things with clang: "clang"
+ build everything that uses GCC 4.8 (not just the defaults): "=gcc_4.8"
+ build all of the arm targets that use clang: "clang-arm" or "arm-clang"
+""".format(
+ name=exec_name,
+ all_platforms=str_platforms(PrimeProcessor(False, False).platforms()),
+ deploy_platforms=str_platforms(PrimeProcessor(False, True).default_platforms()),
+ test_platforms=str_platforms(PrimeProcessor(True, False).default_platforms()),
+ default_platforms=str_platforms(PrimeProcessor(False, False).default_platforms()),
+ ))
+ if exit_status is not None:
+ sys.exit(exit_status)
+
+ def sort_platforms(platforms):
+ return sorted(
+ platforms, key=lambda platform: (-platform.priority(), str(platform)))
+
+ def str_platforms(platforms):
+ r = []
+ for platform in sort_platforms(platforms):
+ r.append(str(platform))
+ if len(r) > 1:
+ r[-1] = 'and ' + r[-1]
+ return ', '.join(r)
+
+ class Arguments(object):
+ def __init__(self):
+ self.jobs = os.sysconf('SC_NPROCESSORS_ONLN') + 2
+ self.action_name = 'build'
+ self.dry_run = False
+ self.targets = []
+ self.platform = None
+ self.extra_flags = []
+
+ args = Arguments()
+
+ if len(sys.argv) < 2:
+ print_help(1, 'Not enough arguments')
+ args.processor = sys.argv.pop(0)
+ args.folder = sys.argv.pop(0)
+ args.main_gyp = sys.argv.pop(0)
+ VALID_ACTIONS = ['build', 'clean', 'deploy', 'tests', 'environment']
+ while sys.argv:
+ arg = sys.argv.pop(0)
+ if arg == '-j' or arg == '--jobs':
+ args.jobs = int(sys.argv.pop(0))
+ continue
+ if arg in VALID_ACTIONS:
+ args.action_name = arg
+ continue
+ if arg == '-n' or arg == '--dry-run':
+ if args.action_name != 'deploy':
+ print_help(1, '--dry-run is only valid for deploy')
+ args.dry_run = True
+ continue
+ if arg == '-h' or arg == '--help':
+ print_help(0)
+ if (re.match('^--gtest_.*$', arg) or arg == '--print-logs' or
+ re.match('^--log_file=.*$', arg)):
+ if args.action_name == 'tests':
+ args.extra_flags.append(arg)
+ continue
+ else:
+ print_help(1, '%s is only valid for tests' % arg)
+ if args.platform:
+ args.targets.append(arg)
+ else:
+ args.platform = arg
+
+ if args.processor == 'prime':
+ processor = PrimeProcessor(args.folder,
+ args.action_name == 'tests',
+ args.action_name == 'deploy')
+ else:
+ print_help(1, message='Unknown processor "%s".' % args.processor)
+
+ unknown_platform_error = None
+ try:
+ platforms = processor.parse_platforms(args.platform)
+ except Processor.UnknownPlatform as e:
+ unknown_platform_error = e.message
+ args.targets.insert(0, args.platform)
+ platforms = processor.parse_platforms(None)
+ if not platforms:
+ print_help(1, 'No platforms selected')
+
+ processor.check_installed(platforms, args.action_name == 'deploy')
+ processor.download_externals(platforms)
+
+ class ToolsConfig(object):
+ def __init__(self):
+ self.variables = {'AOS': aos_path()}
+ with open(os.path.join(aos_path(), 'build', 'tools_config'), 'r') as f:
+ for line in f:
+ if line[0] == '#':
+ pass
+ elif line.isspace():
+ pass
+ else:
+ new_name, new_value = line.rstrip().split('=')
+ for name, value in self.variables.items():
+ new_value = new_value.replace('${%s}' % name, value)
+ self.variables[new_name] = new_value
+ def __getitem__(self, key):
+ return self.variables[key]
+
+ tools_config = ToolsConfig()
+
+ def handle_clean_error(function, path, excinfo):
+ _, _ = function, path
+ if issubclass(OSError, excinfo[0]):
+ if excinfo[1].errno == errno.ENOENT:
+ # Who cares if the file we're deleting isn't there?
+ return
+ raise excinfo[1]
+
+ def need_to_run_gyp(platform):
+ """Determines if we need to run gyp again or not.
+
+ The generated build files are supposed to re-run gyp again themselves, but
+ that doesn't work (or at least it used to not) and we sometimes want to
+ modify the results anyways.
+
+ Args:
+ platform: The platform to check for.
+ """
+ if not os.path.exists(platform.build_ninja()):
+ return True
+ if os.path.getmtime(__file__) > os.path.getmtime(platform.build_ninja()):
+ return True
+ dirs = os.listdir(os.path.join(aos_path(), '..'))
+ # Looking through these folders takes a long time and isn't useful.
+ if dirs.count('output'):
+ dirs.remove('output')
+ if dirs.count('.git'):
+ dirs.remove('.git')
+ return not not subprocess.check_output(
+ ('find',) + tuple(os.path.join(aos_path(), '..', d) for d in dirs)
+ + ('-newer', platform.build_ninja(),
+ '(', '-name', '*.gyp', '-or', '-name', '*.gypi', ')'),
+ stdin=open(os.devnull, 'r'))
+
+ def env(platform):
+ """Makes sure we pass through important environmental variables.
+
+ Returns:
+ An environment suitable for passing to subprocess.Popen and friends.
+ """
+ build_env = dict(platform.build_env())
+ if not 'TERM' in build_env:
+ build_env['TERM'] = os.environ['TERM']
+ if not 'PATH' in build_env:
+ build_env['PATH'] = os.environ['PATH']
+ return build_env
+
+ sorted_platforms = sort_platforms(platforms)
+ user_output('Building %s...' % str_platforms(sorted_platforms))
+
+ if args.action_name == 'tests':
+ for sanitizer, warning in PrimeProcessor.SANITIZER_TEST_WARNINGS.items():
+ warned_about = platforms & processor.select_platforms(sanitizer=sanitizer)
+ if warned_about:
+ user_output(warning[1])
+ if warning[0]:
+ # TODO(brians): Add a --force flag or something to override this?
+ user_output('Refusing to run tests for sanitizer %s.' % sanitizer)
+ exit(1)
+
+ num = 1
+ for platform in sorted_platforms:
+ user_output('Building %s (%d/%d)...' % (platform, num, len(platforms)))
+ if args.action_name == 'clean':
+ shutil.rmtree(platform.outdir(), onerror=handle_clean_error)
+ elif args.action_name == 'environment':
+ user_output('Environment for building <<END')
+ for name, value in env(platform).items():
+ print('%s=%s' % (name, value))
+ print('END')
+ else:
+ if need_to_run_gyp(platform):
+ user_output('Running gyp...')
+ gyp = subprocess.Popen(
+ (tools_config['GYP'],
+ '--check',
+ '--depth=%s' % os.path.join(aos_path(), '..'),
+ '--no-circular-check',
+ '-f', 'ninja',
+ '-I%s' % os.path.join(aos_path(), 'build', 'aos.gypi'),
+ '-I/dev/stdin', '-Goutput_dir=output',
+ '-DOS=%s' % platform.os(),
+ '-DPLATFORM=%s' % platform.gyp_platform(),
+ '-DARCHITECTURE=%s' % platform.architecture(),
+ '-DCOMPILER=%s' % platform.compiler().split('_')[0],
+ '-DFULL_COMPILER=%s' % platform.compiler(),
+ '-DDEBUG=%s' % ('yes' if platform.debug() else 'no'),
+ '-DSANITIZER=%s' % platform.sanitizer(),
+ '-DEXTERNALS_EXTRA=%s' %
+ ('-fPIE' if platform.sanitizer() in PrimeProcessor.PIE_SANITIZERS
+ else '')) +
+ processor.extra_gyp_flags() + (args.main_gyp,),
+ stdin=subprocess.PIPE)
+ gyp.communicate(("""
+{
+ 'target_defaults': {
+ 'configurations': {
+ '%s': {}
+ }
+ }
+}""" % platform.outname()).encode())
+ if gyp.returncode:
+ user_output("Running gyp failed!")
+ exit(1)
+ processor.modify_ninja_file(platform.build_ninja())
+ user_output('Done running gyp')
+ else:
+ user_output("Not running gyp")
+
+ try:
+ call = (tools_config['NINJA'],
+ '-C', platform.outdir()) + tuple(args.targets)
+ if args.jobs:
+ call += ('-j', str(args.jobs))
+ subprocess.check_call(call,
+ stdin=open(os.devnull, 'r'),
+ env=env(platform))
+ except subprocess.CalledProcessError as e:
+ if unknown_platform_error is not None:
+ user_output(unknown_platform_error)
+ raise e
+
+ if args.action_name == 'deploy':
+ platform.deploy(args.dry_run)
+ elif args.action_name == 'tests':
+ dirname = os.path.join(platform.outdir(), 'tests')
+ done_queue = queue.Queue()
+ running = []
+ test_start_semaphore = threading.Semaphore(args.jobs)
+ if args.targets:
+ to_run = []
+ for target in args.targets:
+ if target.endswith('_test'):
+ to_run.append(target)
+ else:
+ to_run = os.listdir(dirname)
+ for f in to_run:
+ thread = TestThread(os.path.join(dirname, f), tuple(args.extra_flags),
+ env(platform), done_queue,
+ test_start_semaphore)
+ running.append(thread)
+ thread.start()
+ try:
+ while running:
+ done = done_queue.get()
+ running.remove(done)
+ with test_output_lock:
+ test_output('Output from test %s:' % done.name)
+ try:
+ while True:
+ line = done.output.get(False)
+ if not sys.stdout.isatty():
+ # Remove color escape codes.
+ line = re.sub(r'\x1B\[[0-9;]*[a-zA-Z]', '', line)
+ sys.stdout.write(line)
+ except queue.Empty:
+ pass
+ if not done.returncode:
+ test_output('Test %s succeeded' % done.name)
+ else:
+ if done.returncode < 0:
+ sig = -done.returncode
+ test_output('Test %s was killed by signal %d (%s)' % \
+ (done.name, sig, strsignal(sig)))
+ elif done.returncode != 1:
+ test_output('Test %s exited with %d' % \
+ (done.name, done.returncode))
+ else:
+ test_output('Test %s failed' % done.name)
+ user_output('Aborting because of test failure for %s.' % \
+ platform)
+ exit(1)
+ finally:
+ if running:
+ test_output('Killing other tests...')
+# Stop all of them before killing processes because otherwise stopping some of
+# them tends to let other ones that are waiting to start go.
+ for thread in running:
+ thread.stop()
+ for thread in running:
+ test_output('\tKilling %s' % thread.name)
+ thread.kill_process()
+ thread.kill_process()
+ test_output('Waiting for other tests to die')
+ for thread in running:
+ thread.kill_process()
+ thread.join()
+ test_output('Done killing other tests')
+
+ user_output('Done building %s (%d/%d)' % (platform, num, len(platforms)))
+ num += 1
+
+if __name__ == '__main__':
+ main()
diff --git a/aos/build/download_externals.sh b/aos/build/download_externals.sh
new file mode 100755
index 0000000..ca49fd2
--- /dev/null
+++ b/aos/build/download_externals.sh
@@ -0,0 +1,222 @@
+#!/bin/bash
+
+#set -x
+set -e
+
+AOS=$(readlink -f $(dirname $0)/..)
+. $(dirname $0)/tools_config
+
+# A separate variable for stuff that should be in LDFLAGS for everything because
+# the value from CONFIGURE_FLAGS has to get overriden in some places.
+ALL_LDFLAGS=""
+
+# Flags that should get passed to all configure scripts.
+# Some of them need to set LDFLAGS separately to work around stupid configure
+# scripts, so we can't just set that here.
+CONFIGURE_FLAGS=""
+
+if [ "$1" == "arm" ]; then
+ COMPILED=${EXTERNALS}/../compiled-arm
+
+ CROSS_COMPILE=arm-linux-gnueabihf-
+
+ export CC=${CROSS_COMPILE}gcc-4.7
+ export CXX=${CROSS_COMPILE}g++-4.7
+ export CFLAGS="-mcpu=cortex-a8 -mfpu=neon"
+ export CXXFLAGS="-mcpu=cortex-a8 -mfpu=neon"
+ export OBJDUMP=${CROSS_COMPILE}objdump
+ CONFIGURE_FLAGS="--host=arm-linux-gnueabihf CC=${CC} CXX=${CXX} CFLAGS=\"${CFLAGS}\" CXXFLAGS=\"${CXXFLAGS}\" OBJDUMP=${OBJDUMP}"
+elif [ "$1" == "arm_frc" ]; then
+ COMPILED=${EXTERNALS}/../compiled-arm_frc
+
+ CROSS_COMPILE=arm-frc-linux-gnueabi-
+
+ export CC=${CROSS_COMPILE}gcc-4.9
+ export CXX=${CROSS_COMPILE}g++-4.9
+ export CFLAGS="-mcpu=cortex-a9 -mfpu=neon -mfloat-abi=softfp"
+ export CXXFLAGS="-mcpu=cortex-a9 -mfpu=neon -mfloat-abi=softfp"
+ export OBJDUMP=${CROSS_COMPILE}objdump
+ CONFIGURE_FLAGS="--host=arm-frc-linux-gnueabi CC=${CC} CXX=${CXX} CFLAGS=\"${CFLAGS}\" CXXFLAGS=\"${CXXFLAGS}\" OBJDUMP=${OBJDUMP}"
+elif [ "$1" == "amd64" ]; then
+ COMPILED=${EXTERNALS}/../compiled-amd64
+elif [ "$1" == "amd64-fPIE" ]; then
+ COMPILED=${EXTERNALS}/../compiled-amd64-fPIE
+
+ export CFLAGS="-fPIE"
+ export CXXFLAGS="-fPIE"
+ ALL_LDFLAGS="-fPIE"
+ export LDFLAGS=${ALL_LDFLAGS}
+ CONFIGURE_FLAGS="CFLAGS=\"${CFLAGS}\" CXXFLAGS=\"${CXXFLAGS}\" LDFLAGS=\"${LDFLAGS}\""
+else
+ echo "Unknown platform $1 to download externals for." 1>&2
+ exit 1
+fi
+
+TMPDIR=/tmp/$$-aos-tmpdir
+mkdir -p ${EXTERNALS}
+[[ -n "${COMPILED}" ]] && mkdir -p ${COMPILED}
+
+# get and build ninja
+[ -d ${NINJA_DIR} ] || git clone --branch ${NINJA_RELEASE} https://github.com/martine/ninja.git ${NINJA_DIR}
+[ -x ${NINJA} ] || env -i "PATH=$PATH" ${NINJA_DIR}/bootstrap.py
+
+# get gyp
+[ -d ${GYP_DIR} ] || ( svn co http://gyp.googlecode.com/svn/trunk -r ${GYP_REVISION} ${GYP_DIR} && patch -p1 -d ${GYP_DIR} < ${AOS}/externals/gyp.patch )
+
+# get eigen
+EIGEN_VERSION=3.2.1
+EIGEN_DIR=${EXTERNALS}/eigen-${EIGEN_VERSION}
+[ -f ${EIGEN_DIR}.tar.bz2 ] || wget http://bitbucket.org/eigen/eigen/get/${EIGEN_VERSION}.tar.bz2 -O ${EIGEN_DIR}.tar.bz2
+[ -d ${EIGEN_DIR} ] || ( mkdir ${EIGEN_DIR} && tar --strip-components=1 -C ${EIGEN_DIR} -xf ${EIGEN_DIR}.tar.bz2 )
+
+# get gtest
+GTEST_VERSION=1.6.0
+GTEST_DIR=${EXTERNALS}/gtest-${GTEST_VERSION}-p2
+GTEST_ZIP=${EXTERNALS}/gtest-${GTEST_VERSION}.zip
+[ -f ${GTEST_ZIP} ] || wget http://googletest.googlecode.com/files/gtest-${GTEST_VERSION}.zip -O ${GTEST_ZIP}
+[ -d ${GTEST_DIR} ] || ( unzip ${GTEST_ZIP} -d ${TMPDIR} && mv ${TMPDIR}/gtest-${GTEST_VERSION} ${GTEST_DIR} && cd ${GTEST_DIR} && patch -p1 < ${AOS}/externals/gtest.patch )
+
+# get and build libjpeg
+LIBJPEG_VERSION=8d
+LIBJPEG_DIR=${COMPILED}/jpeg-${LIBJPEG_VERSION}
+# NOTE: this directory ends up in #include names
+LIBJPEG_PREFIX=${COMPILED}/libjpeg
+LIBJPEG_LIB=${LIBJPEG_PREFIX}/lib/libjpeg.a
+LIBJPEG_TAR=${EXTERNALS}/jpegsrc.v${LIBJPEG_VERSION}.tar.gz
+[ -f ${LIBJPEG_TAR} ] || wget http://www.ijg.org/files/jpegsrc.v${LIBJPEG_VERSION}.tar.gz -O ${LIBJPEG_TAR}
+[ -d ${LIBJPEG_DIR} ] || ( mkdir ${LIBJPEG_DIR} && tar --strip-components=1 -C ${LIBJPEG_DIR} -xf ${LIBJPEG_TAR} )
+[ -f ${LIBJPEG_LIB} ] || bash -c \
+ "cd ${LIBJPEG_DIR} && ./configure --disable-shared \
+ ${CONFIGURE_FLAGS} --prefix=`readlink -f ${LIBJPEG_PREFIX}` \
+ && make && make install"
+
+# get and build ctemplate
+# This is the next revision after the 2.2 release and it only adds spaces to
+# make gcc 4.7 with --std=c++11 happy (user-defined string literals...).
+CTEMPLATE_VERSION=129
+CTEMPLATE_TAR=${EXTERNALS}/ctemplate-${CTEMPLATE_VERSION}.tar.gz
+CTEMPLATE_DIR=${COMPILED}/ctemplate-${CTEMPLATE_VERSION}
+CTEMPLATE_PREFIX=${CTEMPLATE_DIR}-prefix
+CTEMPLATE_LIB=${CTEMPLATE_PREFIX}/lib/libctemplate.a
+CTEMPLATE_URL=http://ctemplate.googlecode.com
+if [[ "${CTEMPLATE_VERSION}" =~ /\./ ]]; then
+ CTEMPLATE_URL=${CTEMPLATE_URL}/files/ctemplate-${CTEMPLATE_VERSION}.tar.gz
+ [ -f ${CTEMPLATE_TAR} ] || \
+ wget ${CTEMPLATE_URL} -O ${CTEMPLATE_TAR}
+ [ -d ${CTEMPLATE_DIR} ] || ( mkdir ${CTEMPLATE_DIR} && tar \
+ --strip-components=1 -C ${CTEMPLATE_DIR} -xf ${CTEMPLATE_TAR} )
+else
+ CTEMPLATE_URL=${CTEMPLATE_URL}/svn/trunk
+ [ -d ${CTEMPLATE_DIR} ] || \
+ svn checkout ${CTEMPLATE_URL} -r ${CTEMPLATE_VERSION} ${CTEMPLATE_DIR}
+fi
+[ -f ${CTEMPLATE_LIB} ] || bash -c "cd ${CTEMPLATE_DIR} && \
+ ./configure --disable-shared \
+ ${CONFIGURE_FLAGS} --prefix=`readlink -f ${CTEMPLATE_PREFIX}` \
+ && make && make install"
+
+# get and build gflags
+GFLAGS_VERSION=2.0
+GFLAGS_TAR=${EXTERNALS}/gflags-${GFLAGS_VERSION}.tar.gz
+GFLAGS_DIR=${COMPILED}/gflags-${GFLAGS_VERSION}
+GFLAGS_PREFIX=${GFLAGS_DIR}-prefix
+GFLAGS_LIB=${GFLAGS_PREFIX}/lib/libgflags.a
+GFLAGS_URL=https://gflags.googlecode.com/files/gflags-${GFLAGS_VERSION}.tar.gz
+[ -f ${GFLAGS_TAR} ] || wget ${GFLAGS_URL} -O ${GFLAGS_TAR}
+[ -d ${GFLAGS_DIR} ] || ( mkdir ${GFLAGS_DIR} && tar \
+ --strip-components=1 -C ${GFLAGS_DIR} -xf ${GFLAGS_TAR} )
+[ -f ${GFLAGS_LIB} ] || bash -c "cd ${GFLAGS_DIR} && ./configure \
+ ${CONFIGURE_FLAGS} --prefix=`readlink -f ${GFLAGS_PREFIX}` \
+ && make && make install"
+
+# get and build libevent
+LIBEVENT_VERSION=2.0.21
+LIBEVENT_TAR=${EXTERNALS}/libevent-${LIBEVENT_VERSION}.tar.gz
+LIBEVENT_DIR=${COMPILED}/libevent-${LIBEVENT_VERSION}
+LIBEVENT_PREFIX=${LIBEVENT_DIR}-prefix
+LIBEVENT_LIB=${LIBEVENT_PREFIX}/lib/libevent.a
+LIBEVENT_URL=https://github.com/downloads/libevent/libevent
+LIBEVENT_URL=${LIBEVENT_URL}/libevent-${LIBEVENT_VERSION}-stable.tar.gz
+[ -f ${LIBEVENT_TAR} ] || wget ${LIBEVENT_URL} -O ${LIBEVENT_TAR}
+[ -d ${LIBEVENT_DIR} ] || ( mkdir ${LIBEVENT_DIR} && tar \
+ --strip-components=1 -C ${LIBEVENT_DIR} -xf ${LIBEVENT_TAR} )
+[ -f ${LIBEVENT_LIB} ] || bash -c "cd ${LIBEVENT_DIR} && ./configure \
+ ${CONFIGURE_FLAGS} --prefix=`readlink -f ${LIBEVENT_PREFIX}` \
+ && make && make install"
+
+# get and build gmp
+GMP_VERSION=5.1.3
+GMP_TAR=${EXTERNALS}/gmp-${GMP_VERSION}.tar.lz
+GMP_DIR=${COMPILED}/gmp-${GMP_VERSION}
+GMP_PREFIX=${GMP_DIR}-prefix
+GMP_LIB=${GMP_PREFIX}/lib/libgmp.a
+GMP_URL=https://gmplib.org/download/gmp/gmp-${GMP_VERSION}.tar.lz
+[ -f ${GMP_TAR} ] || wget ${GMP_URL} -O ${GMP_TAR}
+[ -d ${GMP_DIR} ] || ( mkdir ${GMP_DIR} && tar \
+ --strip-components=1 -C ${GMP_DIR} -xf ${GMP_TAR} )
+[ -f ${GMP_LIB} ] || bash -c "cd ${GMP_DIR} && ./configure \
+ ${CONFIGURE_FLAGS} --prefix=$(readlink -f ${GMP_PREFIX}) \
+ && make && make install"
+
+# get and build libcdd
+LIBCDD_VERSION=094g
+LIBCDD_TAR=${EXTERNALS}/libcdd-${LIBCDD_VERSION}.tar.gz
+LIBCDD_DIR=${COMPILED}/libcdd-${LIBCDD_VERSION}
+LIBCDD_PREFIX=${LIBCDD_DIR}-prefix
+LIBCDD_LIB=${LIBCDD_PREFIX}/lib/libcdd.a
+LIBCDD_URL=ftp://ftp.ifor.math.ethz.ch/pub/fukuda/cdd/cddlib-${LIBCDD_VERSION}.tar.gz
+[ -f ${LIBCDD_TAR} ] || \
+ wget ${LIBCDD_URL} -O ${LIBCDD_TAR}
+[ -d ${LIBCDD_DIR} ] || ( mkdir ${LIBCDD_DIR} && tar \
+ --strip-components=1 -C ${LIBCDD_DIR} -xf ${LIBCDD_TAR} )
+[ -f ${LIBCDD_LIB} ] || \
+ bash -c "cd ${LIBCDD_DIR} && ./configure \
+ --disable-shared ${CONFIGURE_FLAGS} \
+ LDFLAGS=\"${ALL_LDFLAGS} -L${GMP_PREFIX}/lib\" \
+ --prefix=$(readlink -f ${LIBCDD_PREFIX}) \
+ && make gmpdir=${GMP_PREFIX} && make install"
+
+# get and build libunwind
+LIBUNWIND_VERSION=1.1
+LIBUNWIND_TAR=${EXTERNALS}/libunwind-${LIBUNWIND_VERSION}.tar.gz
+LIBUNWIND_DIR=${COMPILED}/libunwind-${LIBUNWIND_VERSION}
+LIBUNWIND_PREFIX=${LIBUNWIND_DIR}-prefix
+LIBUNWIND_LIB=${LIBUNWIND_PREFIX}/lib/libunwind.a
+LIBUNWIND_URL=http://download.savannah.gnu.org/releases/libunwind/libunwind-${LIBUNWIND_VERSION}.tar.gz
+[ -f ${LIBUNWIND_TAR} ] || \
+ wget ${LIBUNWIND_URL} -O ${LIBUNWIND_TAR}
+[ -d ${LIBUNWIND_DIR} ] || ( mkdir ${LIBUNWIND_DIR} && tar \
+ --strip-components=1 -C ${LIBUNWIND_DIR} -xf ${LIBUNWIND_TAR} )
+[ -f ${LIBUNWIND_LIB} ] || \
+ bash -c "cd ${LIBUNWIND_DIR} && ./configure \
+ --prefix=$(readlink -f ${LIBUNWIND_PREFIX}) \
+ ${CONFIGURE_FLAGS} \
+ && make && make install"
+
+# get and build gperftools
+GPERFTOOLS_VERSION=2.3
+GPERFTOOLS_TAR=${EXTERNALS}/gperftools-${GPERFTOOLS_VERSION}.tar.gz
+GPERFTOOLS_DIR=${COMPILED}/gperftools-${GPERFTOOLS_VERSION}
+GPERFTOOLS_PREFIX=${GPERFTOOLS_DIR}-prefix
+TCMALLOC_LIB=${GPERFTOOLS_PREFIX}/lib/libtcmalloc.a
+GPERFTOOLS_URL=https://googledrive.com/host/0B6NtGsLhIcf7MWxMMF9JdTN3UVk/gperftools-2.3.tar.gz
+[ -f ${GPERFTOOLS_TAR} ] || \
+ wget ${GPERFTOOLS_URL} -O ${GPERFTOOLS_TAR}
+[ -d ${GPERFTOOLS_DIR} ] || ( mkdir ${GPERFTOOLS_DIR} && tar \
+ --strip-components=1 -C ${GPERFTOOLS_DIR} -xf ${GPERFTOOLS_TAR} )
+[ -f ${TCMALLOC_LIB} ] || \
+ bash -c "cd ${GPERFTOOLS_DIR} && ./configure \
+ ${CONFIGURE_FLAGS} --prefix=$(readlink -f ${GPERFTOOLS_PREFIX}) \
+ LDFLAGS=\"${ALL_LDFLAGS} -L${LIBUNWIND_PREFIX}/lib -Wl,-rpath-link=${LIBUNWIND_PREFIX}/lib\" \
+ CPPFLAGS=\"-I ${LIBUNWIND_PREFIX}/include\" \
+ && make && make install"
+
+# get and build seasocks
+SEASOCKS_VERSION=1.1.2
+SEASOCKS_PATCH=1
+SEASOCKS_DIR=${EXTERNALS}/seasocks-${SEASOCKS_VERSION}-p${SEASOCKS_PATCH}
+[ -d ${SEASOCKS_DIR} ] || ( git clone --branch v${SEASOCKS_VERSION} \
+ https://github.com/mattgodbolt/seasocks.git ${SEASOCKS_DIR} && \
+ patch -p1 -d ${SEASOCKS_DIR} < ${AOS}/externals/seasocks.patch )
+
+rm -rf ${TMPDIR}
diff --git a/aos/build/externals.gyp b/aos/build/externals.gyp
new file mode 100644
index 0000000..c4bbaa2
--- /dev/null
+++ b/aos/build/externals.gyp
@@ -0,0 +1,219 @@
+# This file has targets for various external libraries.
+# download_externals.sh makes sure that all of them have been downloaded.
+{
+ 'variables': {
+ 'externals': '<(AOS)/../output/downloaded',
+ 'externals_abs': '<!(readlink -f ../../output/downloaded)',
+ 'compiled': '<(externals)/../compiled-<(ARCHITECTURE)<(EXTERNALS_EXTRA)',
+ 'compiled_abs': '<(externals_abs)/../compiled-<(ARCHITECTURE)<(EXTERNALS_EXTRA)',
+
+# These versions have to be kept in sync with the ones in download_externals.sh.
+ 'eigen_version': '3.2.1',
+ 'gtest_version': '1.6.0-p2',
+ 'ctemplate_version': '129',
+ 'gflags_version': '2.0',
+ 'compiler_rt_version': 'RELEASE_32_final',
+ 'libevent_version': '2.0.21',
+ 'libcdd_version': '094g',
+ 'stm32flash_commit': '8399fbe1baf2b7d097746786458021d92895d71b',
+ 'seasocks_version': '1.1.2-p1',
+
+ 'allwpilib': '<(AOS)/externals/allwpilib',
+ 'forwpilib': '<(AOS)/externals/forwpilib',
+ },
+ 'targets': [
+ {
+ 'target_name': 'WPILib',
+ 'type': 'static_library',
+ 'variables': {
+ 'header_dirs': [
+ '<(forwpilib)',
+ '<(allwpilib)/wpilibc/wpilibC++/include',
+ '<(allwpilib)/wpilibc/wpilibC++Devices/include',
+ '<(allwpilib)/hal/include',
+ '<(allwpilib)/hal/lib/Athena/FRC_FPGA_ChipObject',
+ '<(allwpilib)/hal/lib/Athena',
+ ],
+ },
+ 'include_dirs': [
+ '<@(header_dirs)'
+ ],
+ 'cflags': [
+ '-Wno-error',
+ '-fno-strict-aliasing',
+ '-O0',
+ ],
+ 'sources': [
+ '<!@(ls <(allwpilib)/wpilibc/wpilibC++/src/*.cpp)',
+ '<!@(ls <(allwpilib)/wpilibc/wpilibC++Devices/src/*.cpp)',
+ '<!@(ls <(allwpilib)/wpilibc/wpilibC++Devices/src/Internal/*.cpp)',
+ '<!@(ls <(allwpilib)/hal/lib/Athena/*.cpp)',
+ '<!@(ls <(allwpilib)/hal/lib/Athena/ctre/*.cpp)',
+ '<(forwpilib)/dma.cc',
+ ],
+ 'link_settings': {
+ 'library_dirs': [
+ '<(allwpilib)/ni-libraries',
+ ],
+ 'libraries': [
+ '-lpthread',
+ '-lFRC_NetworkCommunication',
+ '-lRoboRIO_FRC_ChipObject',
+ '-lNiFpgaLv',
+ '-lNiFpga',
+ '-lNiRioSrv',
+ '-lspi',
+ '-li2c',
+ ],
+ },
+ 'direct_dependent_settings': {
+ 'include_dirs': [
+ '<@(header_dirs)'
+ ],
+ },
+ },
+ {
+ 'target_name': 'opencv',
+ 'type': 'none',
+ 'link_settings': {
+ 'libraries': [
+ '-lopencv_core',
+ '-lopencv_imgproc',
+ ],
+ },
+ },
+ {
+ 'target_name': 'libevent',
+ 'type': 'none',
+ 'link_settings': {
+ 'libraries': ['<(compiled_abs)/libevent-<(libevent_version)-prefix/lib/libevent.a'],
+ },
+ 'direct_dependent_settings': {
+ 'include_dirs': ['<(compiled)/libevent-<(libevent_version)-prefix/include'],
+ },
+ },
+ {
+ 'target_name': 'eigen',
+ 'type': 'none',
+ 'direct_dependent_settings': {
+ 'cflags': [
+ '-isystem', '<(externals)/eigen-<(eigen_version)'
+ ],
+ },
+ },
+ {
+ 'target_name': 'libjpeg',
+ 'type': 'none',
+ 'direct_dependent_settings': {
+ 'libraries': ['<(compiled_abs)/libjpeg/lib/libjpeg.a'],
+ 'cflags': [
+ '-isystem', '<(compiled)',
+ ],
+ },
+ },
+ {
+# Dependents should only use the "gtest/gtest_prod.h" header.
+# This target is NOT the correct one for "aos/common/gtest_prod.h". That one is
+# aos/common/common.gyp:gtest_prod. This target just deals with setting up to
+# use the gtest header.
+ 'target_name': 'gtest_prod',
+ 'type': 'static_library',
+ 'direct_dependent_settings': {
+ 'include_dirs': [
+ '<(externals)/gtest-<(gtest_version)/include'
+ ],
+ },
+ },
+ {
+ 'target_name': 'gtest',
+ 'type': 'static_library',
+ 'sources': [
+ '<(externals)/gtest-<(gtest_version)/src/gtest-all.cc',
+ '<(AOS)/externals/gtest/gtest_main.cc',
+ ],
+ 'include_dirs': [
+ '<(externals)/gtest-<(gtest_version)',
+ ],
+ 'dependencies': [
+ 'gtest_prod',
+ ],
+ 'export_dependent_settings': [
+ 'gtest_prod',
+ ],
+ 'cflags!': ['-Werror'],
+ 'direct_dependent_settings': {
+ 'include_dirs': ['<(externals)/gtest-<(gtest_version)/include'],
+ 'target_conditions': [
+ ['_type=="executable" and is_special_test==0', {
+ 'product_dir': '<(test_dir)',
+ },
+ ], ['_type=="executable" and is_special_test==1', {
+ 'product_dir': '<(test_dir)-special',
+ },
+ ],
+ ],
+ },
+ },
+ {
+ 'target_name': 'ctemplate',
+ 'type': 'none',
+ 'link_settings': {
+ 'libraries': ['<(compiled_abs)/ctemplate-<(ctemplate_version)-prefix/lib/libctemplate.a'],
+ },
+ 'direct_dependent_settings': {
+ 'include_dirs': ['<(compiled)/ctemplate-<(ctemplate_version)-prefix/include'],
+ },
+ },
+ {
+ 'target_name': 'gflags',
+ 'type': 'none',
+ 'link_settings': {
+ 'libraries': ['<(compiled_abs)/gflags-<(gflags_version)-prefix/lib/libgflags.a'],
+ },
+ 'direct_dependent_settings': {
+ 'include_dirs': ['<(compiled)/gflags-<(gflags_version)-prefix/include'],
+ },
+ },
+ {
+ 'target_name': 'libcdd',
+ 'type': 'none',
+ 'link_settings': {
+ 'libraries': ['<(compiled_abs)/libcdd-<(libcdd_version)-prefix/lib/libcdd.a'],
+ },
+ 'direct_dependent_settings': {
+ 'include_dirs': ['<(compiled_abs)/'],
+ },
+ },
+ {
+ 'target_name': 'seasocks',
+ 'type': 'static_library',
+ 'sources': [
+ '<!@(find <(externals)/seasocks-<(seasocks_version)/src/main/c -name *.cpp)',
+ ],
+ 'include_dirs': [
+ '<(AOS)/externals/seasocks',
+ '<(externals_abs)/seasocks-<(seasocks_version)/src/main/c',
+ ],
+ 'cflags': [
+ '-Wno-unused-parameter',
+ '-Wno-format-nonliteral',
+ '-Wno-error=cast-align',
+ '-Wno-switch-enum',
+ '-Wno-cast-qual',
+ '-Wno-error=strict-aliasing',
+ ],
+ 'direct_dependent_settings': {
+ 'include_dirs': [
+ '<(AOS)/externals/seasocks',
+ '<(externals)/seasocks-<(seasocks_version)/src/main/c',
+ ],
+ 'cflags': [
+ # Since seasocks/WebSocket.h has functions implemented that don't use
+ # their parameters, the compilation of http_status would fail without
+ # the following exception.
+ '-Wno-unused-parameter',
+ ],
+ },
+ },
+ ],
+}
diff --git a/aos/build/queues.gypi b/aos/build/queues.gypi
new file mode 100644
index 0000000..7c0343a
--- /dev/null
+++ b/aos/build/queues.gypi
@@ -0,0 +1,83 @@
+# Include this file in any target that needs to use files generated from queue
+# etc. definitions.
+#
+# To use, create a target of the following form:
+# {
+# 'target_name': 'my_queues',
+# 'type': 'static_library', # or any other type that can handle .cc files
+# 'sources': [
+# 'aos/example/Queue.q',
+# 'aos/example/ControlLoop.q',
+# ],
+# 'variables': {
+# 'header_path': 'aos/example',
+# },
+# 'includes': ['path/to/queues.gypi'],
+# },
+# Code that depends on this target will be able to #include
+# "aos/example/Queue.q.h" and "aos/example/ControlLoop.q.h".
+#
+# using <http://src.chromium.org/svn/trunk/src/build/protoc.gypi> as an
+# example of how this should work
+{
+ 'variables': {
+ #'header_path': '>!(python -c "import os.path; print os.path.relpath(\'<(RULE_INPUT_PATH)\', \'<(DEPTH)\')")',
+ 'prefix_dir': '<(SHARED_INTERMEDIATE_DIR)/<!(echo <(header_path) | sed "s/[^A-Za-z0-9]/_/g")',
+ 'out_dir': '<(prefix_dir)/<(_target_name)/<(header_path)',
+ 'gen_namespace%': '>!(echo >(header_path) | sed "s:\([^/]*\).*:\\1:g")',
+ 'output_h': '<(out_dir)/<(RULE_INPUT_ROOT).q.h',
+ 'output_cc': '<(out_dir)/<(RULE_INPUT_ROOT).q.cc',
+ 'output_main': '<(out_dir)/<(RULE_INPUT_ROOT)_main.cc',
+ 'no_rsync': 1,
+ 'aos_q_dependent_paths%': [],
+ },
+ 'rules': [
+ {
+ 'variables': {
+ 'script': '<(AOS)/build/queues/compiler.rb',
+ },
+ 'rule_name': 'genqueue',
+ 'extension': 'q',
+ 'outputs': [
+ '<(output_h)',
+ '<(output_cc)',
+ ],
+ 'inputs': [
+ '<(script)',
+ '<!@(find <(AOS)/build/queues/ -name *.rb)',
+ '<(AOS)/common/queue.h',
+ '<(AOS)/common/time.h',
+ '>@(aos_q_dependent_paths)',
+ ],
+ 'action': ['ruby', '<(script)',
+ '-I', '<(DEPTH)',
+ '<(RULE_INPUT_PATH)',
+ '-cpp_out',
+ '<(header_path)',
+ '-cpp_base',
+ '<(prefix_dir)/<(_target_name)'],
+ 'message': 'Generating C++ code for <(header_path)/<(RULE_INPUT_PATH)',
+ 'process_outputs_as_sources': 1,
+ },
+ ],
+ 'include_dirs': [
+ '<(prefix_dir)/<(_target_name)',
+ ],
+ 'direct_dependent_settings': {
+ 'include_dirs': [
+ '<(prefix_dir)/<(_target_name)',
+ ],
+ 'variables': {
+ 'aos_q_dependent_paths': ['<@(_sources)'],
+ },
+ },
+ 'dependencies': [
+ '<(AOS)/common/common.gyp:queues',
+ '<(AOS)/common/common.gyp:once',
+ '<(AOS)/common/common.gyp:queue_types',
+ ],
+ 'export_dependent_settings': [
+ '<(AOS)/common/common.gyp:queues',
+ ],
+ 'hard_dependency': 1,
+}
diff --git a/aos/build/queues/compiler.rb b/aos/build/queues/compiler.rb
new file mode 100644
index 0000000..52c0005
--- /dev/null
+++ b/aos/build/queues/compiler.rb
@@ -0,0 +1,131 @@
+require File.dirname(__FILE__) + '/load.rb'
+
+def parse_args(globals,args)
+ i = 0
+ while(i < args.length)
+ if(args[i] == "-I")
+ args.delete_at(i)
+ if(!args[i])
+ $stderr.puts "hey! -I is followed by nothing."
+ $stderr.puts "\tnot a supported usage..."
+ $stderr.puts "\tWot. Wot."
+ exit!(-1)
+ end
+ path = args.delete_at(i)
+ globals.add_path(path)
+ elsif(args[i] == "-cpp_out")
+ args.delete_at(i)
+ path = args.delete_at(i)
+ if(path =~ /\./)
+ $stderr.puts "hey! path #{path} has a \".\" char which is "
+ $stderr.puts "\tnot a supported usage..."
+ $stderr.puts "\tWot. Wot."
+ exit!(-1)
+ elsif(!path)
+ $stderr.puts "hey! No cpp_out path provided."
+ $stderr.puts "\tumm, you could try -cpp_out \"\""
+ $stderr.puts "\tThat might do the trick"
+ $stderr.puts "\tWot. Wot."
+ exit!(-1)
+ end
+ $cpp_out = path.split(/\\|\//)
+ elsif(args[i] == "-cpp_base")
+ args.delete_at(i)
+ path = args.delete_at(i)
+ $cpp_base = File.expand_path(path)
+ if(!File.exists?($cpp_base))
+ $stderr.puts "output directory #{$cpp_base.inspect} does not exist."
+ $stderr.puts "\tI'm not going to make that! sheesh, who do you think I am?"
+ $stderr.puts "\tWot. Wot."
+ exit!(-1)
+ end
+ elsif(args[i] =~ /^-/)
+ $stderr.puts "hey! unknown argument #{args[i]}."
+ $stderr.puts "\tWot. Wot."
+ exit!(-1)
+ else
+ i += 1
+ end
+ end
+ if(!$cpp_base)
+ $stderr.puts "hey! missing -cpp_base argument."
+ $stderr.puts "\tWot. Wot."
+ exit!(-1)
+ end
+ if(!$cpp_out)
+ $stderr.puts "hey! missing -cpp_out argument."
+ $stderr.puts "\tWot. Wot."
+ exit!(-1)
+ end
+end
+def format_pipeline(output)
+ read_in, write_in = IO.pipe()
+ child = Process.spawn('/usr/bin/clang-format-3.5 --style=google',
+ {:in=>read_in, write_in=>:close,
+ :out=>output.fileno})
+ read_in.close
+ [child, write_in]
+end
+def build(filename,globals_template)
+ globals = Globals.new()
+ globals_template.paths.each do |path|
+ globals.add_path(path)
+ end
+ filename = File.expand_path(filename)
+ q_file = QFile.parse(filename)
+ output_file = q_file.q_eval(globals)
+ q_filename = File.basename(filename)
+ rel_path = ($cpp_out + [q_filename]).join("/")
+
+ FileUtils.mkdir_p(Pathname.new($cpp_base) + $cpp_out.join("/"))
+
+ cpp_tree = output_file.make_cpp_tree(rel_path)
+
+ h_file_path = $cpp_base + "/" + rel_path + ".h"
+ cc_file_path = $cpp_base + "/" + rel_path + ".cc"
+ cpp_tree.add_cc_include((rel_path + ".h").inspect)
+ cpp_tree.add_cc_include("aos/common/byteorder.h".inspect)
+ cpp_tree.add_cc_include("<inttypes.h>")
+ cpp_tree.add_cc_include("aos/common/queue_types.h".inspect)
+ cpp_tree.add_cc_include("aos/common/once.h".inspect)
+ cpp_tree.add_cc_include("aos/common/logging/logging_printf_formats.h".inspect)
+ cpp_tree.add_cc_using("::aos::to_network")
+ cpp_tree.add_cc_using("::aos::to_host")
+
+ header_file = WriteIffChanged.new(h_file_path)
+ cc_file = WriteIffChanged.new(cc_file_path)
+ header_child, header_output = format_pipeline(header_file)
+ cc_child, cc_output = format_pipeline(cc_file)
+ cpp_tree.write_header_file($cpp_base,header_output)
+ cpp_tree.write_cc_file($cpp_base,cc_output)
+ header_output.close()
+ cc_output.close()
+ if !Process.wait2(cc_child)[1].success?
+ $stderr.puts "Formatting cc file failed."
+ exit 1
+ end
+ if !Process.wait2(header_child)[1].success?
+ $stderr.puts "Formatting header file failed."
+ exit 1
+ end
+ header_file.close()
+ cc_file.close()
+end
+begin
+ args = ARGV.dup
+ globals = Globals.new()
+ parse_args(globals,args)
+ if(args.length == 0)
+ $stderr.puts "hey! you want me to do something,"
+ $stderr.puts "\tbut you gave me no q files to build!"
+ $stderr.puts "\tWot. Wot."
+ exit!(-1)
+ end
+ args.each do |filename|
+ build(filename,globals)
+ end
+ exit(0)
+rescue QError => e
+ $stderr.print(e.to_s)
+ exit!(-1)
+end
diff --git a/aos/build/queues/cpp_pretty_print/auto_gen.rb b/aos/build/queues/cpp_pretty_print/auto_gen.rb
new file mode 100644
index 0000000..d3fb480
--- /dev/null
+++ b/aos/build/queues/cpp_pretty_print/auto_gen.rb
@@ -0,0 +1,306 @@
+module CPP
+end
+class CPP::Comment
+ def initialize(text)
+ @text = text
+ end
+ def pp(state)
+ state.needs_semi = false
+ if(@text.include?("\n"))
+ state.print("/* #{@text} */")
+ else
+ state.print("// #{@text}")
+ end
+ end
+end
+class CPP::TODO < CPP::Comment
+ def initialize(owner,text)
+ @text = "TODO(#{owner}): #{text}"
+ end
+end
+class CPP::StaticVar
+ class ForwardDec
+ def initialize(func) ; @func = func ; end
+ def pp(state) ; @func.pp_forward_dec(state) ; end
+ end
+ attr_accessor :args, :static
+ def initialize(type_class, val_type, name, args = CPP::Args.new())
+ @type_class = type_class
+ @val_type = val_type
+ @name = name
+ @args = args
+ @static = true
+ end
+ def forward_dec() ; ForwardDec.new(self) ; end
+ def pp_forward_dec(state)
+ if (@static)
+ state.print("static ")
+ end
+ state.print("#{@val_type} #{@name}")
+ end
+ def pp(state)
+ state.print("#{@val_type} #{@type_class.chop_method_prefix}#{@name}(")
+ state.pp(@args)
+ state.print(")")
+ end
+ alias_method :pp_header_file, :pp_forward_dec
+ alias_method :pp_cc_file, :pp
+end
+class CPP::MemberFunc
+ class ForwardDec
+ def initialize(func) ; @func = func ; end
+ def pp(state) ; @func.pp_forward_dec(state) ; end
+ end
+ attr_accessor :args,:suite,:return_type,:name,:pre_func_types,:const,:static,:virtual
+ def initialize(type_class,return_type,name)
+ @type_class = type_class
+ @return_type = return_type
+ @name = name
+ @const = false
+ @static = false
+ @virtual = false
+ @args = CPP::Args.new()
+ @suite = CPP::Suite.new()
+ end
+ attr_accessor :inline
+ def forward_dec() ; ForwardDec.new(self) ; end
+ def pp_forward_dec(state)
+ return self.pp_inline(state) if(@inline)
+ if (@static)
+ state.print("static ")
+ elsif (@virtual)
+ state.print("virtual ")
+ end
+ state.print("#{@return_type} #{@pre_func_types}#{@name}(")
+ state.pp(@args)
+ state.print(")")
+ if (@const)
+ state.print(" const")
+ end
+ end
+ def pp_inline(state)
+ if (@static)
+ state.print("static ")
+ elsif (@virtual)
+ state.print("virtual ")
+ end
+ state.print("#{@return_type} #{@pre_func_types}#{@name}(")
+ state.pp(@args)
+ state.print(") ")
+ if (@const)
+ state.print(" const")
+ end
+ @suite.pp_one_line(state)
+ end
+ def pp(state)
+ return if(@inline)
+ state.print("#{@return_type} #{@pre_func_types}#{@type_class.chop_method_prefix}#{@name}(")
+ state.pp(@args)
+ state.print(") ")
+ if (@const)
+ state.print("const ")
+ end
+ state.pp(@suite)
+ state.v_pad(2)
+ end
+ alias_method :pp_header_file, :pp_forward_dec
+ alias_method :pp_cc_file, :pp
+
+end
+class CPP::Constructor
+ class ForwardDec
+ def initialize(func) ; @func = func ; end
+ def pp(state) ; @func.pp_forward_dec(state) ; end
+ end
+ attr_accessor :args,:suite,:return_type,:name
+ def initialize(type_class)
+ @type_class = type_class
+ @args = CPP::Args.new()
+ @suite = CPP::Suite.new()
+ @var_cons = CPP::Args.new()
+ end
+ def forward_dec() ; ForwardDec.new(self) ; end
+ def add_cons(*args)
+ @var_cons.push(CPP::FuncCall.build(*args))
+ end
+ def pp_forward_dec(state)
+ state.print("#{@type_class.name}(")
+ state.pp(@args)
+ state.print(")")
+ end
+ def pp_inline(state)
+ pp(state,false)
+ end
+ def pp(state,prefix = true)
+ state.needs_semi = false
+ state.print(@type_class.chop_method_prefix) if(prefix)
+ state.print("#{@type_class.name}(")
+ state.pp(@args)
+ if(@var_cons.length >= 1)
+ state.print(")")
+ state.endline()
+ state.indent += 4
+ state.print(": ")
+ state.pp(@var_cons)
+ state.indent -= 4
+ state.print(" ")
+ else
+ state.print(") ")
+ end
+ state.pp(@suite)
+ state.v_pad(2)
+ end
+ alias_method :pp_header_file, :pp_forward_dec
+ alias_method :pp_cc_file, :pp
+end
+class CPP::Destructor
+ class ForwardDec
+ def initialize(func) ; @func = func ; end
+ def pp(state) ; @func.pp_forward_dec(state) ; end
+ end
+ attr_accessor :args,:suite,:return_type,:name
+ def initialize(type_class)
+ @type_class = type_class
+ @args = CPP::Args.new()
+ @suite = CPP::Suite.new()
+ end
+ def forward_dec() ; ForwardDec.new(self) ; end
+ def pp_forward_dec(state)
+ state.print("~#{@type_class.name}(")
+ state.pp(@args)
+ state.print(")")
+ end
+ def pp(state)
+ state.print("#{@type_class.chop_method_prefix}~#{@type_class.name}(")
+ state.pp(@args)
+ state.print(") ")
+ state.pp(@suite)
+ state.v_pad(2)
+ end
+ alias_method :pp_header_file, :pp_forward_dec
+ alias_method :pp_cc_file, :pp
+end
+class CPP::Include
+ attr_accessor :filename
+ def initialize(filename)
+ @filename = filename
+ end
+ def pp(state)
+ state.needs_semi = false
+ state.suppress_indent()
+ state.print("#include #{@filename}")
+ state.endline()
+ end
+end
+class CPP::IncludeGuard
+ attr_accessor :name,:suite
+ def initialize(suite = CPP::Suite.new())
+ @suite = suite
+ end
+ def self.rand_name(len = 40)
+ str = ""
+ len.times { str += ((rand(26) + ?A).chr)}
+ return str
+ end
+ def pp(state)
+ @name ||= IncludeGuard.rand_name
+ state.needs_semi = false
+ state.suppress_indent()
+ state.print("#ifndef #{@name}")
+ state.endline()
+ state.suppress_indent()
+ state.print("#define #{@name}")
+ state.endline()
+ if(@suite.respond_to?(:pp_no_braces))
+ @suite.pp_no_braces(state)
+ else
+ state.pp(@suite)
+ end
+ state.endline()
+ state.needs_semi = false
+ state.suppress_indent()
+ state.print("#endif // #{@name}")
+ state.endline()
+ end
+end
+class CPP::IfnDef
+ attr_accessor :name,:suite
+ def initialize(suite = CPP::Suite.new())
+ @suite = suite
+ end
+ def pp(state)
+ state.needs_semi = false
+ state.suppress_indent()
+ state.print("#ifndef #{@name}")
+ state.endline()
+ if(@suite.respond_to?(:pp_no_braces))
+ @suite.pp_no_braces(state)
+ else
+ state.pp(@suite)
+ end
+ state.endline()
+ state.needs_semi = false
+ state.suppress_indent()
+ state.print("#endif // #{@name}")
+ state.endline()
+ end
+end
+class CPP::PreprocessorIf
+ attr_accessor :name,:suite
+ def initialize(ifsuite, elsesuite)
+ @ifsuite = ifsuite
+ @elsesuite = elsesuite
+ end
+ def write_if(state)
+ state.needs_semi = false
+ state.suppress_indent()
+ state.print("#if #{@name}")
+ state.endline()
+ end
+ def write_else(state)
+ state.needs_semi = false
+ state.suppress_indent()
+ state.print("#else // #{@name}")
+ state.endline()
+ end
+ def write_endif(state)
+ state.needs_semi = false
+ state.suppress_indent()
+ state.print("#endif // #{@name}")
+ state.endline()
+ end
+ def pp_inline(state)
+ self.write_if(state)
+ @ifsuite.pp_inline(state)
+ if(@elsesuite != nil)
+ self.write_else(state)
+ @elsesuite.pp_inline(state)
+ end
+ self.write_endif(state)
+ end
+ def pp(state)
+ self.write_if(state)
+ if(@ifsuite.respond_to?(:pp_no_braces))
+ @ifsuite.pp_no_braces(state)
+ else
+ state.pp(@ifsuite)
+ end
+ if(@elsesuite != nil)
+ self.write_else(state)
+ if(@elsesuite.respond_to?(:pp_no_braces))
+ @elsesuite.pp_no_braces(state)
+ else
+ state.pp(@elsesuite)
+ end
+ end
+ self.write_endif(state)
+ end
+end
+class CPP::Using
+ def initialize(using)
+ @using = using
+ end
+ def pp(state)
+ state.print("using #{@using}")
+ end
+end
diff --git a/aos/build/queues/cpp_pretty_print/dep_file_pair.rb b/aos/build/queues/cpp_pretty_print/dep_file_pair.rb
new file mode 100644
index 0000000..63b3fbd
--- /dev/null
+++ b/aos/build/queues/cpp_pretty_print/dep_file_pair.rb
@@ -0,0 +1,470 @@
+class GroupElement
+ def add_group_dep(group_dep)
+ @group_dep = group_dep
+ end
+ def adjust_group(state,old_group)
+ if(@group_dep != old_group)
+ @group_dep.adjust_from(state,old_group)
+ end
+ return @group_dep
+ end
+end
+class DepMask < GroupElement
+ def initialize(elem)
+ @elem = elem
+ @deps = []
+ end
+ def add_dep(dep)
+ @deps << dep
+ self
+ end
+ def method_missing(method,*args,&blk)
+ @elem.send(method,*args,&blk)
+ end
+ alias_method :old_respond_to, :respond_to?
+ def respond_to?(method)
+ old_respond_to(method) || @elem.respond_to?(method)
+ end
+end
+
+class MemberElementHeader < GroupElement
+ def initialize(elem)
+ @elem = elem
+ end
+ def pp(state)
+ if(@elem.respond_to?(:pp_header_file))
+ @elem.pp_header_file(state)
+ else
+ state.pp(@elem)
+ end
+ end
+ def self.check(plan,elem)
+ plan.push(self.new(elem))
+ end
+end
+class MemberElementInline < GroupElement
+ def initialize(elem)
+ @elem = elem
+ end
+ def pp(state)
+ if(@elem.respond_to?(:pp_inline))
+ @elem.pp_inline(state)
+ else
+ state.pp(@elem)
+ end
+ end
+end
+class MemberElementCC < GroupElement
+ attr_accessor :pp_override
+ def initialize(elem)
+ @elem = elem
+ end
+ def pp(state)
+ return state.pp(@elem) if(@pp_override)
+ @elem.pp_cc_file(state)
+ end
+ def self.check(plan,elem)
+ plan.push(self.new(elem)) if(elem.respond_to?(:pp_cc_file))
+ end
+end
+class ImplicitName
+ attr_accessor :parent
+ def initialize(name,parent)
+ @name = name
+ @parent = parent
+ end
+ def close(state)
+ state.endline()
+ state.needs_semi = false
+ state.print("} // namespace #{@name}\n")
+ end
+ def name()
+ if(@parent)
+ return @parent.name + "." + @name
+ else
+ return "." + @name
+ end
+ end
+ def open(state)
+ state.endline()
+ state.needs_semi = false
+ state.print("namespace #{@name} {\n")
+ end
+ def adjust_from(state,old_group)
+ close_grps = []
+ grp = old_group
+ while(grp)
+ close_grps << grp
+ grp = grp.parent
+ end
+ open_grps = []
+ grp = self
+ while(grp)
+ open_grps << grp
+ grp = grp.parent
+ end
+ while(open_grps[-1] == close_grps[-1] && close_grps[-1])
+ close_grps.pop()
+ open_grps.pop()
+ end
+ close_grps.each do |grp|
+ grp.close(state)
+ end
+ open_grps.reverse.each do |grp|
+ grp.open(state)
+ end
+ end
+ def adjust_to(state,new_group)
+ grp = self
+ while(grp)
+ grp.close(state)
+ grp = grp.parent
+ end
+ end
+end
+class GroupPlan < GroupElement
+ attr_accessor :implicit
+ def initialize(group_elem,members = [])
+ @group_elem = group_elem
+ @members = CPP::Suite.new(members)
+ end
+ def push(mem)
+ mem.add_group_dep(@implicit) if(@implicit)
+ @members << mem
+ end
+ def pp(state)
+ if(@group_elem)
+ @group_elem.open(state)
+ end
+ group = nil
+ @members.each do |member|
+ group = member.adjust_group(state,group)
+ #puts "at:#{group.name}" if(group.respond_to?(:name))
+ state.endline()
+ state.needs_semi = true
+ state.pp(member)
+ state.endline()
+ end
+ group.adjust_to(state,nil) if(group)
+ if(@group_elem)
+ @group_elem.close(state)
+ end
+ end
+end
+class CC_Mask
+ def initialize(elem)
+ @elem = elem
+ end
+ def plan_cc(plan)
+ elem = MemberElementCC.new(@elem)
+ elem.pp_override = true
+ plan.push(elem)
+ end
+ def plan_header(plan);
+ end
+end
+module Types
+ class TypeDef
+ def initialize(type,name)
+ @type = type
+ @name = name
+ end
+ def pp(state)
+ state.pp("typedef #{@type.name} #{@name}")
+ end
+ end
+ class Type
+ attr_accessor :name,:static,:dec,:space
+ def initialize(namespace,name)
+ @space = namespace
+ @name = name
+ @members = []
+ @protections = {}
+ @deps = []
+ end
+ def parent_class
+ @parent.split(' ')[1] if @parent
+ end
+ class ProtectionGroup
+ def initialize(name)
+ @name = name
+ end
+ def adjust_from(state,other)
+ state.indent -= 1
+ state.pp("#@name:")
+ state.endline
+ state.indent += 1
+ end
+ def adjust_to(state,other)
+ other.adjust_from(state,self) if other
+ end
+ end
+ ProtectionGroups = { :public => ProtectionGroup.new(:public),
+ :protected => ProtectionGroup.new(:protected),
+ :private => ProtectionGroup.new(:private)}
+ def set_parent(parent)
+ @parent = parent
+ end
+ def chop_method_prefix()
+ @space.chop_method_prefix + "#@name::"
+ end
+ def def_func(*args)
+ func = CPP::MemberFunc.new(self,*args)
+ @protections[func] = @protection
+ @members << func
+ return func
+ end
+ def add_cc_comment(*args)
+ args.each do |arg|
+ @members.push(CC_Mask.new(CPP::Comment.new(arg)))
+ end
+ end
+ def plan_cc(plan)
+ @members.each do |member|
+ if(member.respond_to?(:plan_cc))
+ member.plan_cc(plan)
+ elsif(member.respond_to?(:pp_cc_file))
+ plan.push(MemberElementCC.new(member))
+ end
+ end
+ end
+ def plan_header(plan)
+ group_plan = GroupPlan.new(self)
+ plan.push(group_plan)
+ @members.each do |member|
+ group_plan.implicit = ProtectionGroups[@protections[member]]
+ if(member.respond_to?(:plan_header))
+ member.plan_header(group_plan)
+ else
+ group_plan.push(MemberElementHeader.new(member))
+ end
+ #member.plan_cc(plan)
+ end
+ end
+ def open(state)
+ state.needs_semi = false
+ state.v_pad(2)
+ if(@static)
+ state.print("static ")
+ end
+ self.template_header(state) if(self.respond_to?(:template_header))
+ state.print("#{self.class.type_name} #{@name}")
+ self.template_spec_args(state) if(self.respond_to?(:template_spec_args))
+ if(@parent)
+ state.print(" : #{@parent} {\n")
+ else
+ state.pp(" {\n")
+ end
+ state.indent += 2
+ end
+ def close(state)
+ state.indent -= 2
+ state.needs_semi = true
+ if(@dec)
+ state.print("\n} #{@dec}")
+ else
+ state.print("\n}")
+ end
+ state.v_pad(2)
+ end
+ def add_dep(other)
+ @deps << other
+ self
+ end
+ end
+ class Class < Type
+ def add_member(protection,member)
+ @protection = protection
+ @members.push(member)
+ @protections[member] = protection
+ return member
+ end
+ def add_struct(*args)
+ add_member(:public, Types::Struct.new(self,*args))
+ end
+ def public() ; @protection = :public ; end
+ def protected() ; @protection = :protected ; end
+ def private() ; @protection = :private ; end
+ def self.type_name() ; "class" ; end
+ end
+ class PreprocessorIf < Type
+ def initialize(*args)
+ super(*args)
+ end
+ def add_member(member)
+ @members.push(member)
+ return member
+ end
+ def self.type_name() ; "#if" ; end
+ def open(state)
+ state.needs_semi = false
+ state.print("\n#if #{@name}")
+ end
+ def close(state)
+ state.needs_semi = false
+ state.print("\n#endif // #{@name}")
+ end
+ end
+ class Struct < Type
+ attr_accessor :members
+ def add_member(member)
+ @members.push(member)
+ return member
+ end
+ def self.type_name() ; "struct" ; end
+ end
+ class TemplateClass < Class
+ def initialize(*args)
+ super(*args)
+ @t_args = CPP::Args.new()
+ end
+ def spec_args()
+ return @spec_args ||= CPP::Args.new()
+ end
+ def template_header(state)
+ if(@t_args.length > 0)
+ state.pp("template < ")
+ state.pp(@t_args)
+ else
+ state.pp("template < ")
+ end
+ state.pp(">\n")
+ end
+ def plan_cc(plan)
+
+ end
+ def plan_header(plan)
+ group_plan = GroupPlan.new(self)
+ plan.push(group_plan)
+ @members.each do |member|
+ group_plan.implicit = ProtectionGroups[@protections[member]]
+ if(member.respond_to?(:plan_header))
+ member.plan_header(group_plan)
+ else
+ group_plan.push(MemberElementInline.new(member))
+ end
+ end
+ end
+ def template_spec_args(state)
+ if(@spec_args)
+ state.pp("< ")
+ state.pp(@spec_args)
+ state.pp(">")
+ end
+ end
+ end
+end
+class DepFilePair
+ class NameSpace
+ def initialize(name,parent)
+ @name,@parent = name,parent
+ @members = []
+ end
+ def add_struct(*args)
+ add Types::Struct.new(self,*args)
+ end
+ def add_template(*args)
+ add Types::TemplateClass.new(self,*args)
+ end
+ def add_class(*args)
+ add Types::Class.new(self,*args)
+ end
+ def add_cc(arg)
+ @members.push(CC_Mask.new(arg))
+ end
+ def chop_method_prefix()
+ ""
+ end
+ def class_comment(*args)
+ add CPP::Comment.new(*args)
+ end
+ def var_dec_comment(*args)
+ add CPP::Comment.new(*args)
+ end
+ def add_var_dec(arg)
+ add DepMask.new(arg)
+ end
+ def plan_cc(plan)
+ plan.implicit = ImplicitName.new(@name,plan.implicit)
+ @members.each do |member|
+ if(member.respond_to?(:plan_cc))
+ member.plan_cc(plan)
+ else
+ MemberElementCC.check(plan,member)
+ end
+ end
+ plan.implicit = plan.implicit.parent
+ end
+ def plan_header(plan)
+ plan.implicit = ImplicitName.new(@name,plan.implicit)
+ @members.each do |member|
+ if(member.respond_to?(:plan_header))
+ member.plan_header(plan)
+ else
+ MemberElementHeader.check(plan,member)
+ end
+ end
+ plan.implicit = plan.implicit.parent
+ end
+ def add val
+ @members << val
+ val
+ end
+ end
+ def initialize(rel_path)
+ @rel_path = rel_path
+ @cc_includes = []
+ @header_includes = []
+ @spaces = []
+ @cc_usings = []
+ @cache = {}
+ end
+ def add_cc_include(inc_path)
+ @cc_includes << CPP::Include.new(inc_path)
+ end
+ def add_cc_using(using)
+ @cc_usings << CPP::Using.new(using)
+ end
+ def add_header_include(inc_path)
+ @header_includes << CPP::Include.new(inc_path)
+ end
+ def add_namespace(name,parent)
+ space = NameSpace.new(name,parent)
+ if(parent != self)
+ parent.add(space)
+ else
+ @spaces.push(space)
+ end
+ return space
+ end
+ def set(type,cached)
+ @cache[type] = cached
+ end
+ def get(type)
+ cached = @cache[type]
+ return cached if cached
+ cached = type.create(self)
+ cached_check = @cache[type]
+ return cached_check if cached_check
+ set(type,cached)
+ return cached
+ end
+ def write_cc_file(cpp_base,cc_file)
+ plan_cc = GroupPlan.new(nil,elems_cc = [])
+ @spaces.each do |space|
+ space.plan_cc(plan_cc)
+ end
+ suite = CPP::Suite.new(@cc_includes + @cc_usings + [plan_cc])
+ CPP.pretty_print(suite,cc_file)
+ end
+ def write_header_file(cpp_base,header_file)
+ plan_header = GroupPlan.new(nil,elems_cc = [])
+ @spaces.each do |space|
+ space.plan_header(plan_header)
+ end
+ suite = CPP::Suite.new(@header_includes + [plan_header])
+ include_guard = CPP::IncludeGuard.new(suite)
+ include_guard.name = @rel_path.upcase.gsub(/[\.\/\\]/,"_") + "_H_"
+ CPP.pretty_print(include_guard,header_file)
+ end
+end
diff --git a/aos/build/queues/cpp_pretty_print/file_pair_types.rb b/aos/build/queues/cpp_pretty_print/file_pair_types.rb
new file mode 100644
index 0000000..25ce4fe
--- /dev/null
+++ b/aos/build/queues/cpp_pretty_print/file_pair_types.rb
@@ -0,0 +1,204 @@
+module CPP
+end
+class CPP::TypePair
+ attr_accessor :name,:static,:dec
+ def initialize(namespace,name)
+ @space,@name = namespace,name
+ @members = []
+ @protections = {}
+ @dont_sort = {}
+ end
+ ProtectionTable = { :public => 0,:protected => 1,:private => 2 }
+ def comp(m1,m2)
+ if(!(@dont_sort[m1] || @dont_sort[m2]))
+ if(@protections[m1] && @protections[m2])
+ comp = ProtectionTable[@protections[m1]] <=> ProtectionTable[@protections[m2]]
+ return comp if(comp != 0)
+ end
+ comp = TypeTable[m1.class] <=> TypeTable[m2.class]
+ return comp if(comp != 0)
+ end
+ return @stable[m1] <=> @stable[m2]
+ end
+ def set_parent(parent)
+ @parent = parent
+ end
+ def add_class(name)
+ return add_member(:public,CPP::ClassPair.new(self,name))
+ end
+ def add_struct(name)
+ return add_member(:public,CPP::StructPair.new(self,name))
+ end
+ def add_cc_comment(*strs)
+ strs.each do |str|
+ @members << comment = CPP::CCMemberWrapper.new(CPP::Comment.new(str))
+ @protections[comment] = @protection
+ end
+ #@dont_sort[comment] = true
+ end
+ def method_prefix()
+ @space.method_prefix + "#@name::"
+ end
+ def chop_method_prefix()
+ @space.chop_method_prefix + "#@name::"
+ end
+ def pp(state)
+ @stable = {}
+ @members.each_with_index { |mem,i| @stable[mem] = i }
+ members = @members.sort { |m1,m2| comp(m1,m2) }
+
+ state.needs_semi = false
+ state.v_pad(2)
+ if(@static)
+ state.print("static ")
+ end
+ if(@parent)
+ state.print("#{self.class.type_name} #{@name} : #{@parent} {\n")
+ else
+ state.print("#{self.class.type_name} #{@name} {\n")
+ end
+ state.indent += 2
+ protection = nil
+ members.each do |member|
+ if(protection != @protections[member])
+ state.indent -= 1
+ state.needs_semi = false
+ state.v_pad(2) if(protection)
+ protection = @protections[member]
+ state.print("#{protection}:\n")
+ state.indent += 1
+ state.endline()
+ end
+ state.endline()
+ state.needs_semi = true
+ if(member.respond_to?(:pp_header_file))
+ member.pp_header_file(state)
+ else
+ state.pp(member)
+ end
+ state.endline()
+ end
+ state.indent -= 2
+ state.needs_semi = true
+ if(@dec)
+ state.print("\n} #{@dec}")
+ else
+ state.print("\n}")
+ end
+ state.v_pad(2)
+ end
+ def pp_header_file(state)
+ pp(state)
+ end
+ def pp_cc_file(state)
+ @members.each do |member|
+ state.needs_semi = true
+ member.pp_cc_file(state) if(member.respond_to?(:pp_cc_file))
+ state.endline()
+ end
+ end
+ def def_func(*args)
+ func = CPP::MemberFunc.new(self,*args)
+ @protections[func] = @protection
+ @members << func
+ return func
+ end
+end
+class CPP::CCMemberWrapper
+ def initialize(member)
+ @member = member
+ end
+ def pp_header_file(state) ; end
+ def pp_cc_file(state)
+ state.pp(@member)
+ end
+end
+class CPP::ClassPair < CPP::TypePair
+ attr_accessor :name
+ def initialize(namespace,name)
+ super(namespace,name)
+ @protection = :public #default to public
+ end
+ def add_member(protection,member)
+ @protection = protection
+ @members << member
+ @protections[member] = protection
+ return member
+ end
+ def public() ; @protection = :public ; end
+ def protected() ; @protection = :protected ; end
+ def private() ; @protection = :private ; end
+ def self.type_name() ; "class" ; end
+end
+class CPP::StructPair < CPP::TypePair
+ def self.type_name() ; "struct" ; end
+ def add_member(member)
+ @members << member
+ return member
+ end
+end
+CPP::TypePair::TypeTable = { CPP::StructPair => 0, CPP::ClassPair => 1,CPP::Constructor => 2 }
+CPP::TypePair::TypeTable.default = 3
+class CPP::TemplateClass < CPP::ClassPair
+ def initialize(namespace,name)
+ super(namespace,name)
+ @t_args = CPP::Args.new()
+ end
+ def spec_args()
+ return @spec_args ||= CPP::Args.new()
+ end
+ def pp(state)
+ @stable = {}
+ @members.each_with_index { |mem,i| @stable[mem] = i }
+ members = @members.sort { |m1,m2| comp(m1,m2) }
+ state.needs_semi = false
+ state.v_pad(2)
+ if(@t_args.length > 0)
+ state.pp("template < ")
+ state.pp(@t_args)
+ else
+ state.pp("template < ")
+ end
+ state.pp(">\n")
+ state.pp("class #@name")
+ if(@spec_args)
+ state.pp("< ")
+ state.pp(@spec_args)
+ state.pp("> {")
+ else
+ state.pp(" {")
+ end
+ state.endline()
+ state.indent += 2
+ protection = nil
+ members.each do |member|
+ if(protection != @protections[member])
+ state.indent -= 1
+ state.needs_semi = false
+ state.v_pad(2) if(protection)
+ protection = @protections[member]
+ state.print("#{protection}:\n")
+ state.indent += 1
+ state.endline()
+ end
+ state.endline()
+ state.needs_semi = true
+ if(member.respond_to?(:pp_inline))
+ member.pp_inline(state)
+ else
+ state.pp(member)
+ end
+ state.endline()
+ end
+ state.indent -= 2
+ state.needs_semi = true
+ if(@dec)
+ state.print("\n} #{@dec}")
+ else
+ state.print("\n}")
+ end
+ state.v_pad(2)
+ end
+ def pp_cc_file(state); end
+end
+
diff --git a/aos/build/queues/cpp_pretty_print/standard_types.rb b/aos/build/queues/cpp_pretty_print/standard_types.rb
new file mode 100644
index 0000000..566f542
--- /dev/null
+++ b/aos/build/queues/cpp_pretty_print/standard_types.rb
@@ -0,0 +1,363 @@
+module CPP
+ class Class
+
+ end
+ class Funct
+ attr_accessor :retval,:name,:args,:suite
+ def initialize(retval = nil,name = nil,args = Args.new(),suite = Suite.new())
+ @retval,@name,@args,@suite = retval,name,args,suite
+ end
+ def pp(state)
+ state.pp(@retval)
+ state.print(" ")
+ state.pp(@name)
+ state.print("(")
+ state.pp(@args)
+ state.print(")")
+ state.pp(@suite)
+ end
+ end
+ class If
+ attr_accessor :cond,:suite,:else_suite
+ def initialize(cond = nil,suite = Suite.new(),else_suite = nil)
+ @cond,@suite,@else_suite = cond,suite,else_suite
+ end
+ def pp(state)
+ state.print("if (")
+ state.pp(@cond)
+ state.print(") ")
+ state.needs_semi = false
+ state.pp(@suite)
+ if(@else_suite)
+ state.print(" else ")
+ state.pp(@else_suite)
+ end
+ end
+ def else_suite()
+ return(@else_suite ||= Suite.new())
+ end
+ end
+ class For
+ attr_accessor :init,:cond,:update,:suite
+ def initialize(init = nil,cond = nil,update = nil,suite = Suite.new())
+ @init,@cond,@update,@suite = init,cond,update,suite
+ end
+ def pp(state)
+ state.print("for (")
+ Args.new([@init,@cond,@update]).pp(state,";")
+ state.print(") ")
+ state.needs_semi = false
+ state.pp(@suite)
+ end
+ end
+ class Args < Array
+ attr_accessor :dont_wrap
+ def pp(state,sep = ",")
+ pos = start = state.col
+ self.each_with_index do |arg,i|
+ #puts "#{state.col - start} , #{start}"
+ state.print(sep) if(i != 0)
+ if(pos > 80)
+ state.wrap(state.indent + 4)
+ pos = start = state.col
+ elsif(state.col * 2 - pos > 80 && !@dont_wrap)
+ state.wrap(start)
+ start = pos
+ else
+ state.print(" ") if(i != 0)
+ pos = state.col
+ end
+ state.pp(arg)
+ end
+ end
+ end
+ class Suite < Array
+ def pp(state)
+ state.print("{")
+ state.>>
+ state.needs_semi = false
+ self.pp_no_braces(state)
+ state.<<
+ state.print("}")
+ end
+ def pp_no_braces(state)
+ self.each do |arg,i|
+ state.endline()
+ state.needs_semi = true
+ state.pp(arg)
+ state.endline()
+ end
+ end
+ def pp_one_line(state)
+ if(self.length == 1)
+ state.needs_semi = true
+ state.print("{ ")
+ state.pp(self[0])
+ state.print(";") if(state.needs_semi)
+ state.needs_semi = false
+ state.print(" }")
+ else
+ self.pp(state)
+ end
+ end
+ end
+ class FuncCall
+ attr_accessor :name,:args
+ def initialize(name = nil,args = Args.new())
+ @name,@args = name,args
+ end
+ def self.build(name,*args)
+ self.new(name,Args.new(args))
+ end
+ def pp(state)
+ state.pp(@name)
+ state.print("(")
+ state.pp(@args)
+ state.print(")")
+ end
+ end
+ class BIT_OR
+ attr_accessor :val1,:val2
+ def initialize(val1,val2)
+ @val1,@val2 = val1,val2
+ end
+ def pp(state)
+ state.pp(@val1)
+ state.print(" | ")
+ state.pp(@val2)
+ end
+ end
+ class LT
+ attr_accessor :val1,:val2
+ def initialize(val1,val2)
+ @val1,@val2 = val1,val2
+ end
+ def pp(state)
+ state.pp(@val1)
+ state.print(" < ")
+ state.pp(@val2)
+ end
+ end
+ class Div
+ attr_accessor :val1,:val2
+ def initialize(val1,val2)
+ @val1,@val2 = val1,val2
+ end
+ def pp(state)
+ state.pp(@val1)
+ state.print(" / ")
+ state.pp(@val2)
+ end
+ end
+ class Add
+ attr_accessor :val1,:val2
+ def initialize(val1,val2)
+ @val1,@val2 = val1,val2
+ end
+ def pp(state)
+ state.pp(@val1)
+ state.print(" + ")
+ state.pp(@val2)
+ end
+ end
+ class Member
+ attr_accessor :obj,:name
+ def initialize(obj,name)
+ @obj,@name = obj,name
+ end
+ def pp(state)
+ state.pp(@obj)
+ state.print(".")
+ state.pp(@name)
+ end
+ end
+ class PointerMember
+ attr_accessor :obj,:name
+ def initialize(obj,name)
+ @obj,@name = obj,name
+ end
+ def pp(state)
+ state.pp(@obj)
+ state.print("->")
+ state.pp(@name)
+ end
+ end
+ class Minus
+ attr_accessor :val
+ def initialize(val)
+ @val = val
+ end
+ def pp(state)
+ state.print("-")
+ state.pp(@val)
+ end
+ end
+ class Not
+ attr_accessor :val
+ def initialize(val)
+ @val = val
+ end
+ def pp(state)
+ state.print("!")
+ state.pp(@val)
+ end
+ end
+ class Address
+ attr_accessor :val
+ def initialize(val)
+ @val = val
+ end
+ def pp(state)
+ state.print("&")
+ state.pp(@val)
+ end
+ end
+ class Paran
+ attr_accessor :val
+ def initialize(val)
+ @val = val
+ end
+ def pp(state)
+ state.print("(")
+ state.pp(@val)
+ state.print(")")
+ end
+ end
+ class Assign
+ attr_accessor :lval,:rval
+ def initialize(lval = nil,rval = Args.new())
+ @lval,@rval = lval,rval
+ end
+ def pp(state)
+ state.pp(@lval)
+ state.print(" = ")
+ state.pp(@rval)
+ end
+ end
+ class PointerDeref
+ attr_accessor :val
+ def initialize(val)
+ @val = val
+ end
+ def pp(state)
+ state.print("*")
+ state.pp(@val)
+ end
+ end
+ class Cast
+ attr_accessor :to,:val
+ def initialize(to,val)
+ @to,@val = to,val
+ end
+ def pp(state)
+ state.print("(")
+ state.pp(@to)
+ state.print(")")
+ state.pp(@val)
+ end
+ end
+ class VarDec
+ attr_accessor :type,:name
+ def initialize(type = nil,name = nil)
+ @type,@name = type,name
+ end
+ def pp(state)
+ state.pp(@type)
+ state.print(" ")
+ state.pp(@name)
+ end
+ end
+ class Return
+ attr_accessor :val
+ def initialize(val = nil)
+ @val = val
+ end
+ def pp(state)
+ state.print("return ")
+ state.pp(@val)
+ end
+ end
+ class TraversalState
+ attr_accessor :needs_semi,:indent,:col
+ def initialize(file)
+ @file = file
+ @indent = 0
+ @not_indented = true
+ @just_endlined = true
+ @hold_points = []
+ @col = 0
+ end
+ def set_hold_point()
+ @hold_points.push(@col)
+ end
+ def wrap(col)
+ @file.print("\n")
+ @file.print(" " * col)
+ @col = col
+ end
+ def suppress_indent()
+ @not_indented = false
+ end
+ def <<()
+ @indent -= 2
+ end
+ def >>()
+ @indent += 2
+ end
+ def pp(ast)
+ return ast.pp(self) if(ast.respond_to?(:pp))
+ self.print(ast)
+ end
+ def print(chars)
+ return print_no_split(chars) if(!chars.respond_to?(:split))
+ chars.split(/\n/,-1).each_with_index do |line,i|
+ endline() if(i != 0)
+ print_no_split(line) if(line && line.length > 0)
+ end
+ end
+ def print_no_split(chars)
+ if(@not_indented)
+ @file.print(" "*@indent)
+ @col += @indent
+ @not_indented = false
+ end
+ if(chars)
+ chars = chars.to_s
+ if(chars.length > 0)
+ @col += chars.length
+ @file.print(chars)
+ @just_endlined = false
+ @v_pad = 0
+ end
+ end
+ end
+ def endline()
+ return if(@just_endlined)
+ @file.print(";") if(@needs_semi)
+ @needs_semi = false
+ @file.print("\n")
+ @not_indented = true
+ @just_endlined = true
+ @hold_points.clear()
+ @v_pad += 1
+ @col = 0
+ end
+ def v_pad(n)
+ while(@v_pad < n)
+ force_endline()
+ end
+ end
+ def force_endline()
+ @just_endlined = false
+ endline()
+ end
+ end
+ def self.pretty_print(ast,file)
+ state = TraversalState.new(file)
+ if(ast.respond_to?(:pp_no_braces))
+ ast.pp_no_braces(state)
+ else
+ state.pp(ast)
+ end
+ end
+end
diff --git a/aos/build/queues/load.rb b/aos/build/queues/load.rb
new file mode 100644
index 0000000..59a4590
--- /dev/null
+++ b/aos/build/queues/load.rb
@@ -0,0 +1,15 @@
+$LOAD_PATH.unshift(".")
+["tokenizer.rb","q_file.rb","queue_group.rb","queue.rb","namespaces.rb",
+"interface.rb","errors.rb", "q_struct.rb"].each do |name|
+ require File.dirname(__FILE__) + "/objects/" + name
+end
+["standard_types.rb","auto_gen.rb","file_pair_types.rb",
+"dep_file_pair.rb"].each do |name|
+ require File.dirname(__FILE__) + "/cpp_pretty_print/" + name
+end
+["q_file.rb","message_dec.rb","queue_dec.rb", "q_struct.rb"].each do |name|
+ require File.dirname(__FILE__) + "/output/" + name
+end
+require File.dirname(__FILE__) + '/write_iff_changed.rb'
+require "fileutils"
+require "pathname"
diff --git a/aos/build/queues/objects/errors.rb b/aos/build/queues/objects/errors.rb
new file mode 100644
index 0000000..d776a22
--- /dev/null
+++ b/aos/build/queues/objects/errors.rb
@@ -0,0 +1,43 @@
+class QError < Exception
+ def initialize(msg)
+ super()
+ @msg = msg
+ @qstacktrace = []
+ end
+ def self.set_name(name)
+ @pretty_name = name
+ end
+ def self.pretty_name()
+ @pretty_name
+ end
+ def to_s
+ msg = "Error:(#{self.class.pretty_name})\n\t"
+ msg += @msg
+ msg += "\n" if(msg[-1] != "\n")
+ @qstacktrace.each do |part|
+ part = part.q_stack_name if(part.respond_to?(:q_stack_name))
+ msg += "\tfrom: #{part}\n"
+ end
+ return msg
+ end
+ set_name("Base Level Exception.")
+ attr_accessor :qstacktrace
+end
+class QSyntaxError < QError
+ def initialize(msg)
+ super(msg)
+ end
+ set_name("Syntax Error")
+end
+class QNamespaceCollision < QError
+ def initialize(msg)
+ super(msg)
+ end
+ set_name("Namespace Collision")
+end
+class QImportNotFoundError < QError
+ def initialize(msg)
+ super(msg)
+ end
+ set_name("Couldn't Find Target of Import Statement")
+end
diff --git a/aos/build/queues/objects/interface.rb b/aos/build/queues/objects/interface.rb
new file mode 100644
index 0000000..52332b6
--- /dev/null
+++ b/aos/build/queues/objects/interface.rb
@@ -0,0 +1,69 @@
+class MessageElementReq
+ def initialize(type,name)
+ @type = type
+ @name = name
+ end
+ def self.parse(tokens)
+ type = tokens.expect(:tWord).data
+ name = tokens.expect(:tWord).data
+ tokens.expect(:tSemi)
+ return self.new(type,name)
+ end
+end
+class QueueReq
+ def initialize(name,type = nil)
+ @name = name
+ @type = type
+ end
+ def self.parse(tokens)
+ type_or_name = tokens.expect(:tWord).data
+ if(tokens.peak == :tSemi)
+ tokens.expect(:tSemi)
+ return self.new(type_or_name)
+ else
+ name = tokens.expect(:tWord).data
+ tokens.expect(:tSemi)
+ return self.new(name,type_or_name)
+ end
+ end
+end
+class InterfaceStmt < QStmt
+ def initialize(name,elements)
+ @name = name
+ @elements = elements
+ end
+ def q_eval(locals)
+
+ end
+ def self.check_type(tokens,new_type,old_type)
+ return new_type if(old_type == nil)
+ if(new_type != old_type)
+ tokens.qError(<<ERROR_MSG)
+error: intermixing queue definitions (a queue_group feature)
+ and type definitions (a message_group feature)
+ this results in an undefined type value.
+ Wot. Wot.
+ERROR_MSG
+ end
+ return old_type
+ end
+ def self.parse(tokens)
+ name = tokens.expect(:tWord).data
+ values = []
+ type = nil
+ tokens.expect(:tOpenB)
+ while(tokens.peak != :tCloseB)
+ if(tokens.peak.data == "queue")
+ tokens.expect(:tWord)
+ values << QueueReq.parse(tokens)
+ type = check_type(tokens,:queue_group,type)
+ else
+ values << MessageElementReq.parse(tokens)
+ type = check_type(tokens,:message_group,type)
+ end
+ end
+ tokens.expect(:tCloseB)
+ tokens.expect(:tSemi)
+ self.new(name,values)
+ end
+end
diff --git a/aos/build/queues/objects/namespaces.rb b/aos/build/queues/objects/namespaces.rb
new file mode 100644
index 0000000..ea3aa80
--- /dev/null
+++ b/aos/build/queues/objects/namespaces.rb
@@ -0,0 +1,211 @@
+class LocalSituation
+ attr_accessor :globals,:local
+ def initialize(globals)
+ @globals = globals
+ @local = nil
+ end
+ def package=(qualified)
+ if(@local)
+ raise QSyntaxError.new(<<ERROR_MSG)
+You are redefining the package path.
+ Stuff might break if you do that.
+ Other options include: using another header file, and just using the same namespace.
+ If you are confident that you need this you can remove this check at
+ #{__FILE__}:#{__LINE__}.
+ Or file a feature request.
+ But that would be weird...
+ Wot. Wot.
+ERROR_MSG
+ end
+ @local = @globals.space
+ qualified.names.each do |name|
+ @local = @local.get_make(name)
+ end
+ end
+ def register(value)
+ if(!@local)
+ raise QError.new(<<ERROR_MSG)
+There is no package path defined, This is a big problem because
+ we are kindof expecting you to have a package path...
+ use a :
+ package my_super.cool.project;
+ statement to remedy this situation. (or file a feature request)
+ Wot. Wot.
+ERROR_MSG
+ end
+ @local[value.name] = value
+ value.parent = @local if(value.respond_to?(:parent=))
+ value.loc = @local
+ end
+ def bind(bind_to)
+ return BoundSituation.new(self,bind_to)
+ end
+end
+class BoundSituation < LocalSituation
+ def initialize(locals,bind_to)
+ @globals = locals.globals
+ @local = bind_to
+ end
+end
+class NameSpace
+ attr_accessor :parent,:name
+ def initialize(name = nil,parent = nil)
+ @name = name
+ @parent = parent
+ @spaces = {}
+ end
+ def []=(key,val)
+ if(old_val = @spaces[key])
+ old_val = old_val.created_by if(old_val.respond_to?(:created_by) && old_val.created_by)
+ if(old_val.respond_to?(:q_stack_name))
+ old_val = old_val.q_stack_name
+ else
+ old_val = "eh, it is a #{old_val.class} thats all I know..."
+ end
+ raise QNamespaceCollision.new(<<ERROR_MSG)
+Woah! The name #{queue_name(key).inspect} is already taken by some chap at #{old_val}.
+\tFind somewhere else to peddle your wares.
+\tWot. Wot.
+ERROR_MSG
+ end
+ @spaces[key] = val
+ end
+ def to_cpp_id(name)
+ txt = @name + "::" + name
+ return @parent.to_cpp_id(txt) if(@parent && parent.name)
+ return "::" + txt
+ end
+ def queue_name(queue)
+ get_name() + "." + queue
+ end
+ def [](key)
+ #puts "getting #{get_name}.#{key}"
+ @spaces[key]
+ end
+ def get_make(name)
+ @spaces[name] ||= self.class.new(name,self)
+ end
+ def get_name()
+ if(@parent)
+ return "#{@parent.get_name}.#{@name}"
+ else
+ return "#{@name}"
+ end
+ end
+ def create(cpp_tree)
+ if(@parent && @parent.name)
+ parent = cpp_tree.get(@parent)
+ else
+ parent = cpp_tree
+ end
+ return cpp_tree.add_namespace(@name,parent)
+ end
+ def to_s()
+ "<NameSpace: #{get_name()}>"
+ end
+ def inspect()
+ "<NameSpace: #{get_name()}>"
+ end
+ def root()
+ return self if(@parent == nil)
+ return @parent.root
+ end
+end
+class Globals
+ attr_accessor :space
+ def initialize()
+ @space = NameSpace.new()
+ @space.get_make("aos")
+ @include_paths = []
+ end
+ def paths()
+ @include_paths
+ end
+ def add_path(path)
+ @include_paths << path
+ end
+ def find_file(filename)
+ @include_paths.each do |path_name|
+ new_path = File.expand_path(path_name) + "/" + filename
+ if(File.exists?(new_path))
+ return new_path
+ end
+ end
+ raise QImportNotFoundError.new(<<ERROR_MSG)
+Problem Loading:#{filename.inspect} I looked in:
+\t#{(@include_paths.collect {|name| name.inspect}).join("\n\t")}
+\tbut alas, it was nowhere to be found.
+\tIt is popular to include the top of the repository as the include path start,
+\tand then reference off of that.
+\tI would suggest doing that and then trying to build again.
+\tWot. Wot.
+ERROR_MSG
+ end
+end
+class QualifiedName
+ attr_accessor :names,:off_root
+ def initialize(names,off_root = false)
+ @names = names
+ @off_root = off_root
+ end
+ def test_lookup(namespace)
+ @names.each do |name|
+ return nil if(!namespace.respond_to?(:[]))
+ namespace = namespace[name]
+ return nil if(!namespace)
+ end
+ return namespace
+ end
+ def is_simple?()
+ return !@off_root && @names.length == 1
+ end
+ def to_simple()
+ return @names[-1]
+ end
+ def to_s()
+ if(@off_root)
+ return ".#{@names.join(".")}"
+ else
+ return @names.join(".")
+ end
+ end
+ def lookup(locals)
+ if(@off_root)
+ local = locals.globals.space
+ else
+ local = locals.local
+ end
+ target = nil
+ while(!target && local)
+ target = test_lookup(local)
+ local = local.parent
+ end
+ return target if(target)
+ if(@off_root)
+ raise QError.new(<<ERROR_MSG)
+I was looking for .#{@names.join(".")}, but alas, it was not under
+\tthe root namespace.
+\tI'm really sorry old chap.
+\tWot. Wot.
+ERROR_MSG
+ else
+ raise QError.new(<<ERROR_MSG)
+I was looking for #{@names.join(".")}, but alas, I could not find
+\tit in #{locals.local.get_name} or any parent namespaces.
+\tI'm really sorry old chap.
+\tWot. Wot.
+ERROR_MSG
+ end
+ end
+ def self.parse(tokens)
+ names = []
+ off_root = (tokens.peak == :tDot)
+ tokens.next if(off_root)
+ names << tokens.expect(:tWord).data
+ while(tokens.peak == :tDot)
+ tokens.next
+ names << tokens.expect(:tWord).data
+ end
+ return self.new(names,off_root)
+ end
+end
diff --git a/aos/build/queues/objects/q_file.rb b/aos/build/queues/objects/q_file.rb
new file mode 100644
index 0000000..f73b638
--- /dev/null
+++ b/aos/build/queues/objects/q_file.rb
@@ -0,0 +1,150 @@
+class QStmt
+ def set_line(val)
+ @file_line = val
+ return self
+ end
+ def q_stack_name()
+ @file_line
+ end
+ def self.method_added(name)
+ @wrapped ||= {}
+ return if(name != :q_eval && name != :q_eval_extern)
+ return if(@wrapped[name])
+ @wrapped[name] = true
+ method = self.instance_method(name)
+ define_method(name) do |*args,&blk|
+ begin
+ method.bind(self).call(*args,&blk)
+ rescue QError => e
+ e.qstacktrace << self
+ raise e
+ end
+ end
+ end
+
+end
+class ImportStmt < QStmt
+ def initialize(filename)
+ @filename = filename
+ end
+ def q_eval(locals)
+ filename = locals.globals.find_file(@filename)
+ #puts "importing #{filename.inspect}"
+ q_file = QFile.parse(filename)
+ q_output = q_file.q_eval_extern(locals.globals)
+ q_output.extern = true
+ return Target::QInclude.new(@filename + ".h")
+ end
+ def self.parse(tokens)
+ line = tokens.pos
+ to_import = (tokens.expect(:tString) do |token|
+ <<ERROR_MSG
+I found a #{token.humanize} at #{token.pos}.
+\tI was really looking for a "filename" for this import statement.
+\tSomething like: import "super_cool_file";
+\tWot.Wot
+ERROR_MSG
+ end).data
+ tokens.expect(:tSemi) do |token|
+ <<ERROR_MSG
+I found a #{token.humanize} at #{token.pos}.
+\tI was really looking for a ";" to finish off this import statement
+\tat line #{line};
+\tSomething like: import #{to_import.inspect};
+\tWot.Wot #{" "*to_import.inspect.length}^
+ERROR_MSG
+ end
+ return self.new(to_import).set_line(line)
+ end
+end
+class PackageStmt < QStmt
+ def initialize(name)
+ @name = name
+ end
+ def q_eval(locals)
+ locals.package = @name
+ return nil
+ end
+ def self.parse(tokens)
+ line = tokens.pos
+ qualified_name = QualifiedName.parse(tokens)
+ tokens.expect(:tSemi)
+ return self.new(qualified_name).set_line(line)
+ end
+end
+class QFile
+ attr_accessor :namespace
+ def initialize(filename,suite)
+ @filename,@suite = filename,suite
+ end
+ def q_eval(globals = Globals.new())
+ local_pos = LocalSituation.new(globals)
+ q_file = Target::QFile.new()
+ @suite.each do |name|
+ val = name.q_eval(local_pos)
+ if(val)
+ if(val.respond_to?(:create))
+ q_file.add_type(val)
+ end
+ end
+ end
+ @namespace = local_pos.local
+ return q_file
+ end
+ def q_eval_extern(globals)
+ local_pos = LocalSituation.new(globals)
+ q_file = Target::QFile.new()
+ @suite.each do |name|
+ if(name.respond_to?(:q_eval_extern))
+ val = name.q_eval_extern(local_pos)
+ else
+ val = name.q_eval(local_pos)
+ end
+ if(val)
+ if(val.respond_to?(:create))
+ q_file.add_type(val)
+ end
+ end
+ end
+ return q_file
+ end
+ def self.parse(filename)
+ tokens = Tokenizer.new(filename)
+ suite = []
+ while(tokens.peak != :tEnd)
+ token = tokens.expect(:tWord) do |token| #symbol
+ <<ERROR_MSG
+I found a #{token.humanize} at #{token.pos}.
+\tI was really looking for a "package", "import", "queue_group",
+\t"message", or "queue" statement to get things moving.
+\tSomething like: import "super_cool_file";
+\tWot.Wot
+ERROR_MSG
+ end
+ case token.data
+ when "package"
+ suite << PackageStmt.parse(tokens)
+ when "import"
+ suite << ImportStmt.parse(tokens)
+ when "queue_group"
+ suite << QueueGroupStmt.parse(tokens)
+ when "message"
+ suite << MessageStmt.parse(tokens)
+ when "queue"
+ suite << QueueStmt.parse(tokens)
+ when "interface"
+ suite << InterfaceStmt.parse(tokens)
+ when "struct"
+ suite << StructStmt.parse(tokens)
+ else
+ tokens.qError(<<ERROR_MSG)
+expected a "package","import","queue","struct","queue_group", or "message" statement rather
+ than a #{token.data.inspect}, (whatever that is?)
+ oh! no! a feature request!?
+ Wot. Wot.
+ERROR_MSG
+ end
+ end
+ return self.new(filename,suite)
+ end
+end
diff --git a/aos/build/queues/objects/q_struct.rb b/aos/build/queues/objects/q_struct.rb
new file mode 100644
index 0000000..cd47fe6
--- /dev/null
+++ b/aos/build/queues/objects/q_struct.rb
@@ -0,0 +1,37 @@
+
+class StructStmt
+ def initialize(name,suite)
+ @name = name
+ @suite = suite
+ end
+ def q_eval(locals)
+ group = Target::StructDec.new(@name)
+ locals.register(group)
+ @suite.each do |stmt|
+ stmt.q_eval(locals.bind(group))
+ end
+ return group
+ end
+ def self.parse(tokens)
+ name = tokens.expect(:tWord).data
+ values = []
+ tokens.expect(:tOpenB)
+ while(tokens.peak != :tCloseB)
+ values << MessageElementStmt.parse(tokens)
+ end
+ names = {}
+ values.each do |val|
+ if(names[val.name])
+ raise QSyntaxError.new(<<ERROR_MSG)
+Hey! duplicate name #{val.name.inspect} in your message declaration statement (message #{name}).
+\tI found them at: #{names[val.name].q_stack_name()} and #{val.q_stack_name()}.
+\tWot. Wot.
+ERROR_MSG
+ end
+ names[val.name] = val
+ end
+ tokens.expect(:tCloseB)
+ tokens.expect(:tSemi)
+ self.new(name,values)
+ end
+end
diff --git a/aos/build/queues/objects/queue.rb b/aos/build/queues/objects/queue.rb
new file mode 100644
index 0000000..81cd181
--- /dev/null
+++ b/aos/build/queues/objects/queue.rb
@@ -0,0 +1,173 @@
+class MessageElementStmt < QStmt
+ attr_accessor :name
+ def initialize(type_name,name,length = nil) #lengths are for arrays
+ @type_name = type_name
+ @type = type_name.to_s
+ @type = '::aos::time::Time' if @type == 'Time'
+ @name = name
+ @length = length
+ end
+ CommonMistakes = {"short" => "int16_t","int" => "int32_t","long" => "int64_t","time" => "Time"}
+ def check_type_error(locals)
+ if(!(Sizes[@type] || (@length != nil && @type == "char")) )
+ if(correction = CommonMistakes[@type])
+ raise QError.new(<<ERROR_MSG)
+Hey! you have a \"#{@type}\" in your message statement.
+\tplease use #{correction} instead. Your type is not supported because we
+\twant to guarantee that the sizes of the messages stay the same across platforms.
+\tWot. Wot.
+ERROR_MSG
+ elsif(@type == "char")
+ raise QError.new(<<ERROR_MSG)
+Hey! you have a \"#{@type}\" in your message statement.
+\tyou need your declaration to be a char array like: char[10].
+\tor, please use int8_t or uint8_t.
+\tWot. Wot.
+ERROR_MSG
+ else
+ @is_struct_type = true
+ return if(lookup_type(locals))
+ raise QError.new(<<ERROR_MSG)
+Hey! you have a \"#{@type}\" in your message statement.
+\tThat is not in the list of supported types.
+\there is the list of supported types:
+\tint{8,16,32,64}_t,uint{8,16,32,64}_t,bool,float,double
+\tWot. Wot.
+ERROR_MSG
+ end
+ end
+ end
+
+ PrintFormat = {"bool" => "%c",
+ "float" => "%f",
+ "char" => "%c",
+ "double" => "%f",
+ "uint8_t" => "%\" PRIu8 \"",
+ "uint16_t" => "%\" PRIu16 \"",
+ "uint32_t" => "%\" PRIu32 \"",
+ "uint64_t" => "%\" PRIu64 \"",
+ "int8_t" => "%\" PRId8 \"",
+ "int16_t" => "%\" PRId16 \"",
+ "int32_t" => "%\" PRId32 \"",
+ "int64_t" => "%\" PRId64 \"",
+ "::aos::time::Time" => "\" AOS_TIME_FORMAT \""}
+ def toPrintFormat()
+ if(format = PrintFormat[@type])
+ return format;
+ end
+ raise QError.new(<<ERROR_MSG)
+Somehow this slipped past me, but
+\tI couldn't find the print format of #{@type}. Really, my bad.
+\tWot. Wot.
+ERROR_MSG
+ end
+
+ Sizes = {"bool" => 1, "float" => 4,"double" => 8,"::aos::time::Time" => 8}
+ [8,16,32,64].each do |len|
+ Sizes["int#{len}_t"] = len / 8
+ Sizes["uint#{len}_t"] = len / 8
+ end
+ Zero = {"float" => "0.0f","double" => "0.0","bool" => "false","::aos::time::Time" => "::aos::time::Time(0, 0)"}
+ def size()
+ if(size = Sizes[@type]); return size; end
+ return 1 if(@type == "char")
+ raise QError.new(<<ERROR_MSG)
+Somehow this slipped past me, but
+\tI couldn't find the size of #{@type}. Really, my bad.
+\tWot. Wot.
+ERROR_MSG
+ end
+ def lookup_type(locals)
+ return @type_name.lookup(locals)
+ end
+ def q_eval(locals)
+ check_type_error(locals)
+ if(@is_struct_type)
+ tval = lookup_type(locals)
+ member = Target::MessageStructElement.new(tval, @name)
+ if (@length != nil)
+ inner_member = member
+ member = Target::MessageArrayElement.new(inner_member, @length)
+ end
+ else
+ member = Target::MessageElement.new(@type,@name)
+ member.size = size()
+ member.zero = Zero[@type] || "0";
+ member.printformat = toPrintFormat()
+
+ if(@length != nil)
+ inner_member = member
+ member = Target::MessageArrayElement.new(inner_member,@length)
+ end
+ end
+ locals.local.add_member(member)
+ end
+ def self.parse(tokens)
+ line = tokens.pos
+ #type = tokens.expect(:tWord).data
+ type_name = QualifiedName.parse(tokens)
+ len = nil
+ if(tokens.peak == :tOpenB)
+ tokens.expect(:tOpenB)
+ len = tokens.expect(:tNumber).data
+ tokens.expect(:tCloseB)
+ end
+ name = tokens.expect(:tWord).data
+ tokens.expect(:tSemi)
+ return self.new(type_name,name,len).set_line(line)
+ end
+end
+class MessageStmt < QStmt
+ def initialize(name,suite)
+ @name = name
+ @suite = suite
+ end
+ def q_eval(locals)
+ group = Target::MessageDec.new(@name)
+ locals.register(group)
+ @suite.each do |stmt|
+ stmt.q_eval(locals.bind(group))
+ end
+ return group
+ end
+ def self.parse(tokens)
+ name = tokens.expect(:tWord).data
+ values = []
+ tokens.expect(:tOpenB)
+ while(tokens.peak != :tCloseB)
+ values << MessageElementStmt.parse(tokens)
+ end
+ names = {}
+ values.each do |val|
+ if(names[val.name])
+ raise QSyntaxError.new(<<ERROR_MSG)
+Hey! duplicate name #{val.name.inspect} in your message declaration statement (message #{name}).
+\tI found them at: #{names[val.name].q_stack_name()} and #{val.q_stack_name()}.
+\tWot. Wot.
+ERROR_MSG
+ end
+ names[val.name] = val
+ end
+ tokens.expect(:tCloseB)
+ tokens.expect(:tSemi)
+ self.new(name,values)
+ end
+end
+class QueueStmt < QStmt
+ def initialize(type,name)
+ @type,@name = type,name
+ end
+ def q_eval(locals)
+ queue = Target::QueueDec.new(@type.lookup(locals),@name)
+ locals.register(queue)
+ locals.local.add_queue(queue) if(locals.local.respond_to?(:add_queue))
+ return queue
+ end
+ def self.parse(tokens)
+ line = tokens.pos
+ type_name = QualifiedName.parse(tokens)
+ name = tokens.expect(:tWord).data
+ tokens.expect(:tSemi)
+ return self.new(type_name,name).set_line(line)
+ end
+end
diff --git a/aos/build/queues/objects/queue_group.rb b/aos/build/queues/objects/queue_group.rb
new file mode 100644
index 0000000..136834f
--- /dev/null
+++ b/aos/build/queues/objects/queue_group.rb
@@ -0,0 +1,115 @@
+class QueueGroupTypeStmt < QStmt
+ def initialize(name,suite)
+ @name,@suite = name,suite
+ end
+ def q_eval(locals)
+ group = Target::QueueGroupDec.new(@name)
+ group.created_by = self
+ locals.register(group)
+ @suite.each do |stmt|
+ stmt.q_eval(locals.bind(group))
+ end
+ return group
+ end
+end
+class ImplementsStmt < QStmt
+ def initialize(name)
+ @name = name
+ end
+ def q_eval(locals)
+
+ end
+ def self.parse(tokens)
+ name = QualifiedName.parse(tokens)
+ tokens.expect(:tSemi)
+ return self.new(name)
+ end
+end
+class QueueGroupStmt < QStmt
+ def initialize(type,name)
+ @type,@name = type,name
+ end
+ def q_eval(locals)
+ group = Target::QueueGroup.new(@type.lookup(locals),@name)
+ group.created_by = self
+ locals.register(group)
+ return group
+ end
+ def self.parse(tokens)
+ line = tokens.pos
+ type_name = QualifiedName.parse(tokens)
+ if(type_name.names.include?("queue_group"))
+ tokens.qError(<<ERROR_MSG)
+I was looking at the identifier you gave
+\tfor the queue group type between line #{line} and #{tokens.pos}
+\tThere shouldn't be a queue_group type called queue_group
+\tor including queue_group in it's path, it is a reserved keyword.
+\tWot. Wot.
+ERROR_MSG
+ end
+ if(tokens.peak == :tOpenB)
+ if(type_name.is_simple?())
+ type_name = type_name.to_simple
+ else
+ tokens.qError(<<ERROR_MSG)
+You gave the name: "#{type_name.to_s}" but you're only allowed to
+\thave simple names like "#{type_name.names[-1]}" in queue_group definitions
+\ttry something like:
+\tqueue_group ControlLoop { }
+\tWot. Wot.
+ERROR_MSG
+ end
+ tokens.expect(:tOpenB)
+ suite = []
+ while(tokens.peak != :tCloseB)
+ token = tokens.expect(:tWord) do |token|
+ <<ERROR_MSG
+I'm a little confused, I found a #{token.humanize} at #{token.pos}
+\tbut what I really wanted was an identifier signifying a nested
+\tmessage declaration, or queue definition, or an impliments statement.
+\tWot.Wot
+ERROR_MSG
+ end
+ case token.data
+ when "message"
+ suite << MessageStmt.parse(tokens)
+ when "queue"
+ suite << QueueStmt.parse(tokens)
+ when "implements"
+ suite << ImplementsStmt.parse(tokens)
+ else
+ tokens.qError(<<ERROR_MSG)
+expected a "queue","implements" or "message" statement rather
+\tthan a #{token.data.inspect}.
+\tWot. Wot.
+ERROR_MSG
+ end
+ end
+ tokens.expect(:tCloseB)
+ obj = QueueGroupTypeStmt.new(type_name,suite).set_line(line)
+ else
+ name = (tokens.expect(:tWord) do |token|
+ <<ERROR_MSG
+I found a #{token.humanize} at #{token.pos}
+\tbut I was in the middle of parsing a queue_group statement, and
+\twhat I really wanted was an identifier to store the queue group.
+\tSomething like: queue_group control_loops.Drivetrain my_cool_group;
+\tWot.Wot
+ERROR_MSG
+ end).data
+ obj = QueueGroupStmt.new(type_name,name).set_line(line)
+ if(tokens.peak == :tDot)
+ tokens.qError(<<ERROR_MSG)
+Hey! It looks like you're trying to use a complex identifier at: #{tokens.pos}
+\tThats not going to work. Queue Group definitions have to be of the form:
+\tqueue_group ComplexID SimpleID
+\tWot. Wot.
+ERROR_MSG
+ end
+ end
+ tokens.expect(:tSemi) do |token|
+ token.pos
+ end
+ return obj
+ end
+end
diff --git a/aos/build/queues/objects/tokenizer.rb b/aos/build/queues/objects/tokenizer.rb
new file mode 100644
index 0000000..2a33b90
--- /dev/null
+++ b/aos/build/queues/objects/tokenizer.rb
@@ -0,0 +1,213 @@
+class BufferedReader
+ def initialize(file)
+ @file = file
+ @line = 1
+ @chars = []
+ @col_nums = []
+ @col = 0
+ end
+ def filename
+ return File.basename(@file.path)
+ end
+ def pos()
+ "#{filename()}:#{lineno()},#{@col}"
+ end
+ def clear_comment()
+ @file.gets
+ @line += 1
+ end
+ def lineno()
+ return @line
+ return @file.lineno + 1
+ end
+ def pop_char()
+ val = @chars.pop() || @file.read(1)
+ @col_nums[@line] = @col += 1
+ if(val == "\n")
+ @line += 1
+ @col = 0
+ end
+
+ return val
+ end
+ def unpop_char(char)
+ if(char == "\n")
+ @line -= 1
+ @col = @col_nums[@line]
+ end
+ @col -= 1
+ @chars.push(char)
+ end
+end
+class Tokenizer
+ TOKEN_TYPES = {"{" => :tOpenB,"}"=> :tCloseB,";" => :tSemi,"," => :tComma,
+ "(" => :tOpenParan,")" => :tCloseParan,"=" => :tAssign,"." => :tDot,
+ "<<"=> :tLShift,"*" => :tMult,"+" => :tAdd,"[" => :tOpenB,
+ "]" => :tCloseB}
+ Humanize = TOKEN_TYPES.invert
+ class Token
+ attr_accessor :type,:data,:pos
+ def to_s
+ if(@type == :tString)
+ val = @data.inspect.to_s
+ elsif(@type == :tWord)
+ val = @data.to_s
+ else
+ val = @data.to_s
+ end
+ return "#{val.ljust(50)}:#{@type}"
+ end
+ def humanize()
+ if(@type == :tString)
+ return "#{@data.inspect.to_s} string"
+ elsif(@type == :tWord)
+ return "#{@data.inspect} identifier"
+ end
+ return Humanize[@type].inspect
+ end
+ def inspect()
+ data = ""
+ data = " #{@data.inspect}" if(@data)
+ "<Token :#{@type}#{data} at #{@pos}>"
+ end
+ def ==(other)
+ if(other.class == Symbol)
+ return @type == other
+ elsif(other.class == self.class)
+ return @type == other.type && @data == other.data
+ else
+ return nil
+ end
+ end
+ end
+ def initialize(file)
+ file = File.open(file,"r") if(file.class == String)
+ @read = BufferedReader.new(file)
+ end
+ def qError(error)
+ syntax_error(error)
+ end
+ def syntax_error(msg)
+ err = QSyntaxError.new(msg)
+ err.qstacktrace << "#{@read.lineno} of #{@read.filename}"
+ raise err
+ end
+ def peak_token()
+ @peak_token = next_token()
+ end
+ def peak()
+ peak_token()
+ end
+ def next()
+ next_token()
+ end
+ def pos()
+ @read.pos
+ end
+ def tokenize(string_token)
+ token = Token.new()
+ token.type = TOKEN_TYPES[string_token]
+ return token
+ end
+ def next_token()
+ if(token = @peak_token)
+ @peak_token = nil
+ return token
+ end
+ token = next_token_cache()
+ pos = self.pos()
+ token.pos = pos
+ return token
+ end
+ def next_token_cache()
+ token = Token.new()
+ token.data = ""
+ while (char = @read.pop_char())
+ #puts "#{char.inspect}:#{token.inspect}"
+ if(char == "/")
+ if(@read.pop_char == "/")
+ @read.clear_comment()
+ else
+ syntax_error("unexpected #{char.inspect}")
+ end
+ elsif(char == "#")
+ @read.clear_comment()
+ elsif(char =~ /[\s\r\n]/)
+ if(token.type)
+ return token
+ end
+ elsif(char =~ /\"/)
+ token.type = :tString
+ token.data = ""
+ while((char = @read.pop_char) != "\"")
+ token.data += char
+ end
+ return token
+ elsif(char =~ /[1-9]/)
+ token.type = :tNumber
+ token.data = char.to_i
+ while(char != ".")
+ char = @read.pop_char
+ if(char =~ /[0-9]/)
+ token.data = char.to_i + token.data * 10
+ elsif(char == ".")
+ else
+ @read.unpop_char(char)
+ return token
+ end
+ end
+ second_char = 0
+ man = 0
+ while(true)
+ char = @read.pop_char
+ if(char =~ /[0-9]/)
+ second_char = char.to_i + second_char * 10
+ man = man * 10
+ else
+ @read.unpop_char(char)
+ token.data += second_char / man.to_f
+ return token
+ end
+ end
+ elsif(char == ":")
+ if(@read.pop_char == "=")
+ return tokenize(":=")
+ end
+ syntax_error("unexpected \":\"")
+ elsif(char =~ /[;\{\}=()\",\*\+\.\[\]]/)
+ return(tokenize(char))
+ elsif(char =~ /[a-zA-Z_]/)
+ token.type = :tWord
+ token.data = char
+ while(true)
+ char = @read.pop_char()
+ if(char && char =~ /\w/)
+ token.data += char
+ else
+ @read.unpop_char(char)
+ return token
+ end
+ end
+ elsif(char == "<")
+ if((char = @read.pop_char()) == "<")
+ return tokenize("<<")
+ else
+ @read.unpop_char(char)
+ return tokenize("<")
+ end
+ else
+ syntax_error("unexpected #{char.inspect}")
+ end
+ end
+ token.type = :tEnd
+ return token
+ end
+ def expect(type,&blk)
+ token = self.next
+ if(token != type)
+ syntax_error(blk.call(token)) if(blk)
+ syntax_error("unexpected: #{token.type}")
+ end
+ return token
+ end
+end
diff --git a/aos/build/queues/output/message_dec.rb b/aos/build/queues/output/message_dec.rb
new file mode 100644
index 0000000..5b4e136
--- /dev/null
+++ b/aos/build/queues/output/message_dec.rb
@@ -0,0 +1,428 @@
+begin
+ require "sha1"
+rescue LoadError
+ require "digest/sha1"
+end
+
+class Target::StructBase < Target::Node
+ def create_DoGetType(type_class, cpp_tree)
+ member_func = CPP::MemberFunc.new(type_class,"const ::aos::MessageType*","DoGetType")
+ member_func.static = true
+ fields = []
+ register_members = []
+ @members.each do |member|
+ tId = member.getTypeID()
+ fieldName = member.name.inspect
+ if(member.respond_to?(:add_TypeRegister))
+ register_members.push(member)
+ end
+ fields << "new ::aos::MessageType::Field{#{tId}, #{member.respond_to?(:length) ? member.length : 0}, #{fieldName}}"
+ end
+ register_members.uniq do |member|
+ member.type
+ end.each do |member|
+ member.add_TypeRegister(cpp_tree, type_class, member_func)
+ end
+ id = getTypeID()
+ member_func.suite << ("static const ::aos::MessageType kMsgMessageType(#{type_class.parent_class ? type_class.parent_class + '::Size()' : 0}, #{id}, #{(@loc.respond_to?(:queue_name) ? @loc.queue_name(@name) : "#{@loc.get_name()}.#{@name}").inspect}, {" +
+ "#{fields.join(", ")}})");
+ type_class.add_member(member_func)
+ member_func.suite << "::aos::type_cache::Add(kMsgMessageType)"
+ member_func.suite << CPP::Return.new("&kMsgMessageType")
+ end
+ def create_InOrderConstructor(type_class, cpp_tree)
+ if @members.empty?
+ return
+ end
+ cons = CPP::Constructor.new(type_class)
+ type_class.add_member(cons)
+ @members.each do |member|
+ if member.respond_to?(:type_name)
+ type_name = "#{member.type_name(cpp_tree)} #{member.name}_in"
+ else
+ type_name = member.create_usage(cpp_tree, "#{member.name}_in")
+ end
+
+ cons.args << type_name
+ cons.add_cons(member.name, member.name + '_in')
+ end
+ end
+ def create_DefaultConstructor(type_class, cpp_tree)
+ cons = CPP::Constructor.new(type_class)
+ type_class.add_member(cons)
+ cons.add_cons(type_class.parent_class) if type_class.parent_class
+ cons.suite << CPP::FuncCall.build('Zero')
+ end
+ def create_Zero(type_class,cpp_tree)
+ member_func = CPP::MemberFunc.new(type_class,"void","Zero")
+ type_class.add_member(member_func)
+ @members.each do |elem|
+ elem.zeroCall(member_func.suite)
+ end
+ member_func.suite << CPP::FuncCall.new(type_class.parent_class + '::Zero') if type_class.parent_class
+ end
+ def create_Size(type_class,cpp_tree)
+ member_func = CPP::MemberFunc.new(type_class,"size_t","Size")
+ member_func.inline = true
+ member_func.static = true
+ type_class.add_member(member_func.forward_dec)
+ size = 0
+ @members.each do |elem|
+ size += elem.size
+ end
+ if type_class.parent_class
+ member_func.suite << CPP::Return.new(CPP::Add.new(size,
+ "#{type_class.parent_class}::Size()"))
+ else
+ member_func.suite << CPP::Return.new(size)
+ end
+ end
+ def create_Serialize(type_class,cpp_tree)
+ member_func = CPP::MemberFunc.new(type_class,"size_t","Serialize")
+ type_class.add_member(member_func)
+ member_func.args << "char *buffer"
+ member_func.suite << "#{type_class.parent_class}::Serialize(buffer)" if type_class.parent_class
+ member_func.const = true
+ offset = type_class.parent_class ? type_class.parent_class + '::Size()' : '0'
+ @members.each do |elem|
+ elem.toNetwork(offset,member_func.suite)
+ offset += " + #{elem.size}";
+ end
+ member_func.suite << CPP::Return.new(CPP::FuncCall.new('Size'))
+ end
+ def create_Deserialize(type_class,cpp_tree)
+ member_func = CPP::MemberFunc.new(type_class,"size_t","Deserialize")
+ type_class.add_member(member_func)
+ member_func.args << "const char *buffer"
+ member_func.suite << "#{type_class.parent_class}::Deserialize(buffer)" if type_class.parent_class
+ offset = type_class.parent_class ? type_class.parent_class + '::Size()' : '0'
+ @members.each do |elem|
+ elem.toHost(offset,member_func.suite)
+ offset += " + #{elem.size}";
+ end
+ member_func.suite << CPP::Return.new(CPP::FuncCall.new('Size'))
+ end
+ def create_EqualsNoTime(type_class,cpp_tree)
+ member_func = CPP::MemberFunc.new(type_class,"bool","EqualsNoTime")
+ member_func.const = true
+ type_class.add_member(member_func)
+ member_func.args << "const #{type_class.name} &other"
+ member_func.suite << "if (!#{type_class.parent_class}::EqualsNoTime(other)) return false;" if type_class.parent_class
+ @members.each do |elem|
+ if elem.respond_to? :create_EqualsNoTime
+ member_func.suite << "if (!other.#{elem.name}.EqualsNoTime(#{elem.name})) return false;"
+ elsif elem.respond_to?(:length) && elem.member_type.respond_to?(:create_EqualsNoTime)
+ 0.upto(elem.length - 1) do |i|
+ member_func.suite << "if (!other.#{elem.name}[#{i}].EqualsNoTime(#{elem.name}[#{i}])) return false;"
+ end
+ else
+ member_func.suite << "if (other.#{elem.name} != #{elem.name}) return false;"
+ end
+ end
+ member_func.suite << CPP::Return.new('true')
+ end
+ def simpleStr()
+ return "{\n" + @members.collect() { |elem| elem.simpleStr() + "\n"}.join("") + "}"
+ end
+ def getTypeID()
+ return "0x" + (((Digest::SHA1.hexdigest(simpleStr())[0..3].to_i(16)) << 16) | size).to_s(16)
+ end
+ def add_member(member)
+ @members << member
+ end
+ def size()
+ return @size if(@size)
+ @size = 0
+ @members.each do |elem|
+ @size += elem.size
+ end
+ return @size
+ end
+end
+
+class Target::MessageDec < Target::StructBase
+ attr_accessor :name,:loc,:parent,:msg_hash
+ def initialize(name)
+ @name = name
+ @members = []
+ end
+ def extern=(value)
+ @extern=value
+ end
+ def get_name()
+ if(@parent)
+ return "#{@parent.get_name}.#{@name}"
+ else
+ return "#{@name}"
+ end
+ end
+ def create_Print(type_class,cpp_tree)
+ member_func = CPP::MemberFunc.new(type_class,"size_t","Print")
+ type_class.add_member(member_func)
+ member_func.args << "char *buffer"
+ member_func.args << "size_t length"
+ member_func.const = true
+ format = "\""
+ args = []
+ @members.each do |elem|
+ format += ", "
+ format += elem.toPrintFormat()
+ elem.fetchPrintArgs(args)
+ end
+ format += "\""
+ member_func.suite << "size_t super_size = ::aos::Message::Print(buffer, length)"
+ member_func.suite << "buffer += super_size"
+ member_func.suite << "length -= super_size"
+ if !args.empty?
+ member_func.suite << "return super_size + snprintf(buffer, length, " + ([format] + args).join(", ") + ")";
+ else
+ # snprintf will return zero.
+ member_func.suite << "return super_size"
+ end
+ end
+ def create_GetType(type_class, cpp_tree)
+ member_func = CPP::MemberFunc.new(type_class,"const ::aos::MessageType*","GetType")
+ type_class.add_member(member_func)
+ member_func.static = true
+ member_func.suite << "static ::aos::Once<const ::aos::MessageType> getter(#{type_class.name}::DoGetType)"
+ member_func.suite << CPP::Return.new("getter.Get()")
+ end
+ def self.builder_loc(loc)
+ return @builder_loc if(@builder_loc)
+ return @builder_loc = loc.root.get_make("aos")
+ end
+ def type_name(builder_loc,name)
+ if(builder_loc == @loc) #use relative name
+ return name
+ else #use full name
+ return @loc.to_cpp_id(name)
+ end
+ end
+ def create(cpp_tree)
+ return self if(@extern)
+ orig_namespace = namespace = cpp_tree.get(@loc)
+ name = ""
+ if(namespace.class < Types::Type) #is nested
+ name = namespace.name + "_" + name
+ namespace = namespace.space
+ end
+ type_class = namespace.add_struct(name + @name)
+ if(name.length > 0)
+ orig_namespace.add_member(:public, Types::TypeDef.new(type_class,@name))
+ end
+ cpp_tree.set(self,type_class)
+ type_class.set_parent("public ::aos::Message")
+ ts = self.simpleStr()
+ self.msg_hash = "0x#{Digest::SHA1.hexdigest(ts)[-8..-1]}"
+ type_class.add_member("enum {kQueueLength = 100, kHash = #{self.msg_hash}}")
+ @members.each do |elem|
+ type_class.add_member(elem.create_usage(cpp_tree))
+ end
+
+ create_Serialize(type_class,cpp_tree)
+ create_Deserialize(type_class,cpp_tree)
+ create_Zero(type_class,cpp_tree)
+ create_Size(type_class,cpp_tree)
+ create_Print(type_class,cpp_tree)
+ create_GetType(type_class, cpp_tree)
+ create_DoGetType(type_class, cpp_tree)
+ create_DefaultConstructor(type_class, cpp_tree)
+ create_InOrderConstructor(type_class, cpp_tree)
+ create_EqualsNoTime(type_class, cpp_tree)
+
+ b_namespace = cpp_tree.get(b_loc = self.class.builder_loc(@loc))
+
+ template = b_namespace.add_template("MessageBuilder")
+ template.spec_args << t = @loc.to_cpp_id(@name)
+ msg_ptr_t = "ScopedMessagePtr< #{t}>"
+ msg_bld_t = "MessageBuilder< #{t}>"
+ template.add_member(:private,"#{msg_ptr_t} msg_ptr_")
+ template.add_member(:private,"#{msg_bld_t}(const #{msg_bld_t}&)")
+ template.add_member(:private,"void operator=(const #{msg_bld_t}&)")
+ template.add_member(:private,"friend class ::aos::Queue< #{t}>")
+
+ cons = CPP::Constructor.new(template)
+ template.add_member(:private,cons)
+ cons.args << "RawQueue *queue"
+ cons.args << "#{t} *msg"
+ cons.add_cons("msg_ptr_","queue","msg")
+ template.public
+ DefineMembers(cpp_tree, template, msg_bld_t)
+ end
+ def DefineMembers(cpp_tree, template, msg_bld_t)
+ send = template.def_func("bool","Send")
+ send.suite << CPP::Return.new("msg_ptr_.Send()")
+ @members.each do |elem|
+=begin
+ MessageBuilder<frc971::control_loops::Drivetrain::Goal> &steering(
+ double steering) {
+ msg_ptr_->steering = steering;
+ return *this;
+ }
+=end
+ setter = template.def_func(msg_bld_t,"&#{elem.name}")
+ setter.args << elem.create_usage(cpp_tree)
+ elem.set_message_builder(setter.suite)
+ setter.suite << CPP::Return.new("*this")
+
+ end
+ end
+end
+class Target::MessageElement < Target::Node
+ attr_accessor :name,:loc,:size,:zero,:type,:printformat
+ def initialize(type,name)
+ @type,@name = type,name
+ end
+ def type()
+ return @type
+ end
+ def type_name(cpp_tree)
+ return type()
+ end
+ def toPrintFormat()
+ return printformat
+ end
+ def create_usage(cpp_tree, this_name=nil)
+ if (this_name == nil)
+ this_name = @name
+ end
+ "#{@type} #{this_name}"
+ end
+ def toNetwork(offset,suite, parent = "", this_name=nil)
+ if (this_name == nil)
+ this_name = @name
+ end
+ suite << f_call = CPP::FuncCall.build("to_network",
+ "&#{parent}#{this_name}",
+ "&buffer[#{offset}]")
+ f_call.args.dont_wrap = true
+ end
+ def toHost(offset,suite, parent = "", this_name=nil)
+ if (this_name == nil)
+ this_name = @name
+ end
+ suite << f_call = CPP::FuncCall.build("to_host",
+ "&buffer[#{offset}]",
+ "&#{parent}#{this_name}")
+ f_call.args.dont_wrap = true
+ end
+ def getTypeID()
+ '0x' + ((Digest::SHA1.hexdigest(@type)[0..3].to_i(16) << 16) |
+ 0x2000 | # marks it as primitive
+ size).to_s(16)
+ end
+ def simpleStr()
+ "#{@type} #{@name}"
+ end
+ def set_message_builder(suite, this_name=nil)
+ this_name ||= @name
+ suite << "msg_ptr_->#{this_name} = #{this_name}"
+ end
+
+ def zeroCall(suite, parent = "", this_name=nil)
+ if (this_name == nil)
+ this_name = @name
+ end
+ suite << CPP::Assign.new(parent + this_name,@zero)
+ end
+ def fetchPrintArgs(args, parent = "", this_name=nil)
+ if (this_name == nil)
+ this_name = @name
+ end
+ if (@type == 'bool')
+ args.push("#{parent}#{this_name} ? 'T' : 'f'")
+ elsif (@type == '::aos::time::Time')
+ args.push("AOS_TIME_ARGS(#{parent}#{this_name}.sec(), #{parent}#{this_name}.nsec())")
+ else
+ args.push("#{parent}#{this_name}")
+ end
+ end
+end
+class Target::MessageArrayElement < Target::Node
+ attr_accessor :loc, :length
+ def initialize(type,length)
+ @type,@length = type,length
+ end
+ def zero()
+ return @type.zero()
+ end
+ def name()
+ return @type.name()
+ end
+
+ def add_TypeRegister(*args)
+ if @type.respond_to?(:add_TypeRegister)
+ @type.add_TypeRegister(*args)
+ end
+ end
+
+ def toPrintFormat()
+ return ([@type.toPrintFormat] * @length).join(", ")
+ end
+ def create_usage(cpp_tree, this_name=nil)
+ if (this_name == nil)
+ this_name = name
+ end
+ return "::std::array< #{@type.type_name(cpp_tree)}, #{@length}> #{this_name}"
+ end
+ def type()
+ return "::std::array< #{@type.type()}, #{@length}>"
+ end
+ def member_type()
+ return @type
+ end
+ def simpleStr()
+ return "#{@type.type} #{@name}[#{@length}]"
+ end
+ def getTypeID()
+ return @type.getTypeID()
+ end
+ def size()
+ return @type.size * @length
+ end
+ def toNetwork(offset, suite, parent="", this_name=nil)
+ if (this_name == nil)
+ this_name = name
+ end
+ offset = (offset == 0) ? "" : "#{offset} + "
+ for_loop_var = getForLoopVar()
+ suite << for_stmt = CPP::For.new("int #{for_loop_var} = 0","#{for_loop_var} < #{@length}","#{for_loop_var}++")
+ @type.toNetwork("#{offset} (#{for_loop_var} * #{@type.size})", for_stmt.suite, parent, "#{this_name}[#{for_loop_var}]")
+ end
+ def toHost(offset, suite, parent="", this_name=nil)
+ if (this_name == nil)
+ this_name = name
+ end
+ offset = (offset == 0) ? "" : "#{offset} + "
+ for_loop_var = getForLoopVar()
+ suite << for_stmt = CPP::For.new("int #{for_loop_var} = 0","#{for_loop_var} < #{@length}","#{for_loop_var}++")
+ @type.toHost("#{offset} (#{for_loop_var} * #{@type.size})", for_stmt.suite, parent, "#{this_name}[#{for_loop_var}]")
+ end
+ def set_message_builder(suite, this_name=nil)
+ if (this_name == nil)
+ this_name = name
+ end
+
+ for_loop_var = getForLoopVar()
+ suite << for_stmt = CPP::For.new("int #{for_loop_var} = 0","#{for_loop_var} < #{@length}","#{for_loop_var}++")
+ @type.set_message_builder(for_stmt.suite, "#{this_name}[#{for_loop_var}]")
+ end
+ def zeroCall(suite, parent="", this_name=nil)
+ if (this_name == nil)
+ this_name = name
+ end
+
+ offset = (offset == 0) ? "" : "#{offset} + "
+ for_loop_var = getForLoopVar()
+ suite << for_stmt = CPP::For.new("int #{for_loop_var} = 0","#{for_loop_var} < #{@length}","#{for_loop_var}++")
+ @type.zeroCall(for_stmt.suite, parent, "#{this_name}[#{for_loop_var}]")
+ end
+ def fetchPrintArgs(args, parent = "", this_name=nil)
+ if (this_name == nil)
+ this_name = name
+ end
+ for i in 0..(@length-1)
+ @type.fetchPrintArgs(args, parent, "#{this_name}[#{i}]")
+ end
+ end
+end
diff --git a/aos/build/queues/output/q_file.rb b/aos/build/queues/output/q_file.rb
new file mode 100644
index 0000000..471bdcb
--- /dev/null
+++ b/aos/build/queues/output/q_file.rb
@@ -0,0 +1,198 @@
+begin
+ require "sha1"
+rescue LoadError
+ require "digest/sha1"
+end
+
+$i = 0
+def getForLoopVar()
+ $i = $i + 1
+ return "_autogen_index_#{$i}"
+end
+
+module Target
+end
+class Target::Node
+ attr_accessor :created_by
+ def get_name()
+ if(@parent)
+ return "#{@parent.get_name}.#{@name}"
+ else
+ return "#{@name}"
+ end
+ end
+end
+class Target::QFile < Target::Node
+ def initialize() #needs to know repo_path,
+ @class_types = []
+ end
+ def add_type(type)
+ @class_types << type
+ end
+ def extern=(value)
+ @class_types.each do |type|
+ type.extern=value
+ end
+ end
+ def make_cpp_tree(rel_path)
+ cpp_tree = DepFilePair.new(rel_path)
+ cpp_tree.add_header_include("<array>")
+ cpp_tree.add_header_include("\"aos/common/macros.h\"")
+ cpp_tree.add_header_include("\"aos/common/queue.h\"")
+ @class_types.each do |type|
+ cpp_tree.get(type)
+ end
+ return cpp_tree
+ end
+end
+class Target::QInclude < Target::Node
+ def initialize(path)
+ @path = path
+ end
+ def create(cpp_tree)
+# inc = cpp_tree.header.add_include("\"#{@path}\"")
+ inc = cpp_tree.add_header_include("\"#{@path}\"")
+ cpp_tree.set(self,inc)
+ end
+end
+class Target::QueueGroupDec < Target::Node
+ attr_accessor :name,:loc,:parent,:queues
+ def initialize(name)
+ @name = name
+ @queues = []
+ @subs = {}
+ end
+ def root()
+ @parent.root()
+ end
+ def []=(key,val)
+ #puts "#{key}= #{val}"
+ @subs[key] = val
+ end
+ def extern=(value)
+ @extern=value
+ end
+ def get_name()
+ if(@parent)
+ return "#{@parent.get_name}.#{@name}"
+ else
+ return "#{@name}"
+ end
+ end
+ def to_cpp_id(id)
+ name = "#{@name}::#{id}"
+ return @parent.to_cpp_id(name) if(@parent)
+ return name
+ end
+ def [](key)
+ #puts "#{key}"
+ @subs[key]
+ end
+ def add_queue(queue)
+ @queues.push(queue)
+ end
+ def hash_with_name(name)
+ ts = (@queues.collect { |queue|
+ queue.msg_hash()
+ }).join("") + name
+ return "0x#{Digest::SHA1.hexdigest(ts)[-8..-1]}"
+ end
+ def create(cpp_tree)
+ return if(@extern)
+ namespace = cpp_tree.get(@loc)
+ type_class = Types::Class.new(namespace,@name)
+ cpp_tree.set(self,type_class) #breaks infinite recursion
+ type_class.set_parent("public ::aos::QueueGroup")
+ @queues.each do |queue|
+ type_class.add_member(:public,queue.create_usage(cpp_tree))
+ end
+ create_Constructor(type_class,cpp_tree)
+ namespace.add(type_class)
+ end
+ def create_Constructor(type_class,cpp_tree)
+ member_func = CPP::Constructor.new(type_class)
+ type_class.add_member(:public,member_func)
+ #cpp_tree.cc_file.add_funct(member_func)
+ member_func.args << "const char *name";
+ member_func.args << "uint32_t hash";
+ member_func.add_cons("::aos::QueueGroup","name", "hash")
+ @queues.each do |queue|
+ member_func.args << "const char *#{queue.name}_name";
+ member_func.add_cons(queue.name,"#{queue.name}_name")
+ end
+ end
+end
+class Target::QueueGroup < Target::Node
+ attr_accessor :name,:loc
+ def initialize(type,name)
+ @type,@name = type,name
+ end
+ def type_name()
+ if(@type.loc == @loc) #use relative name
+ return @type.name
+ else #use full name
+ return @type.loc.to_cpp_id(@type.name)
+ end
+ end
+ def create(cpp_tree)
+ namespace = cpp_tree.get(@loc)
+ type = cpp_tree.get(@type)
+ cpp_tree.set(self,self)
+ namespace.add_cc("static #{type_name} *#{@name}_ptr")
+ counter = "#{@name}_counter"
+ namespace.add_cc("static int #{counter}")
+
+ str = <<COMMENT_END
+Schwarz counter to construct and destruct the #{@name} object.
+Must be constructed before &#{@name} is constructed so that #{@name}_ptr
+is valid so we can store a reference to the object.
+COMMENT_END
+ str.split(/\n/).each do |str_sec|
+ namespace.class_comment(str_sec)
+ end
+ init_class = namespace.add_class("InitializerFor_" + @name).add_dep(type)
+
+
+ init_class.add_cc_comment(
+ "The counter is initialized at load-time, i.e., before any of the static",
+ "objects are initialized.")
+
+
+ cons = CPP::Constructor.new(init_class)
+ init_class.add_member(:public,cons)
+ cons.suite << if_stmt = CPP::If.new("0 == #{counter}++")
+
+ cons_call = CPP::FuncCall.new("new #{type_name}")
+ cons_call.args.push(@loc.queue_name(@name).inspect)
+
+ cons_call.args.push(@type.hash_with_name(@loc.queue_name(@name).inspect))
+ @type.queues.collect do |queue|
+ cons_call.args.push(@loc.queue_name(@name + "." + queue.name).inspect)
+ end
+ if_stmt.suite << CPP::Assign.new("#{@name}_ptr",cons_call)
+
+
+ destruct = CPP::Destructor.new(init_class)
+ destruct.suite << if_stmt = CPP::If.new("0 == --#{counter}")
+ if_stmt.suite << "delete #{@name}_ptr"
+ if_stmt.suite << CPP::Assign.new("#{@name}_ptr","NULL")
+
+ init_class.add_member(:public,destruct)
+ init_class.static = true
+ init_class.dec = @name + "_initializer";
+
+ str = <<COMMENT_END
+Create a reference to the new object in the pointer. Since we have already
+created the initializer
+COMMENT_END
+ str.split(/\n/).map{|str_sec| CPP::Comment.new(str_sec)}.each do |comment|
+ namespace.add(comment)
+ end
+ namespace.add("static UNUSED_VARIABLE #{type_name} &#{@name}" +
+ " = #{@name}_initializer.get()")
+
+ get = init_class.def_func(type_name,"get") #.add_dep(type)
+ get.pre_func_types = "&"
+ get.suite << CPP::Return.new("*#{@name}_ptr")
+ end
+end
diff --git a/aos/build/queues/output/q_struct.rb b/aos/build/queues/output/q_struct.rb
new file mode 100644
index 0000000..f538bb3
--- /dev/null
+++ b/aos/build/queues/output/q_struct.rb
@@ -0,0 +1,156 @@
+class Target::StructDec < Target::StructBase
+ attr_accessor :name,:loc,:parent, :extern
+ def initialize(name)
+ @name = name
+ @members = []
+ end
+ def add_member(member)
+ @members << member
+ end
+ def create_GetType(type_class, cpp_tree)
+ member_func = CPP::MemberFunc.new(type_class,"const ::aos::MessageType*","GetType")
+ type_class.add_member(member_func)
+ member_func.static = true
+ member_func.suite << "static ::aos::Once<const ::aos::MessageType> getter(#{type_class.name}::DoGetType)"
+ member_func.suite << CPP::Return.new("getter.Get()")
+ end
+ def create(cpp_tree)
+ return self if(@extern)
+ orig_namespace = namespace = cpp_tree.get(@loc)
+ name = ""
+ if(namespace.class < Types::Type) #is nested
+ name = namespace.name + "_" + name
+ namespace = namespace.space
+ end
+ type_class = namespace.add_struct(name + @name)
+
+ @members.each do |elem|
+ type_class.add_member(elem.create_usage(cpp_tree))
+ end
+ create_DoGetType(type_class, cpp_tree)
+ create_GetType(type_class, cpp_tree)
+ create_DefaultConstructor(type_class, cpp_tree)
+ create_InOrderConstructor(type_class, cpp_tree)
+ create_Zero(type_class, cpp_tree)
+ create_Size(type_class, cpp_tree)
+ create_Serialize(type_class, cpp_tree)
+ create_Deserialize(type_class, cpp_tree)
+ create_EqualsNoTime(type_class, cpp_tree)
+ return type_class
+ end
+ def getPrintFormat()
+ return "{" + @members.collect { |elem| elem.toPrintFormat() }.join(", ") + "}"
+ end
+ def fetchPrintArgs(args, parent = "")
+ @members.each do |elem|
+ elem.fetchPrintArgs(args, parent)
+ end
+ end
+ def toHost(offset, suite, parent)
+ @members.each do |elem|
+ elem.toHost(offset, suite, parent)
+ offset += " + #{elem.size()}"
+ end
+ end
+ def toNetwork(offset, suite, parent)
+ @members.each do |elem|
+ elem.toNetwork(offset, suite, parent)
+ offset += " + #{elem.size()}"
+ end
+ end
+ def zeroCall(suite, parent)
+ @members.each do |elem|
+ elem.zeroCall(suite, parent)
+ end
+ end
+end
+class Target::MessageStructElement < Target::Node
+ attr_accessor :name,:loc
+ def initialize(type,name)
+ @type, @name = type, name
+ end
+ def type()
+ return @type.get_name
+ end
+ def create_EqualsNoTime(type_class,cpp_tree)
+ member_func = CPP::MemberFunc.new(type_class,"bool","EqualsNoTime")
+ member_func.const = true
+ type_class.add_member(member_func)
+ member_func.args << "const #{type_class.name} &other"
+ member_func.suite << "if (!#{type_class.parent_class}::EqualsNoTime(other)) return false;" if type_class.parent_class
+ @members.each do |elem|
+ if elem.respond_to? :create_EqualsNoTime
+ member_func.suite << "if (!other.#{elem.name}.EqualsNoTime(#{elem.name})) return false;"
+ elsif elem.respond_to?(:length) && elem.member_type.respond_to?(:create_EqualsNoTime)
+ 0.upto(elem.length - 1) do |i|
+ member_func.suite << "if (!other.#{elem.name}[#{i}].EqualsNoTime(#{elem.name}[#{i}])) return false;"
+ end
+ else
+ member_func.suite << "if (other.#{elem.name} != #{elem.name}) return false;"
+ end
+ end
+ member_func.suite << CPP::Return.new('true')
+ end
+ def type_name(cpp_tree)
+ type = cpp_tree.get(@type)
+ if(@type.loc == @loc) #use relative name
+ return type.name
+ else #use full name
+ return @type.loc.to_cpp_id(type.name)
+ end
+ end
+ def size()
+ return @type.size()
+ end
+ def toPrintFormat()
+ @type.getPrintFormat()
+ end
+ def create_usage(cpp_tree, this_name=nil)
+ if (this_name == nil)
+ this_name = @name
+ end
+ return "#{type_name(cpp_tree)} #{this_name}"
+ end
+ def add_TypeRegister(cpp_tree, o_type, member_func)
+ type = cpp_tree.get(@type)
+ tName = @type.loc.to_cpp_id(type.name)
+ member_func.suite << "#{tName}::GetType()"
+ end
+ def fetchPrintArgs(args, parent = "", this_name=nil)
+ if (this_name == nil)
+ this_name = @name
+ end
+ @type.fetchPrintArgs(args, parent + "#{this_name}.")
+ end
+ def toNetwork(offset,suite, parent = "", this_name=nil)
+ if (this_name == nil)
+ this_name = @name
+ end
+ @type.toNetwork(offset, suite, parent + "#{this_name}.")
+ end
+ def toHost(offset,suite, parent = "", this_name=nil)
+ if (this_name == nil)
+ this_name = @name
+ end
+ @type.toHost(offset, suite, parent + "#{this_name}.")
+ end
+ def set_message_builder(suite, this_name=nil)
+ if (this_name == nil)
+ this_name = @name
+ end
+ suite << "msg_ptr_->#{this_name} = #{this_name}"
+ end
+
+ def zeroCall(suite, parent = "", this_name=nil)
+ if (this_name == nil)
+ this_name = @name
+ end
+ @type.zeroCall(suite, parent + "#{this_name}.")
+ end
+ def simpleStr()
+ "#{@type.simpleStr()} #{@name}"
+ end
+ def getTypeID()
+ return @type.getTypeID()
+ end
+end
diff --git a/aos/build/queues/output/queue_dec.rb b/aos/build/queues/output/queue_dec.rb
new file mode 100644
index 0000000..031c033
--- /dev/null
+++ b/aos/build/queues/output/queue_dec.rb
@@ -0,0 +1,89 @@
+class Target::QueueDec < Target::Node
+ attr_accessor :name,:loc
+ def initialize(type,name)
+ @type,@name = type,name
+ end
+ def msg_hash()
+ return @type.msg_hash
+ end
+ def full_message_name(cpp_tree)
+ type = cpp_tree.get(@type)
+ return @type.loc.to_cpp_id(type.name)
+ end
+ def full_builder_name(cpp_tree)
+ return "::aos::MessageBuilder< #{full_message_name(cpp_tree)}>"
+ end
+ def full_type_name(cpp_tree)
+ return "::aos::Queue< #{full_message_name(cpp_tree)}>"
+ end
+ def type_name(cpp_tree,queue_type = "::aos::Queue")
+ type = cpp_tree.get(@type)
+ if(@type.loc == @loc) #use relative name
+ return "#{queue_type}<#{type.name}>"
+ else #use full name
+ return "#{queue_type}< #{@type.loc.to_cpp_id(type.name)}>"
+ end
+ end
+ def create_usage(cpp_tree)
+ "#{type_name(cpp_tree)} #{@name}"
+ end
+ def create(cpp_tree)
+ namespace = cpp_tree.get(@loc)
+ type = cpp_tree.get(@type)
+ cpp_tree.set(self,self)
+ type_name = type_name(cpp_tree)
+ full_type_name = full_type_name(cpp_tree)
+ namespace.add_cc("static #{type_name} *#{@name}_ptr")
+ counter = "#{@name}_counter"
+ namespace.add_cc("static int #{counter}")
+
+ str = <<COMMENT_END
+Schwarz counter to construct and destruct the #{@name} queue.
+Must be constructed before &#{@name} is constructed so that #{@name}_ptr
+is valid so we can store a reference to the object.
+COMMENT_END
+ str.split(/\n/).each do |str_sec|
+ namespace.class_comment(str_sec)
+ end
+ init_class = namespace.add_class("InitializerFor_" + @name)
+
+ init_class.add_cc_comment(
+ "The counter is initialized at load-time, i.e., before any of the static",
+ "objects are initialized.")
+
+
+ cons = CPP::Constructor.new(init_class)
+ init_class.add_member(:public,cons)
+ cons.suite << if_stmt = CPP::If.new("0 == #{counter}++")
+
+ cons_call = CPP::FuncCall.new("new #{type_name}")
+ cons_call.args.push(@loc.queue_name(@name).inspect)
+ if_stmt.suite << CPP::Assign.new("#{@name}_ptr",cons_call)
+
+
+ destruct = CPP::Destructor.new(init_class)
+ init_class.add_member(:public,destruct)
+ destruct.suite << if_stmt = CPP::If.new("0 == --#{counter}")
+ if_stmt.suite << "delete #{@name}_ptr"
+ if_stmt.suite << CPP::Assign.new("#{@name}_ptr","NULL")
+
+ init_class.static = true
+ init_class.dec = @name + "_initializer";
+
+ str = <<COMMENT_END
+Create a reference to the new object in the pointer. Since we have already
+created the initializer
+COMMENT_END
+ str.split(/\n/).map{|str_sec| CPP::Comment.new(str_sec)}.each do |comment|
+ namespace.add(comment)
+ end
+ namespace.add("static UNUSED_VARIABLE #{type_name} &#{@name}" +
+ " = #{@name}_initializer.get()")
+
+ get = init_class.def_func(full_type_name,"get")
+ get.pre_func_types = "&"
+ get.suite << CPP::Return.new("*#{@name}_ptr")
+
+ end
+end
+
diff --git a/aos/build/queues/print_field.rb b/aos/build/queues/print_field.rb
new file mode 100644
index 0000000..75cc1da
--- /dev/null
+++ b/aos/build/queues/print_field.rb
@@ -0,0 +1,91 @@
+require File.dirname(__FILE__) + '/load.rb'
+
+# TODO(brians): Special-case Time too and float/double if we can find a good way to do it.
+GenericTypeNames = ['float', 'double', 'char', '::aos::time::Time']
+IntegerSizes = [8, 16, 32, 64]
+
+WriteIffChanged.open(ARGV[0]) do |output|
+ output.puts <<END
+// This file is generated by #{File.expand_path(__FILE__)}.
+// DO NOT EDIT BY HAND!
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <stdio.h>
+
+#include "aos/common/byteorder.h"
+#include "aos/common/time.h"
+#include "aos/common/print_field_helpers.h"
+#include "aos/common/logging/logging_printf_formats.h"
+
+namespace aos {
+
+bool PrintField(char *output, size_t *output_bytes, const void *input,
+ size_t *input_bytes, uint32_t type) {
+ switch (type) {
+#{GenericTypeNames.collect do |name|
+ message_element = Target::MessageElement.new(name, 'value')
+ statement = MessageElementStmt.new(name, 'value')
+ message_element.size = statement.size
+ print_args = []
+ message_element.fetchPrintArgs(print_args)
+ next <<END2
+ case #{message_element.getTypeID()}:
+ {
+ if (*input_bytes < #{statement.size}) return false;
+ *input_bytes -= #{statement.size};
+ #{name} value;
+ to_host(static_cast<const char *>(input), &value);
+ int ret = snprintf(output, *output_bytes,
+ "#{statement.toPrintFormat()}",
+ #{print_args[0]});
+ if (ret < 0) return false;
+ if (static_cast<unsigned int>(ret) >= *output_bytes) return false;
+ *output_bytes -= ret;
+ return true;
+ }
+END2
+end.join('')}
+#{IntegerSizes.collect do |size|
+ [true, false].collect do |signed|
+ size_bytes = size / 8
+ name = "#{signed ? '' : 'u'}int#{size}_t"
+ message_element = Target::MessageElement.new(name, 'value')
+ message_element.size = size_bytes
+ next <<END2
+ case #{message_element.getTypeID()}:
+ {
+ if (*input_bytes < #{size_bytes}) return false;
+ *input_bytes -= #{size_bytes};
+ #{name} value;
+ to_host(static_cast<const char *>(input), &value);
+ return PrintInteger<#{name}>(output, value, output_bytes);
+ }
+END2
+ end
+end.flatten.join('')}
+#{
+ message_element = Target::MessageElement.new('bool', 'value')
+ message_element.size = 1
+ r = <<END2
+ case #{message_element.getTypeID()}:
+ {
+ if (*input_bytes < 1) return false;
+ if (*output_bytes < 1) return false;
+ *input_bytes -= 1;
+ bool value = static_cast<const char *>(input)[0];
+ *output_bytes -= 1;
+ *output = value ? 'T' : 'f';
+ return true;
+ }
+END2
+}
+ default:
+ return false;
+ }
+}
+
+} // namespace aos
+END
+end
diff --git a/aos/build/queues/q_specs/nested_structs.q b/aos/build/queues/q_specs/nested_structs.q
new file mode 100644
index 0000000..7162620
--- /dev/null
+++ b/aos/build/queues/q_specs/nested_structs.q
@@ -0,0 +1,12 @@
+package aos.test;
+
+import "q_specs/struct_test.q";
+
+
+message Position {
+ int32_t a;
+ int32_t[2] b;
+ .aos.test2.ClawHalfPosition top;
+ .aos.test2.ClawHalfPosition bottom;
+ bool c;
+};
diff --git a/aos/build/queues/q_specs/struct_queue_group.q b/aos/build/queues/q_specs/struct_queue_group.q
new file mode 100644
index 0000000..2bc5046
--- /dev/null
+++ b/aos/build/queues/q_specs/struct_queue_group.q
@@ -0,0 +1,20 @@
+package aos.test;
+
+struct Claw {
+ int32_t a;
+ int32_t b;
+};
+
+ message HPosition {
+ Claw top;
+ };
+
+// All angles here are 0 horizontal, positive up.
+queue_group ClawGroup {
+
+ message Position {
+ Claw top;
+ };
+ queue Position kjks;
+};
+
diff --git a/aos/build/queues/q_specs/struct_test.q b/aos/build/queues/q_specs/struct_test.q
new file mode 100644
index 0000000..afb22ac
--- /dev/null
+++ b/aos/build/queues/q_specs/struct_test.q
@@ -0,0 +1,12 @@
+package aos.test2;
+
+struct SubStruct {
+ int32_t g;
+};
+
+struct ClawHalfPosition {
+ double position;
+ bool hall_effect;
+ SubStruct clampy;
+};
+
diff --git a/aos/build/queues/queue_primitives.rb b/aos/build/queues/queue_primitives.rb
new file mode 100644
index 0000000..421c3b4
--- /dev/null
+++ b/aos/build/queues/queue_primitives.rb
@@ -0,0 +1,54 @@
+require File.dirname(__FILE__) + '/load.rb'
+
+require 'fileutils'
+
+TypeNames = [8, 16, 32, 64].collect do |size|
+ ["uint#{size}_t", "int#{size}_t"]
+end.flatten + ['bool', 'float', 'char', 'double', '::aos::time::Time']
+
+FileUtils.mkdir_p(File.dirname(ARGV[0]))
+WriteIffChanged.open(ARGV[0]) do |output|
+ output.puts <<END
+// This file is generated by #{File.expand_path(__FILE__)}.
+// DO NOT EDIT BY HAND!
+
+#include <stdint.h>
+
+#include "aos/common/time.h"
+
+namespace aos {
+namespace queue_primitive_types {
+#{TypeNames.collect do |name|
+ message_element = Target::MessageElement.new(name, 'value')
+ statement = MessageElementStmt.new(name, 'value')
+ message_element.size = statement.size
+ name = 'Time' if name == '::aos::time::Time'
+ next <<END2
+ static const uint32_t #{name}_p = #{message_element.getTypeID()};
+END2
+end.join('')}
+} // namespace queue_primitive_types
+
+// A class for mapping an actual type to a type ID.
+// There are specializations for all of the actual primitive types.
+template<typename T>
+class TypeID {
+ public:
+ static const uint32_t id;
+};
+
+#{TypeNames.collect do |name|
+ message_element = Target::MessageElement.new(name, 'value')
+ statement = MessageElementStmt.new(name, 'value')
+ message_element.size = statement.size
+ next <<END2
+template<>
+class TypeID< #{name}> {
+ public:
+ static const uint32_t id = #{message_element.getTypeID()};
+};
+END2
+end.join('')}
+} // namespace aos
+END
+end
diff --git a/aos/build/queues/write_iff_changed.rb b/aos/build/queues/write_iff_changed.rb
new file mode 100644
index 0000000..74373a9
--- /dev/null
+++ b/aos/build/queues/write_iff_changed.rb
@@ -0,0 +1,30 @@
+require 'tempfile'
+
+# Writes a file like normal except doesn't do anything if the new contents are
+# the same as what's already there. Useful for helping build tools not rebuild
+# if generated files are re-generated but don't change.
+# Usable as a standard File, including to redirect to with IO.popen etc.
+class WriteIffChanged < Tempfile
+ def initialize(filename)
+ super('write_iff_changed')
+ @filename = filename
+ end
+ def WriteIffChanged.open(filename)
+ out = WriteIffChanged.new(filename)
+ yield out
+ out.close
+ end
+ def close
+ flush
+ contents = File.open(path, 'r') do |f|
+ f.readlines(nil)
+ end
+ if !File.exists?(@filename) ||
+ File.new(@filename, 'r').readlines(nil) != contents
+ File.open(@filename, 'w') do |out|
+ out.write(contents[0])
+ end
+ end
+ super
+ end
+end
diff --git a/aos/build/strip_debuglink b/aos/build/strip_debuglink
new file mode 100755
index 0000000..fce95e6
--- /dev/null
+++ b/aos/build/strip_debuglink
@@ -0,0 +1,18 @@
+#!/bin/dash
+
+set -e
+
+OBJCOPY=$1
+shift
+
+# The arguments after any -o flags.
+OUTPUT=`echo "$@" | awk \
+ 'BEGIN { RS=" " }; output { print ; output = 0 }; /-o/ { output = 1 }'`
+DEBUG_DIR=$(dirname $(dirname ${OUTPUT}))/unstripped_files
+mkdir -p ${DEBUG_DIR}
+DEBUG_FILE=${DEBUG_DIR}/$(basename ${OUTPUT}).full
+
+"$@"
+
+cp ${OUTPUT} ${DEBUG_FILE}
+${OBJCOPY} --strip-debug --add-gnu-debuglink=${DEBUG_FILE} ${OUTPUT}
diff --git a/aos/build/tools_config b/aos/build/tools_config
new file mode 100644
index 0000000..88931af
--- /dev/null
+++ b/aos/build/tools_config
@@ -0,0 +1,15 @@
+# This is a shell fragment that sets up variables related to where the tools
+# that download_externals.sh downloads so build.sh can use.
+# It also gets parsed by build.py, so it has to stay just variable assignments
+# and comments.
+
+EXTERNALS=${AOS}/../output/downloaded
+
+NINJA_RELEASE=v1.4.0
+NINJA_DIR=${EXTERNALS}/ninja-${NINJA_RELEASE}
+NINJA=${NINJA_DIR}/ninja
+
+# From chromium@154360:trunk/src/DEPS.
+GYP_REVISION=1738
+GYP_DIR=${EXTERNALS}/gyp-${GYP_REVISION}
+GYP=${GYP_DIR}/gyp
diff --git a/aos/common/actions/action_test.cc b/aos/common/actions/action_test.cc
new file mode 100644
index 0000000..1e94b32
--- /dev/null
+++ b/aos/common/actions/action_test.cc
@@ -0,0 +1,482 @@
+#include <unistd.h>
+
+#include <memory>
+#include <thread>
+
+#include "gtest/gtest.h"
+#include "aos/common/queue.h"
+#include "aos/common/queue_testutils.h"
+#include "aos/common/actions/actor.h"
+#include "aos/common/actions/actions.h"
+#include "aos/common/actions/actions.q.h"
+#include "aos/common/actions/test_action.q.h"
+#include "aos/common/event.h"
+
+using ::aos::time::Time;
+
+namespace aos {
+namespace common {
+namespace actions {
+namespace testing {
+
+class TestActorIndex
+ : public aos::common::actions::ActorBase<actions::TestActionQueueGroup> {
+ public:
+ explicit TestActorIndex(actions::TestActionQueueGroup *s)
+ : aos::common::actions::ActorBase<actions::TestActionQueueGroup>(s) {}
+
+ bool RunAction(const uint32_t &new_index) override {
+ index = new_index;
+ return true;
+ }
+
+ uint32_t index = 0;
+};
+
+::std::unique_ptr<
+ aos::common::actions::TypedAction<actions::TestActionQueueGroup>>
+MakeTestActionIndex(uint32_t index) {
+ return ::std::unique_ptr<
+ aos::common::actions::TypedAction<actions::TestActionQueueGroup>>(
+ new aos::common::actions::TypedAction<actions::TestActionQueueGroup>(
+ &actions::test_action, index));
+}
+
+class TestActorNOP
+ : public aos::common::actions::ActorBase<actions::TestActionQueueGroup> {
+ public:
+ explicit TestActorNOP(actions::TestActionQueueGroup *s)
+ : actions::ActorBase<actions::TestActionQueueGroup>(s) {}
+
+ bool RunAction(const uint32_t &) override { return true; }
+};
+
+::std::unique_ptr<
+ aos::common::actions::TypedAction<actions::TestActionQueueGroup>>
+MakeTestActionNOP() {
+ return MakeTestActionIndex(0);
+}
+
+class TestActorShouldCancel
+ : public aos::common::actions::ActorBase<actions::TestActionQueueGroup> {
+ public:
+ explicit TestActorShouldCancel(actions::TestActionQueueGroup *s)
+ : aos::common::actions::ActorBase<actions::TestActionQueueGroup>(s) {}
+
+ bool RunAction(const uint32_t &) override {
+ while (!ShouldCancel()) {
+ LOG(FATAL, "NOT CANCELED!!\n");
+ }
+ return true;
+ }
+};
+
+::std::unique_ptr<
+ aos::common::actions::TypedAction<actions::TestActionQueueGroup>>
+MakeTestActionShouldCancel() {
+ return MakeTestActionIndex(0);
+}
+
+class TestActor2Nop
+ : public aos::common::actions::ActorBase<actions::TestAction2QueueGroup> {
+ public:
+ explicit TestActor2Nop(actions::TestAction2QueueGroup *s)
+ : actions::ActorBase<actions::TestAction2QueueGroup>(s) {}
+
+ bool RunAction(const actions::MyParams &) { return true; }
+};
+
+::std::unique_ptr<
+ aos::common::actions::TypedAction<actions::TestAction2QueueGroup>>
+MakeTestAction2NOP(const actions::MyParams ¶ms) {
+ return ::std::unique_ptr<
+ aos::common::actions::TypedAction<actions::TestAction2QueueGroup>>(
+ new aos::common::actions::TypedAction<actions::TestAction2QueueGroup>(
+ &actions::test_action2, params));
+}
+
+class ActionTest : public ::testing::Test {
+ protected:
+ ActionTest() {
+ // Flush the robot state queue so we can use clean shared memory for this.
+ // test.
+ actions::test_action.goal.Clear();
+ actions::test_action.status.Clear();
+ actions::test_action2.goal.Clear();
+ actions::test_action2.status.Clear();
+ }
+
+ virtual ~ActionTest() {
+ actions::test_action.goal.Clear();
+ actions::test_action.status.Clear();
+ actions::test_action2.goal.Clear();
+ actions::test_action2.status.Clear();
+ }
+
+ // Bring up and down Core.
+ ::aos::common::testing::GlobalCoreInstance my_core;
+ ::aos::common::actions::ActionQueue action_queue_;
+};
+
+// Tests that the the actions exist in a safe state at startup.
+TEST_F(ActionTest, DoesNothing) {
+ // Tick an empty queue and make sure it was not running.
+ EXPECT_FALSE(action_queue_.Running());
+ action_queue_.Tick();
+ EXPECT_FALSE(action_queue_.Running());
+}
+
+// Tests that starting with an old run message in the goal queue actually works.
+// This used to result in the client hanging, waiting for a response to its
+// cancel message.
+TEST_F(ActionTest, StartWithOldGoal) {
+ ASSERT_TRUE(actions::test_action.goal.MakeWithBuilder().run(971).Send());
+
+ TestActorNOP nop_act(&actions::test_action);
+
+ ASSERT_FALSE(actions::test_action.status.FetchLatest());
+ ::std::thread init_thread([&nop_act]() { nop_act.Initialize(); });
+ ::aos::time::SleepFor(::aos::time::Time::InSeconds(0.1));
+ ASSERT_TRUE(actions::test_action.goal.MakeWithBuilder().run(1).Send());
+ init_thread.join();
+ ASSERT_TRUE(actions::test_action.status.FetchLatest());
+ EXPECT_EQ(0u, actions::test_action.status->running);
+ EXPECT_EQ(0u, actions::test_action.status->last_running);
+
+ action_queue_.EnqueueAction(MakeTestActionNOP());
+ nop_act.WaitForActionRequest();
+
+ // We started an action and it should be running.
+ EXPECT_TRUE(action_queue_.Running());
+
+ action_queue_.CancelAllActions();
+ action_queue_.Tick();
+
+ EXPECT_TRUE(action_queue_.Running());
+
+ // Run the action so it can signal completion.
+ nop_act.RunIteration();
+ action_queue_.Tick();
+
+ // Make sure it stopped.
+ EXPECT_FALSE(action_queue_.Running());
+}
+
+// Tests that the queues are properly configured for testing. Tests that queues
+// work exactly as used in the tests.
+TEST_F(ActionTest, QueueCheck) {
+ actions::TestActionQueueGroup *send_side = &actions::test_action;
+ actions::TestActionQueueGroup *recv_side = &actions::test_action;
+
+ send_side->goal.MakeWithBuilder().run(1).Send();
+
+ EXPECT_TRUE(recv_side->goal.FetchLatest());
+ EXPECT_TRUE(recv_side->goal->run);
+
+ send_side->goal.MakeWithBuilder().run(0).Send();
+
+ EXPECT_TRUE(recv_side->goal.FetchLatest());
+ EXPECT_FALSE(recv_side->goal->run);
+
+ send_side->status.MakeWithBuilder().running(5).last_running(6).Send();
+
+ EXPECT_TRUE(recv_side->status.FetchLatest());
+ EXPECT_EQ(5, static_cast<int>(recv_side->status->running));
+ EXPECT_EQ(6, static_cast<int>(recv_side->status->last_running));
+}
+
+// Tests that an action starts and stops.
+TEST_F(ActionTest, ActionQueueWasRunning) {
+ TestActorNOP nop_act(&actions::test_action);
+
+ // Tick an empty queue and make sure it was not running.
+ action_queue_.Tick();
+ EXPECT_FALSE(action_queue_.Running());
+
+ action_queue_.EnqueueAction(MakeTestActionNOP());
+ nop_act.WaitForActionRequest();
+
+ // We started an action and it should be running.
+ EXPECT_TRUE(action_queue_.Running());
+
+ // Tick it and make sure it is still running.
+ action_queue_.Tick();
+ EXPECT_TRUE(action_queue_.Running());
+
+ // Run the action so it can signal completion.
+ nop_act.RunIteration();
+ action_queue_.Tick();
+
+ // Make sure it stopped.
+ EXPECT_FALSE(action_queue_.Running());
+}
+
+// Tests that we can cancel two actions and have them both stop.
+TEST_F(ActionTest, ActionQueueCancelAll) {
+ TestActorNOP nop_act(&actions::test_action);
+
+ // Tick an empty queue and make sure it was not running.
+ action_queue_.Tick();
+ EXPECT_FALSE(action_queue_.Running());
+
+ // Enqueue two actions to test both cancel. We can have an action and a next
+ // action so we want to test that.
+ action_queue_.EnqueueAction(MakeTestActionNOP());
+ action_queue_.EnqueueAction(MakeTestActionNOP());
+ nop_act.WaitForActionRequest();
+ action_queue_.Tick();
+
+ // Check that current and next exist.
+ EXPECT_TRUE(action_queue_.GetCurrentActionState(nullptr, nullptr, nullptr,
+ nullptr, nullptr, nullptr));
+ EXPECT_TRUE(action_queue_.GetNextActionState(nullptr, nullptr, nullptr,
+ nullptr, nullptr, nullptr));
+
+ action_queue_.CancelAllActions();
+ action_queue_.Tick();
+
+ // It should still be running as the actor could not have signaled.
+ EXPECT_TRUE(action_queue_.Running());
+
+ bool sent_started, sent_cancel, interrupted;
+ EXPECT_TRUE(action_queue_.GetCurrentActionState(
+ nullptr, &sent_started, &sent_cancel, &interrupted, nullptr, nullptr));
+ EXPECT_TRUE(sent_started);
+ EXPECT_TRUE(sent_cancel);
+ EXPECT_FALSE(interrupted);
+
+ EXPECT_FALSE(action_queue_.GetNextActionState(nullptr, nullptr, nullptr,
+ nullptr, nullptr, nullptr));
+
+ // Run the action so it can signal completion.
+ nop_act.RunIteration();
+ action_queue_.Tick();
+
+ // Make sure it stopped.
+ EXPECT_FALSE(action_queue_.Running());
+}
+
+// Tests that an action that would block forever stops when canceled.
+TEST_F(ActionTest, ActionQueueCancelOne) {
+ TestActorShouldCancel cancel_act(&actions::test_action);
+
+ // Enqueue blocking action.
+ action_queue_.EnqueueAction(MakeTestActionShouldCancel());
+
+ cancel_act.WaitForActionRequest();
+ action_queue_.Tick();
+ EXPECT_TRUE(action_queue_.Running());
+
+ // Tell action to cancel.
+ action_queue_.CancelCurrentAction();
+ action_queue_.Tick();
+
+ // This will block forever on failure.
+ // TODO(ben): prolly a bad way to fail
+ cancel_act.RunIteration();
+ action_queue_.Tick();
+
+ // It should still be running as the actor could not have signalled.
+ EXPECT_FALSE(action_queue_.Running());
+}
+
+// Tests that an action starts and stops.
+TEST_F(ActionTest, ActionQueueTwoActions) {
+ TestActorNOP nop_act(&actions::test_action);
+
+ // Tick an empty queue and make sure it was not running.
+ action_queue_.Tick();
+ EXPECT_FALSE(action_queue_.Running());
+
+ // Enqueue action to be canceled.
+ action_queue_.EnqueueAction(MakeTestActionNOP());
+ nop_act.WaitForActionRequest();
+ action_queue_.Tick();
+
+ // Should still be running as the actor could not have signalled.
+ EXPECT_TRUE(action_queue_.Running());
+
+ // id for the first time run.
+ uint32_t nop_act_id = 0;
+ // Check the internal state and write down id for later use.
+ bool sent_started, sent_cancel, interrupted;
+ EXPECT_TRUE(action_queue_.GetCurrentActionState(nullptr, &sent_started,
+ &sent_cancel, &interrupted,
+ &nop_act_id, nullptr));
+ EXPECT_TRUE(sent_started);
+ EXPECT_FALSE(sent_cancel);
+ EXPECT_FALSE(interrupted);
+ ASSERT_NE(0u, nop_act_id);
+
+ // Add the next action which should ensure the first stopped.
+ action_queue_.EnqueueAction(MakeTestActionNOP());
+
+ // id for the second run.
+ uint32_t nop_act2_id = 0;
+ // Check the internal state and write down id for later use.
+ EXPECT_TRUE(action_queue_.GetNextActionState(nullptr, &sent_started,
+ &sent_cancel, &interrupted,
+ &nop_act2_id, nullptr));
+ EXPECT_NE(nop_act_id, nop_act2_id);
+ EXPECT_FALSE(sent_started);
+ EXPECT_FALSE(sent_cancel);
+ EXPECT_FALSE(interrupted);
+ ASSERT_NE(0u, nop_act2_id);
+
+ action_queue_.Tick();
+
+ // Run the action so it can signal completion.
+ nop_act.RunIteration();
+ action_queue_.Tick();
+ // Wait for the first id to finish, needed for the correct number of fetches.
+ nop_act.WaitForStop(nop_act_id);
+
+ // Start the next action on the actor side.
+ nop_act.WaitForActionRequest();
+
+ // Check the new action is the right one.
+ uint32_t test_id = 0;
+ EXPECT_TRUE(action_queue_.GetCurrentActionState(
+ nullptr, &sent_started, &sent_cancel, &interrupted, &test_id, nullptr));
+ EXPECT_TRUE(sent_started);
+ EXPECT_FALSE(sent_cancel);
+ EXPECT_FALSE(interrupted);
+ EXPECT_EQ(nop_act2_id, test_id);
+
+ // Make sure it is still going.
+ EXPECT_TRUE(action_queue_.Running());
+
+ // Run the next action so it can accomplish signal completion.
+ nop_act.RunIteration();
+ action_queue_.Tick();
+ nop_act.WaitForStop(nop_act_id);
+
+ // Make sure it stopped.
+ EXPECT_FALSE(action_queue_.Running());
+}
+
+// Tests that we do get an index with our goal
+TEST_F(ActionTest, ActionIndex) {
+ TestActorIndex idx_act(&actions::test_action);
+
+ // Tick an empty queue and make sure it was not running.
+ action_queue_.Tick();
+ EXPECT_FALSE(action_queue_.Running());
+
+ // Enqueue action to post index.
+ action_queue_.EnqueueAction(MakeTestActionIndex(5));
+ EXPECT_TRUE(actions::test_action.goal.FetchLatest());
+ EXPECT_EQ(5u, actions::test_action.goal->params);
+ EXPECT_EQ(0u, idx_act.index);
+
+ idx_act.WaitForActionRequest();
+ action_queue_.Tick();
+
+ // Check the new action is the right one.
+ uint32_t test_id = 0;
+ EXPECT_TRUE(action_queue_.GetCurrentActionState(nullptr, nullptr, nullptr,
+ nullptr, &test_id, nullptr));
+
+ // Run the next action so it can accomplish signal completion.
+ idx_act.RunIteration();
+ action_queue_.Tick();
+ idx_act.WaitForStop(test_id);
+ EXPECT_EQ(5u, idx_act.index);
+
+ // Enqueue action to post index.
+ action_queue_.EnqueueAction(MakeTestActionIndex(3));
+ EXPECT_TRUE(actions::test_action.goal.FetchLatest());
+ EXPECT_EQ(3u, actions::test_action.goal->params);
+
+ // Run the next action so it can accomplish signal completion.
+ idx_act.RunIteration();
+ action_queue_.Tick();
+ idx_act.WaitForStop(test_id);
+ EXPECT_EQ(3u, idx_act.index);
+}
+
+// Tests that an action with a structure params works.
+TEST_F(ActionTest, StructParamType) {
+ TestActor2Nop nop_act(&actions::test_action2);
+
+ // Tick an empty queue and make sure it was not running.
+ action_queue_.Tick();
+ EXPECT_FALSE(action_queue_.Running());
+
+ actions::MyParams p;
+ p.param1 = 5.0;
+ p.param2 = 7;
+
+ action_queue_.EnqueueAction(MakeTestAction2NOP(p));
+ nop_act.WaitForActionRequest();
+
+ // We started an action and it should be running.
+ EXPECT_TRUE(action_queue_.Running());
+
+ // Tick it and make sure it is still running.
+ action_queue_.Tick();
+ EXPECT_TRUE(action_queue_.Running());
+
+ // Run the action so it can signal completion.
+ nop_act.RunIteration();
+ action_queue_.Tick();
+
+ // Make sure it stopped.
+ EXPECT_FALSE(action_queue_.Running());
+}
+
+// Tests that cancelling an action before the message confirming it started is
+// received works.
+// Situations like this used to lock the action queue up waiting for an action
+// to report that it successfully cancelled.
+// This situation is kind of a race condition, but it happens very consistently
+// when hitting buttons while the robot is in teleop-disabled. To hit the race
+// condition consistently in the test, there are a couple of Events inserted in
+// between various things running.
+TEST_F(ActionTest, CancelBeforeStart) {
+ Event thread_ready, ready_to_start, ready_to_stop;
+ ::std::thread action_thread(
+ [this, &thread_ready, &ready_to_start, &ready_to_stop]() {
+ TestActorNOP nop_act(&actions::test_action);
+ nop_act.Initialize();
+ thread_ready.Set();
+ ready_to_start.Wait();
+ nop_act.WaitForActionRequest();
+ LOG(DEBUG, "got a request to run\n");
+ const uint32_t running_id = nop_act.RunIteration();
+ LOG(DEBUG, "waiting for %" PRIx32 " to be stopped\n", running_id);
+ ready_to_stop.Set();
+ nop_act.WaitForStop(running_id);
+ });
+
+ action_queue_.CancelAllActions();
+ EXPECT_FALSE(action_queue_.Running());
+ thread_ready.Wait();
+ LOG(DEBUG, "starting action\n");
+ action_queue_.EnqueueAction(MakeTestActionNOP());
+ action_queue_.Tick();
+ action_queue_.CancelAllActions();
+ ready_to_start.Set();
+ LOG(DEBUG, "started action\n");
+ EXPECT_TRUE(action_queue_.Running());
+ ready_to_stop.Wait();
+ EXPECT_TRUE(action_queue_.Running());
+ LOG(DEBUG, "action is ready to stop\n");
+
+ action_queue_.Tick();
+ action_queue_.CancelAllActions();
+ EXPECT_FALSE(action_queue_.Running());
+ action_queue_.Tick();
+ action_queue_.CancelAllActions();
+ ASSERT_FALSE(action_queue_.Running());
+ action_thread.join();
+
+ action_queue_.Tick();
+ action_queue_.CancelAllActions();
+ ASSERT_FALSE(action_queue_.Running());
+}
+
+} // namespace testing
+} // namespace actions
+} // namespace common
+} // namespace aos
diff --git a/aos/common/actions/actions.cc b/aos/common/actions/actions.cc
new file mode 100644
index 0000000..ff3f386
--- /dev/null
+++ b/aos/common/actions/actions.cc
@@ -0,0 +1,75 @@
+#include "aos/common/actions/actions.h"
+
+namespace aos {
+namespace common {
+namespace actions {
+
+void ActionQueue::EnqueueAction(::std::unique_ptr<Action> action) {
+ if (current_action_) {
+ LOG(INFO, "Queueing action, canceling prior\n");
+ current_action_->Cancel();
+ next_action_ = ::std::move(action);
+ } else {
+ LOG(INFO, "Queueing action\n");
+ current_action_ = ::std::move(action);
+ current_action_->Start();
+ }
+}
+
+void ActionQueue::CancelCurrentAction() {
+ LOG(INFO, "Canceling current action\n");
+ if (current_action_) {
+ current_action_->Cancel();
+ }
+}
+
+void ActionQueue::CancelAllActions() {
+ LOG(DEBUG, "Cancelling all actions\n");
+ if (current_action_) {
+ current_action_->Cancel();
+ }
+ next_action_.reset();
+}
+
+void ActionQueue::Tick() {
+ if (current_action_) {
+ if (!current_action_->Running()) {
+ LOG(INFO, "Action is done.\n");
+ current_action_ = ::std::move(next_action_);
+ if (current_action_) {
+ LOG(INFO, "Running next action\n");
+ current_action_->Start();
+ }
+ }
+ }
+}
+
+bool ActionQueue::Running() { return static_cast<bool>(current_action_); }
+
+bool ActionQueue::GetCurrentActionState(bool* has_started, bool* sent_started,
+ bool* sent_cancel, bool* interrupted,
+ uint32_t* run_value,
+ uint32_t* old_run_value) {
+ if (current_action_) {
+ current_action_->GetState(has_started, sent_started, sent_cancel,
+ interrupted, run_value, old_run_value);
+ return true;
+ }
+ return false;
+}
+
+bool ActionQueue::GetNextActionState(bool* has_started, bool* sent_started,
+ bool* sent_cancel, bool* interrupted,
+ uint32_t* run_value,
+ uint32_t* old_run_value) {
+ if (next_action_) {
+ next_action_->GetState(has_started, sent_started, sent_cancel, interrupted,
+ run_value, old_run_value);
+ return true;
+ }
+ return false;
+}
+
+} // namespace actions
+} // namespace common
+} // namespace aos
diff --git a/aos/common/actions/actions.gyp b/aos/common/actions/actions.gyp
new file mode 100644
index 0000000..69c9c7a
--- /dev/null
+++ b/aos/common/actions/actions.gyp
@@ -0,0 +1,71 @@
+{
+ 'targets': [
+ {
+ 'target_name': 'action_lib',
+ 'type': 'static_library',
+ 'sources': [
+ 'actions.cc',
+ 'actor.cc',
+ ],
+ 'dependencies': [
+ '<(AOS)/build/aos.gyp:logging',
+ '<(AOS)/common/common.gyp:queues',
+ '<(AOS)/common/logging/logging.gyp:queue_logging',
+ '<(AOS)/common/common.gyp:time',
+ '<(AOS)/common/controls/controls.gyp:control_loop',
+ '<(AOS)/common/util/util.gyp:phased_loop',
+ ],
+ 'export_dependent_settings': [
+ '<(AOS)/build/aos.gyp:logging',
+ '<(AOS)/common/common.gyp:queues',
+ '<(AOS)/common/logging/logging.gyp:queue_logging',
+ '<(AOS)/common/common.gyp:time',
+ '<(AOS)/common/controls/controls.gyp:control_loop',
+ '<(AOS)/common/util/util.gyp:phased_loop',
+ ],
+ },
+ {
+ 'target_name': 'action_queue',
+ 'type': 'static_library',
+ 'sources': ['actions.q'],
+ 'variables': {
+ 'header_path': 'aos/common/actions',
+ },
+ 'includes': ['../../build/queues.gypi'],
+ },
+ {
+ 'target_name': 'test_action_queue',
+ 'type': 'static_library',
+ 'sources': ['test_action.q'],
+ 'variables': {
+ 'header_path': 'aos/common/actions',
+ },
+ 'dependencies': [
+ 'action_queue',
+ ],
+ 'export_dependent_settings': [
+ 'action_queue',
+ ],
+ 'includes': ['../../build/queues.gypi'],
+ },
+ {
+ 'target_name': 'action_test',
+ 'type': 'executable',
+ 'sources': [
+ 'action_test.cc',
+ ],
+ 'dependencies': [
+ '<(EXTERNALS):gtest',
+ 'action_lib',
+ 'test_action_queue',
+ '<(AOS)/common/common.gyp:queue_testutils',
+ '<(AOS)/build/aos.gyp:logging',
+ '<(AOS)/common/logging/logging.gyp:queue_logging',
+ '<(AOS)/common/common.gyp:queues',
+ '<(AOS)/common/common.gyp:time',
+ 'action_queue',
+ '<(AOS)/common/common.gyp:event',
+ ],
+ },
+ ],
+}
diff --git a/aos/common/actions/actions.h b/aos/common/actions/actions.h
new file mode 100644
index 0000000..fd88903
--- /dev/null
+++ b/aos/common/actions/actions.h
@@ -0,0 +1,358 @@
+#ifndef AOS_COMMON_ACTIONS_ACTIONS_H_
+#define AOS_COMMON_ACTIONS_ACTIONS_H_
+
+#include <inttypes.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <type_traits>
+#include <atomic>
+#include <memory>
+
+#include "aos/common/logging/logging.h"
+#include "aos/common/queue.h"
+#include "aos/common/logging/queue_logging.h"
+
+namespace aos {
+namespace common {
+namespace actions {
+
+class Action;
+
+// A queue which queues Actions, verifies a single action is running, and
+// cancels Actions.
+class ActionQueue {
+ public:
+ // Queues up an action for sending.
+ void EnqueueAction(::std::unique_ptr<Action> action);
+
+ // Cancels the current action, and runs the next one after the current one has
+ // finished and the action queue ticks again.
+ void CancelCurrentAction();
+
+ // Cancels all running actions.
+ void CancelAllActions();
+
+ // Runs the next action when the current one is finished running and polls the
+ // running action state for use by Running.
+ void Tick();
+
+ // Returns true if any action is running or could be running.
+ bool Running();
+
+ // Retrieves the internal state of the current action for testing.
+ // See comments on the private members of TypedAction<T, S> for details.
+ bool GetCurrentActionState(bool* has_started, bool* sent_started,
+ bool* sent_cancel, bool* interrupted,
+ uint32_t* run_value, uint32_t* old_run_value);
+
+ // Retrieves the internal state of the next action for testing.
+ // See comments on the private members of TypedAction<T, S> for details.
+ bool GetNextActionState(bool* has_started, bool* sent_started,
+ bool* sent_cancel, bool* interrupted,
+ uint32_t* run_value, uint32_t* old_run_value);
+
+ private:
+ ::std::unique_ptr<Action> current_action_;
+ ::std::unique_ptr<Action> next_action_;
+};
+
+// The basic interface an ActionQueue can access.
+class Action {
+ public:
+ virtual ~Action() {}
+
+ // Cancels the action.
+ void Cancel() { DoCancel(); }
+ // Returns true if the action is currently running.
+ bool Running() { return DoRunning(); }
+ // Starts the action.
+ void Start() { DoStart(); }
+
+ // Waits until the action has finished.
+ void WaitUntilDone() { DoWaitUntilDone(); }
+
+ // Run all the checks for one iteration of waiting. Will return true when the
+ // action has completed, successfully or not. This is non-blocking.
+ bool CheckIteration() { return DoCheckIteration(false); }
+
+ // Retrieves the internal state of the action for testing.
+ // See comments on the private members of TypedAction<T, S> for details.
+ void GetState(bool* has_started, bool* sent_started, bool* sent_cancel,
+ bool* interrupted, uint32_t* run_value,
+ uint32_t* old_run_value) {
+ DoGetState(has_started, sent_started, sent_cancel, interrupted, run_value,
+ old_run_value);
+ }
+
+ private:
+ // Cancels the action.
+ virtual void DoCancel() = 0;
+ // Returns true if the action is running or we don't have an initial response
+ // back from it to signal whether or not it is running.
+ virtual bool DoRunning() = 0;
+ // Starts the action if a goal has been created.
+ virtual void DoStart() = 0;
+ // Blocks until complete.
+ virtual void DoWaitUntilDone() = 0;
+ // Updates status for one cycle of waiting
+ virtual bool DoCheckIteration(bool blocking) = 0;
+ // For testing we will need to get the internal state.
+ // See comments on the private members of TypedAction<T, S> for details.
+ virtual void DoGetState(bool* has_started, bool* sent_started,
+ bool* sent_cancel, bool* interrupted,
+ uint32_t* run_value, uint32_t* old_run_value) = 0;
+};
+
+// Templated subclass to hold the type information.
+template <typename T>
+class TypedAction : public Action {
+ public:
+ // A convenient way to refer to the type of our goals.
+ typedef typename std::remove_reference<decltype(
+ *(static_cast<T*>(nullptr)->goal.MakeMessage().get()))>::type GoalType;
+ typedef typename std::remove_reference<
+ decltype(static_cast<GoalType*>(nullptr)->params)>::type ParamType;
+
+ TypedAction(T* queue_group, const ParamType ¶ms)
+ : queue_group_(queue_group),
+ goal_(queue_group_->goal.MakeMessage()),
+ // This adds 1 to the counter (atomically because it's potentially
+ // shared across threads) and then bitwise-ORs the bottom of the PID to
+ // differentiate it from other processes's values (ie a unique id).
+ run_value_(run_counter_.fetch_add(1, ::std::memory_order_relaxed) |
+ ((getpid() & 0xFFFF) << 16)),
+ params_(params) {
+ LOG(INFO, "Action %" PRIx32 " created on queue %s\n", run_value_,
+ queue_group_->goal.name());
+ // Clear out any old status messages from before now.
+ queue_group_->status.FetchLatest();
+ if (queue_group_->status.get()) {
+ LOG_STRUCT(DEBUG, "have status", *queue_group_->status);
+ }
+ }
+
+ virtual ~TypedAction() {
+ LOG(DEBUG, "Calling destructor of %" PRIx32 "\n", run_value_);
+ DoCancel();
+ }
+
+ // Returns the current goal that will be sent when the action is sent.
+ GoalType* GetGoal() { return goal_.get(); }
+
+ private:
+ void DoCancel() override;
+
+ bool DoRunning() override;
+
+ void DoWaitUntilDone() override;
+
+ bool DoCheckIteration(bool blocking);
+
+ // Sets the started flag (also possibly the interrupted flag).
+ void CheckStarted();
+
+ // Checks for interrupt.
+ void CheckInterrupted();
+
+ void DoStart() override;
+
+ void DoGetState(bool* has_started, bool* sent_started, bool* sent_cancel,
+ bool* interrupted, uint32_t* run_value,
+ uint32_t* old_run_value) override {
+ if (has_started != nullptr) *has_started = has_started_;
+ if (sent_started != nullptr) *sent_started = sent_started_;
+ if (sent_cancel != nullptr) *sent_cancel = sent_cancel_;
+ if (interrupted != nullptr) *interrupted = interrupted_;
+ if (run_value != nullptr) *run_value = run_value_;
+ if (old_run_value != nullptr) *old_run_value = old_run_value_;
+ }
+
+ T* const queue_group_;
+ ::aos::ScopedMessagePtr<GoalType> goal_;
+
+ // Track if we have seen a response to the start message.
+ bool has_started_ = false;
+ // Track if we have sent an initial start message.
+ bool sent_started_ = false;
+
+ bool sent_cancel_ = false;
+
+ // Gets set to true if we ever see somebody else's value in running.
+ bool interrupted_ = false;
+
+ // The value we're going to use for goal.run etc.
+ const uint32_t run_value_;
+
+ // flag passed to action in order to have differing types
+ const ParamType params_;
+
+ // The old value for running that we may have seen. If we see any value other
+ // than this or run_value_, somebody else got in the way and we're done. 0 if
+ // there was nothing there to start with. Only valid after sent_started_
+ // changes to true.
+ uint32_t old_run_value_;
+
+ static ::std::atomic<uint16_t> run_counter_;
+};
+
+template <typename T>
+::std::atomic<uint16_t> TypedAction<T>::run_counter_{0};
+
+template <typename T>
+void TypedAction<T>::DoCancel() {
+ if (!sent_started_) {
+ LOG(INFO, "Action %" PRIx32 " on queue %s was never started\n", run_value_,
+ queue_group_->goal.name());
+ } else {
+ if (interrupted_) {
+ LOG(INFO,
+ "Action %" PRIx32 " on queue %s was interrupted -> not cancelling\n",
+ run_value_, queue_group_->goal.name());
+ } else {
+ if (sent_cancel_) {
+ LOG(INFO, "Action %" PRIx32 " on queue %s already cancelled\n",
+ run_value_, queue_group_->goal.name());
+ } else {
+ LOG(INFO, "Canceling action %" PRIx32 " on queue %s\n", run_value_,
+ queue_group_->goal.name());
+ queue_group_->goal.MakeWithBuilder().run(0).Send();
+ sent_cancel_ = true;
+ }
+ }
+ }
+}
+
+template <typename T>
+bool TypedAction<T>::DoRunning() {
+ if (!sent_started_) {
+ LOG(DEBUG, "haven't sent start message yet\n");
+ return false;
+ }
+ if (has_started_) {
+ queue_group_->status.FetchNext();
+ CheckInterrupted();
+ } else {
+ while (queue_group_->status.FetchNext()) {
+ LOG_STRUCT(DEBUG, "got status", *queue_group_->status);
+ CheckStarted();
+ if (has_started_) CheckInterrupted();
+ }
+ }
+ if (interrupted_) return false;
+ // We've asked it to start but haven't gotten confirmation that it's started
+ // yet.
+ if (!has_started_) return true;
+ return queue_group_->status.get() &&
+ queue_group_->status->running == run_value_;
+}
+
+template <typename T>
+void TypedAction<T>::DoWaitUntilDone() {
+ CHECK(sent_started_);
+ queue_group_->status.FetchNext();
+ LOG_STRUCT(DEBUG, "got status", *queue_group_->status);
+ CheckInterrupted();
+ while (true) {
+ if (DoCheckIteration(true)) {
+ return;
+ }
+ }
+}
+
+template <typename T>
+bool TypedAction<T>::DoCheckIteration(bool blocking) {
+ CHECK(sent_started_);
+ if (interrupted_) return true;
+ CheckStarted();
+ if (blocking) {
+ queue_group_->status.FetchNextBlocking();
+ LOG_STRUCT(DEBUG, "got status", *queue_group_->status);
+ } else {
+ if (!queue_group_->status.FetchNext()) {
+ return false;
+ }
+ LOG_STRUCT(DEBUG, "got status", *queue_group_->status);
+ }
+ CheckStarted();
+ CheckInterrupted();
+ if (has_started_ && (queue_group_->status.get() &&
+ queue_group_->status->running != run_value_)) {
+ return true;
+ }
+ return false;
+}
+
+template <typename T>
+void TypedAction<T>::CheckStarted() {
+ if (has_started_) return;
+ if (queue_group_->status.get()) {
+ if (queue_group_->status->running == run_value_ ||
+ (queue_group_->status->running == 0 &&
+ queue_group_->status->last_running == run_value_)) {
+ // It's currently running our instance.
+ has_started_ = true;
+ LOG(INFO, "Action %" PRIx32 " on queue %s has been started\n",
+ run_value_, queue_group_->goal.name());
+ } else if (old_run_value_ != 0 &&
+ queue_group_->status->running == old_run_value_) {
+ LOG(DEBUG, "still running old instance %" PRIx32 "\n", old_run_value_);
+ } else {
+ LOG(WARNING, "Action %" PRIx32 " on queue %s interrupted by %" PRIx32
+ " before starting\n",
+ run_value_, queue_group_->goal.name(), queue_group_->status->running);
+ has_started_ = true;
+ interrupted_ = true;
+ }
+ } else {
+ LOG(WARNING, "No status message recieved.\n");
+ }
+}
+
+template <typename T>
+void TypedAction<T>::CheckInterrupted() {
+ if (!interrupted_ && has_started_ && queue_group_->status.get()) {
+ if (queue_group_->status->running != 0 &&
+ queue_group_->status->running != run_value_) {
+ LOG(WARNING, "Action %" PRIx32 " on queue %s interrupted by %" PRIx32
+ " after starting\n",
+ run_value_, queue_group_->goal.name(), queue_group_->status->running);
+ }
+ }
+}
+
+template <typename T>
+void TypedAction<T>::DoStart() {
+ if (goal_) {
+ LOG(INFO, "Starting action %" PRIx32 "\n", run_value_);
+ goal_->run = run_value_;
+ goal_->params = params_;
+ sent_started_ = true;
+ if (!goal_.Send()) {
+ LOG(ERROR, "sending goal for action %" PRIx32 " on queue %s failed\n",
+ run_value_, queue_group_->goal.name());
+ // Don't wait to see a message with it.
+ has_started_ = true;
+ }
+ queue_group_->status.FetchNext();
+ if (queue_group_->status.get()) {
+ LOG_STRUCT(DEBUG, "got status", *queue_group_->status);
+ }
+ if (queue_group_->status.get() && queue_group_->status->running != 0) {
+ old_run_value_ = queue_group_->status->running;
+ LOG(INFO, "Action %" PRIx32 " on queue %s already running\n",
+ old_run_value_, queue_group_->goal.name());
+ } else {
+ old_run_value_ = 0;
+ }
+ } else {
+ LOG(WARNING, "Action %" PRIx32 " on queue %s already started\n", run_value_,
+ queue_group_->goal.name());
+ }
+}
+
+} // namespace actions
+} // namespace common
+} // namespace aos
+
+#endif // AOS_COMMON_ACTIONS_ACTIONS_H_
diff --git a/aos/common/actions/actions.q b/aos/common/actions/actions.q
new file mode 100644
index 0000000..e3046c9
--- /dev/null
+++ b/aos/common/actions/actions.q
@@ -0,0 +1,35 @@
+package aos.common.actions;
+
+interface StatusInterface {
+ // 0 if the action isn't running or the value from goal.run.
+ uint32_t running;
+};
+
+interface GoalInterface {
+ // 0 to stop or an arbitrary value to put in status.running.
+ uint32_t run;
+};
+
+message Status {
+ // The run value of the instance we're currently running or 0.
+ uint32_t running;
+ // A run value we were previously running or 0.
+ uint32_t last_running;
+ // If false the action failed to complete and may be in a bad state,
+ // this is a critical problem not a cancellation.
+ bool success;
+};
+
+message Goal {
+ // The unique value to put into status.running while running this instance or
+ // 0 to cancel.
+ uint32_t run;
+ // Default parameter. The more useful thing to do would be to define your own
+ // goal type to change param to a useful structure.
+ double params;
+};
+
+interface ActionQueueGroup {
+ queue Status status;
+ queue Goal goal;
+};
diff --git a/aos/common/actions/actor.cc b/aos/common/actions/actor.cc
new file mode 100644
index 0000000..6c6bef7
--- /dev/null
+++ b/aos/common/actions/actor.cc
@@ -0,0 +1,9 @@
+#include "aos/common/actions/actor.h"
+
+namespace aos {
+namespace common {
+namespace actions {
+
+} // namespace actions
+} // namespace common
+} // namespace aos
diff --git a/aos/common/actions/actor.h b/aos/common/actions/actor.h
new file mode 100644
index 0000000..b3a0220
--- /dev/null
+++ b/aos/common/actions/actor.h
@@ -0,0 +1,247 @@
+#ifndef AOS_COMMON_ACTIONS_ACTOR_H_
+#define AOS_COMMON_ACTIONS_ACTOR_H_
+
+#include <stdio.h>
+#include <inttypes.h>
+
+#include <functional>
+
+#include "aos/common/logging/logging.h"
+#include "aos/common/logging/queue_logging.h"
+#include "aos/common/time.h"
+#include "aos/common/controls/control_loop.h"
+#include "aos/common/util/phased_loop.h"
+
+namespace aos {
+namespace common {
+namespace actions {
+
+template <class T>
+class ActorBase {
+ public:
+ typedef typename std::remove_reference<decltype(
+ *(static_cast<T*>(nullptr)->goal.MakeMessage().get()))>::type GoalType;
+ typedef typename std::remove_reference<
+ decltype(static_cast<GoalType*>(nullptr)->params)>::type ParamType;
+
+ ActorBase(T* acq) : action_q_(acq) {}
+
+ // Will return true if finished or asked to cancel.
+ // Will return false if it failed accomplish its goal
+ // due to a problem with the system.
+ virtual bool RunAction(const ParamType& params) = 0;
+
+ // Runs action while enabled.
+ void Run();
+
+ // Gets ready to run actions.
+ void Initialize();
+
+ // Checks if an action was initially running when the thread started.
+ bool CheckInitialRunning();
+
+ // Wait here until someone asks us to go.
+ void WaitForActionRequest();
+
+ // Do work son.
+ uint32_t RunIteration();
+
+ // Wait for stop is signalled.
+ void WaitForStop(uint32_t running_id);
+
+ // Will run until the done condition is met or times out.
+ // Will return false if successful or end_time is reached and true if the
+ // action was canceled or failed.
+ // Done condition are defined as functions that return true when done and have
+ // some sort of blocking statement (such as FetchNextBlocking) to throttle
+ // spin rate.
+ // end_time is when to stop and return true. Time(0, 0) (the default) means
+ // never time out.
+ bool WaitUntil(::std::function<bool(void)> done_condition,
+ const ::aos::time::Time& end_time = ::aos::time::Time(0, 0));
+
+ // Waits for a certain amount of time from when this method is called.
+ // Returns false if the action was canceled or failed, and true if the wait
+ // succeeded.
+ bool WaitOrCancel(const ::aos::time::Time& duration) {
+ return !WaitUntil([]() {
+ ::aos::time::PhasedLoopXMS(
+ ::aos::controls::kLoopFrequency.ToMSec(), 2500);
+ return false;
+ },
+ ::aos::time::Time::Now() + duration);
+ }
+
+ // Returns true if the action should be canceled.
+ bool ShouldCancel();
+
+ protected:
+ // Set to true when we should stop ASAP.
+ bool abort_ = false;
+
+ // The queue for this action.
+ T* action_q_;
+};
+
+template <class T>
+bool ActorBase<T>::CheckInitialRunning() {
+ LOG(DEBUG, "Waiting for input to start\n");
+
+ if (action_q_->goal.FetchLatest()) {
+ LOG_STRUCT(DEBUG, "goal queue", *action_q_->goal);
+ const uint32_t initially_running = action_q_->goal->run;
+ if (initially_running != 0) {
+ while (action_q_->goal->run == initially_running) {
+ LOG(INFO, "run is still %" PRIx32 "\n", initially_running);
+ action_q_->goal.FetchNextBlocking();
+ LOG_STRUCT(DEBUG, "goal queue", *action_q_->goal);
+ }
+ }
+ LOG(DEBUG, "Done waiting, goal\n");
+ return true;
+ }
+ LOG(DEBUG, "Done waiting, no goal\n");
+ return false;
+}
+
+template <class T>
+void ActorBase<T>::WaitForActionRequest() {
+ while (action_q_->goal.get() == nullptr || !action_q_->goal->run) {
+ LOG(INFO, "Waiting for an action request.\n");
+ action_q_->goal.FetchNextBlocking();
+ LOG_STRUCT(DEBUG, "goal queue", *action_q_->goal);
+ if (!action_q_->goal->run) {
+ if (!action_q_->status.MakeWithBuilder()
+ .running(0)
+ .last_running(0)
+ .success(!abort_)
+ .Send()) {
+ LOG(ERROR, "Failed to send the status.\n");
+ }
+ }
+ }
+}
+
+template <class T>
+uint32_t ActorBase<T>::RunIteration() {
+ CHECK(action_q_->goal.get() != nullptr);
+ const uint32_t running_id = action_q_->goal->run;
+ LOG(INFO, "Starting action %" PRIx32 "\n", running_id);
+ if (!action_q_->status.MakeWithBuilder()
+ .running(running_id)
+ .last_running(0)
+ .success(!abort_)
+ .Send()) {
+ LOG(ERROR, "Failed to send the status.\n");
+ }
+ LOG_STRUCT(INFO, "goal", *action_q_->goal);
+ abort_ = !RunAction(action_q_->goal->params);
+ LOG(INFO, "Done with action %" PRIx32 "\n", running_id);
+
+ // If we have a new one to run, we shouldn't say we're stopped in between.
+ if (action_q_->goal->run == 0 || action_q_->goal->run == running_id) {
+ if (!action_q_->status.MakeWithBuilder()
+ .running(0)
+ .last_running(running_id)
+ .success(!abort_)
+ .Send()) {
+ LOG(ERROR, "Failed to send the status.\n");
+ } else {
+ LOG(INFO, "Sending Done status %" PRIx32 "\n", running_id);
+ }
+ } else {
+ LOG(INFO, "skipping sending stopped status for %" PRIx32 "\n", running_id);
+ }
+
+ return running_id;
+}
+
+template <class T>
+void ActorBase<T>::WaitForStop(uint32_t running_id) {
+ assert(action_q_->goal.get() != nullptr);
+ while (action_q_->goal->run == running_id) {
+ LOG(INFO, "Waiting for the action (%" PRIx32 ") to be stopped.\n",
+ running_id);
+ action_q_->goal.FetchNextBlocking();
+ LOG_STRUCT(DEBUG, "goal queue", *action_q_->goal);
+ }
+}
+
+template <class T>
+void ActorBase<T>::Run() {
+ Initialize();
+
+ while (true) {
+ // Wait for a request to come in before starting.
+ WaitForActionRequest();
+
+ LOG_STRUCT(INFO, "running with goal", *action_q_->goal);
+
+ // Perform the action once.
+ uint32_t running_id = RunIteration();
+
+ LOG(INFO, "done running\n");
+
+ // Don't start again until asked.
+ WaitForStop(running_id);
+ LOG(DEBUG, "action %" PRIx32 " was stopped\n", running_id);
+ }
+}
+
+template <class T>
+void ActorBase<T>::Initialize() {
+ // Make sure the last job is done and we have a signal.
+ if (CheckInitialRunning()) {
+ LOG(DEBUG, "action %" PRIx32 " was stopped\n", action_q_->goal->run);
+ }
+
+ if (!action_q_->status.MakeWithBuilder()
+ .running(0)
+ .last_running(0)
+ .success(!abort_)
+ .Send()) {
+ LOG(ERROR, "Failed to send the status.\n");
+ }
+}
+
+template <class T>
+bool ActorBase<T>::WaitUntil(::std::function<bool(void)> done_condition,
+ const ::aos::time::Time& end_time) {
+ while (!done_condition()) {
+ if (ShouldCancel() || abort_) {
+ // Clear abort bit as we have just aborted.
+ abort_ = false;
+ return true;
+ }
+ if (end_time != ::aos::time::Time(0, 0) &&
+ ::aos::time::Time::Now() >= end_time) {
+ LOG(DEBUG, "WaitUntil timed out\n");
+ return false;
+ }
+ }
+ if (ShouldCancel() || abort_) {
+ // Clear abort bit as we have just aborted.
+ abort_ = false;
+ return true;
+ } else {
+ return false;
+ }
+}
+
+template <class T>
+bool ActorBase<T>::ShouldCancel() {
+ if (action_q_->goal.FetchNext()) {
+ LOG_STRUCT(DEBUG, "goal queue", *action_q_->goal);
+ }
+ bool ans = !action_q_->goal->run;
+ if (ans) {
+ LOG(INFO, "Time to stop action\n");
+ }
+ return ans;
+}
+
+} // namespace actions
+} // namespace common
+} // namespace aos
+
+#endif // AOS_COMMON_ACTIONS_ACTOR_H_
diff --git a/aos/common/actions/test_action.q b/aos/common/actions/test_action.q
new file mode 100644
index 0000000..3451c04
--- /dev/null
+++ b/aos/common/actions/test_action.q
@@ -0,0 +1,35 @@
+package aos.common.actions;
+
+import "aos/common/actions/actions.q";
+
+queue_group TestActionQueueGroup {
+ implements aos.common.actions.ActionQueueGroup;
+
+ message Goal {
+ uint32_t run;
+ uint32_t params;
+ };
+
+ queue Goal goal;
+ queue aos.common.actions.Status status;
+};
+
+struct MyParams {
+ double param1;
+ int32_t param2;
+};
+
+queue_group TestAction2QueueGroup {
+ implements aos.common.actions.ActionQueueGroup;
+
+ message Goal {
+ uint32_t run;
+ MyParams params;
+ };
+
+ queue Goal goal;
+ queue aos.common.actions.Status status;
+};
+
+queue_group TestActionQueueGroup test_action;
+queue_group TestAction2QueueGroup test_action2;
diff --git a/aos/common/byteorder.h b/aos/common/byteorder.h
new file mode 100644
index 0000000..ae6ecf4
--- /dev/null
+++ b/aos/common/byteorder.h
@@ -0,0 +1,201 @@
+#ifndef AOS_COMMON_BYTEORDER_H_
+#define AOS_COMMON_BYTEORDER_H_
+
+#ifndef __VXWORKS__
+#include <endian.h> // endian(3)
+#endif
+#include <string.h>
+#include <stdint.h>
+
+// Contains functions for converting between host and network byte order for
+// things other than 16/32 bit integers (those are provided by byteorder(3)).
+// Also gives a nice templated interface to these functions.
+
+namespace aos {
+namespace {
+
+template <typename int_type, int_type (*function)(int_type)>
+static inline void copier(const void *in, void *out) {
+ // Have confirmed by looking at the assembly code that this generates the same
+ // code as the (undefined by the c++ standard) way of using a union to do it.
+ // (which is pretty efficient)
+ int_type temp;
+ memcpy(&temp, in, sizeof(int_type));
+ temp = function(temp);
+ memcpy(out, &temp, sizeof(int_type));
+}
+template <typename int_type, typename T, int_type (*function)(int_type)>
+static inline T memcpier(T in) {
+ copier<int_type, function>(&in, &in);
+ return in;
+}
+
+// Needed because be64toh etc are macros (not that the manpage says
+// anything...).
+// These are used instead of ntohs etc because gcc didn't inline those.
+#ifdef __clang__
+// Apparently the macros use "register", and clang doesn't like that.
+#define register
+#endif
+static inline uint64_t _be64toh(uint64_t in) { return be64toh(in); }
+static inline uint64_t _htobe64(uint64_t in) { return htobe64(in); }
+static inline uint32_t _be32toh(uint32_t in) { return be32toh(in); }
+static inline uint32_t _htobe32(uint32_t in) { return htobe32(in); }
+static inline uint16_t _be16toh(uint16_t in) { return be16toh(in); }
+static inline uint16_t _htobe16(uint16_t in) { return htobe16(in); }
+#ifdef __clang__
+#undef register
+#endif
+
+template<int bytes, typename T> class do_ntoh {
+ public:
+ static inline T apply(T net);
+ static inline void copy(const char *in, T &net);
+};
+template<typename T> class do_ntoh<1, T> {
+ public:
+ static inline T apply(T net) { return net; }
+ static inline void copy(const char *in, T *host) { host[0] = in[0]; }
+};
+template<typename T> class do_ntoh<2, T> {
+ public:
+ static inline T apply(T net) { return memcpier<uint16_t, T, _be16toh>(net); }
+ static inline void copy(const char *in, T *host) {
+ copier<uint16_t, _be16toh>(in, host);
+ }
+};
+template<typename T> class do_ntoh<4, T> {
+ public:
+ static inline T apply(T net) { return memcpier<uint32_t, T, _be32toh>(net); }
+ static inline void copy(const char *in, T *host) {
+ copier<uint32_t, _be32toh>(in, host);
+ }
+};
+template<typename T> class do_ntoh<8, T> {
+ public:
+ static inline T apply(T net) { return memcpier<uint64_t, T, _be64toh>(net); }
+ static inline void copy(const char *in, T *host) {
+ copier<uint64_t, _be64toh>(in, host);
+ }
+};
+
+template <size_t Size>
+struct do_ntoh_int {
+ static inline void copy(const char *in, char *host);
+};
+template <>
+struct do_ntoh_int<1> {
+ static inline void copy(const char *in, char *host) { host[0] = in[0]; }
+};
+template <>
+struct do_ntoh_int<2> {
+ static inline void copy(const char *in, char *host) {
+ copier<uint16_t, _be16toh>(in, host);
+ }
+};
+template <>
+struct do_ntoh_int<4> {
+ static inline void copy(const char *in, char *host) {
+ copier<uint32_t, _be32toh>(in, host);
+ }
+};
+template <>
+struct do_ntoh_int<8> {
+ static inline void copy(const char *in, char *host) {
+ copier<uint64_t, _be64toh>(in, host);
+ }
+};
+
+template<int bytes, typename T> class do_hton {
+ public:
+ static inline T apply(T host);
+ static inline void copy(const T *host, char *out);
+};
+template<typename T> class do_hton<1, T> {
+ public:
+ static inline T apply(T host) { return host; }
+ static inline void copy(const T *host, char *out) { out[0] = host[0]; }
+};
+template<typename T> class do_hton<2, T> {
+ public:
+ static inline T apply(T host) { return memcpier<uint16_t, T, _htobe16>(host); }
+ static inline void copy(const T *host, char *out) {
+ copier<uint16_t, _htobe16>(host, out);
+ }
+};
+template<typename T> class do_hton<4, T> {
+ public:
+ static inline T apply(T host) { return memcpier<uint32_t, T, _htobe32>(host); }
+ static inline void copy(const T *host, char *out) {
+ copier<uint32_t, _htobe32>(host, out);
+ }
+};
+template<typename T> class do_hton<8, T> {
+ public:
+ static inline T apply(T host) { return memcpier<uint64_t, T, _htobe64>(host); }
+ static inline void copy(const T *host, char *out) {
+ copier<uint64_t, _htobe64>(host, out);
+ }
+};
+
+template <size_t Size>
+struct do_hton_int {
+ static inline void copy(const char *host, char *out);
+};
+template <>
+struct do_hton_int<1> {
+ static inline void copy(const char *host, char *out) { out[0] = host[0]; }
+};
+template <>
+struct do_hton_int<2> {
+ static inline void copy(const char *host, char *out) {
+ copier<uint16_t, _htobe16>(host, out);
+ }
+};
+template <>
+struct do_hton_int<4> {
+ static inline void copy(const char *host, char *out) {
+ copier<uint32_t, _htobe32>(host, out);
+ }
+};
+template <>
+struct do_hton_int<8> {
+ static inline void copy(const char *host, char *out) {
+ copier<uint64_t, _htobe64>(host, out);
+ }
+};
+
+} // namespace
+
+// Converts T from network to host byte order.
+template <typename T>
+static inline T ntoh(T net) {
+ return do_ntoh<sizeof(net), T>::apply(net);
+}
+// Converts T from host to network byte order.
+template <typename T>
+static inline T hton(T host) {
+ return do_hton<sizeof(host), T>::apply(host);
+}
+
+template <typename T>
+static inline void to_host(const char *input, T *host) {
+ do_ntoh<sizeof(*host), T>::copy(input, host);
+}
+template <typename T>
+static inline void to_network(const T *host, char *output) {
+ do_hton<sizeof(*host), T>::copy(host, output);
+}
+
+template <size_t Size>
+static inline void to_host(const char *input, char *host) {
+ do_ntoh_int<Size>::copy(input, host);
+}
+template <size_t Size>
+static inline void to_network(const char *host, char *output) {
+ do_hton_int<Size>::copy(host, output);
+}
+
+} // namespace aos
+
+#endif
diff --git a/aos/common/common.gyp b/aos/common/common.gyp
new file mode 100644
index 0000000..461200e
--- /dev/null
+++ b/aos/common/common.gyp
@@ -0,0 +1,370 @@
+{
+ 'targets': [
+ {
+ 'target_name': 'test_queue',
+ 'type': 'static_library',
+ 'sources': [
+ '<(AOS)/common/test_queue.q',
+ ],
+ 'variables': {
+ 'header_path': 'aos/common',
+ },
+ 'includes': ['../build/queues.gypi'],
+ },
+ {
+ 'target_name': 'queue_testutils',
+ 'type': 'static_library',
+ 'sources': [
+ 'queue_testutils.cc',
+ ],
+ 'dependencies': [
+ '<(AOS)/build/aos.gyp:logging',
+ 'once',
+ '<(AOS)/linux_code/ipc_lib/ipc_lib.gyp:shared_mem',
+ 'mutex',
+ ],
+ 'export_dependent_settings': [
+ '<(AOS)/linux_code/ipc_lib/ipc_lib.gyp:shared_mem',
+ ],
+ },
+ {
+ 'target_name': 'time',
+ 'type': 'static_library',
+ 'sources': [
+ 'time.cc'
+ ],
+ 'dependencies': [
+ '<(AOS)/build/aos.gyp:logging_interface',
+ 'mutex',
+ ],
+ },
+ {
+ 'target_name': 'queue_types',
+ 'type': 'static_library',
+ 'variables': {
+ 'print_field_cc': '<(SHARED_INTERMEDIATE_DIR)/print_field.cc',
+ 'queue_primitives_h': '<(SHARED_INTERMEDIATE_DIR)/aos_queue_primitives/aos/queue_primitives.h',
+ },
+ 'sources': [
+ 'queue_types.cc',
+ '<(print_field_cc)',
+ ],
+ 'dependencies': [
+ '<(AOS)/build/aos.gyp:logging_interface',
+ '<(AOS)/linux_code/ipc_lib/ipc_lib.gyp:shared_mem',
+ '<(AOS)/linux_code/ipc_lib/ipc_lib.gyp:core_lib',
+ 'mutex',
+ 'time',
+ ],
+ 'export_dependent_settings': [
+ 'time',
+ ],
+ 'actions': [
+ {
+ 'variables': {
+ 'script': '<(AOS)/build/queues/print_field.rb',
+ },
+ 'action_name': 'gen_print_field',
+ 'inputs': [
+ '<(script)',
+ '<!@(find <(AOS)/build/queues/ -name *.rb)',
+ ],
+ 'outputs': [
+ '<(print_field_cc)',
+ ],
+ 'action': ['ruby', '<(script)', '<(print_field_cc)'],
+ 'message': 'Generating print_field.cc',
+ },
+ {
+ 'variables': {
+ 'script': '<(AOS)/build/queues/queue_primitives.rb',
+ },
+ 'action_name': 'gen_queue_primitives',
+ 'inputs': [
+ '<(script)',
+ '<!@(find <(AOS)/build/queues/ -name *.rb)',
+ ],
+ 'outputs': [
+ '<(queue_primitives_h)',
+ ],
+ 'action': ['ruby', '<(script)', '<(queue_primitives_h)'],
+ 'message': 'Generating queue_primitives.h',
+ },
+ ],
+ 'direct_dependent_settings': {
+ 'include_dirs': [
+ '<(SHARED_INTERMEDIATE_DIR)/aos_queue_primitives',
+ ],
+ },
+ 'hard_dependency': 1,
+ },
+ {
+ 'target_name': 'queue_types_test',
+ 'type': 'executable',
+ 'sources': [
+ 'queue_types_test.cc',
+ ],
+ 'dependencies': [
+ 'queue_types',
+ '<(EXTERNALS):gtest',
+ 'test_queue',
+ '<(AOS)/build/aos.gyp:logging',
+ 'queue_testutils',
+ ],
+ },
+ {
+ 'target_name': 'queues',
+ 'type': 'static_library',
+ 'sources': [
+ 'queue.cc',
+ ],
+ 'dependencies': [
+ '<(AOS)/linux_code/ipc_lib/ipc_lib.gyp:queue',
+ 'time',
+ ],
+ 'export_dependent_settings': [
+ '<(AOS)/linux_code/ipc_lib/ipc_lib.gyp:queue',
+ 'time',
+ ],
+ },
+ {
+ 'target_name': 'scoped_fd',
+ 'type': 'static_library',
+ 'sources': [
+ # 'scoped_fd.h'
+ ],
+ 'dependencies': [
+ '<(AOS)/build/aos.gyp:logging',
+ ],
+ 'export_dependent_settings': [
+ '<(AOS)/build/aos.gyp:logging',
+ ],
+ },
+ {
+ 'target_name': 'queue_test',
+ 'type': 'executable',
+ 'sources': [
+ 'queue_test.cc',
+ ],
+ 'dependencies': [
+ '<(EXTERNALS):gtest',
+ 'queue_testutils',
+ 'test_queue',
+ '<(AOS)/common/util/util.gyp:thread',
+ 'die',
+ ],
+ },
+ {
+ 'target_name': 'type_traits_test',
+ 'type': 'executable',
+ 'sources': [
+ 'type_traits_test.cpp',
+ ],
+ 'dependencies': [
+ '<(EXTERNALS):gtest',
+ ],
+ },
+ {
+ 'target_name': 'gtest_prod',
+ 'type': 'static_library',
+ 'dependencies': [
+ '<(EXTERNALS):gtest_prod',
+ ],
+ 'export_dependent_settings': [
+ '<(EXTERNALS):gtest_prod',
+ ],
+ },
+ {
+ 'target_name': 'once',
+ 'type': 'static_library',
+ 'dependencies': [
+ '<(EXTERNALS):gtest_prod',
+ ],
+ 'export_dependent_settings': [
+ '<(EXTERNALS):gtest_prod',
+ ],
+ },
+ {
+ 'target_name': 'once_test',
+ 'type': 'executable',
+ 'sources': [
+ 'once_test.cc',
+ ],
+ 'dependencies': [
+ '<(EXTERNALS):gtest',
+ 'once',
+ ],
+ },
+ {
+ 'target_name': 'time_test',
+ 'type': 'executable',
+ 'sources': [
+ 'time_test.cc',
+ ],
+ 'dependencies': [
+ '<(EXTERNALS):gtest',
+ 'time',
+ '<(AOS)/build/aos.gyp:logging',
+ '<(AOS)/common/util/util.gyp:death_test_log_implementation',
+ ],
+ },
+ {
+ 'target_name': 'die',
+ 'type': 'static_library',
+ 'sources': [
+ 'die.cc',
+ ],
+ 'dependencies': [
+ '<(AOS)/common/libc/libc.gyp:aos_strerror',
+ ],
+ 'export_dependent_settings': [
+ '<(AOS)/common/libc/libc.gyp:aos_strerror',
+ ],
+ },
+ {
+ 'target_name': 'condition',
+ 'type': 'static_library',
+ 'sources': [
+ '<(AOS)/linux_code/ipc_lib/condition.cc',
+ ],
+ 'dependencies': [
+ 'mutex',
+ '<(AOS)/linux_code/ipc_lib/ipc_lib.gyp:aos_sync',
+ '<(AOS)/build/aos.gyp:logging_interface',
+ ],
+ 'export_dependent_settings': [
+ '<(AOS)/linux_code/ipc_lib/ipc_lib.gyp:aos_sync',
+ ],
+ },
+ {
+ 'target_name': 'mutex',
+ 'type': 'static_library',
+ 'sources': [
+ '<(AOS)/linux_code/ipc_lib/mutex.cc',
+ ],
+ 'dependencies': [
+ '<(AOS)/linux_code/ipc_lib/ipc_lib.gyp:aos_sync',
+ '<(AOS)/build/aos.gyp:logging_interface',
+ ],
+ 'export_dependent_settings': [
+ '<(AOS)/linux_code/ipc_lib/ipc_lib.gyp:aos_sync',
+ ],
+ },
+ {
+ 'target_name': 'event',
+ 'type': 'static_library',
+ 'sources': [
+ '<(AOS)/linux_code/ipc_lib/event.cc',
+ ],
+ 'dependencies': [
+ '<(AOS)/linux_code/ipc_lib/ipc_lib.gyp:aos_sync',
+ '<(AOS)/build/aos.gyp:logging_interface',
+ ],
+ 'export_dependent_settings': [
+ '<(AOS)/linux_code/ipc_lib/ipc_lib.gyp:aos_sync',
+ ],
+ },
+ {
+ 'target_name': 'queue_testutils_test',
+ 'type': 'executable',
+ 'sources': [
+ 'queue_testutils_test.cc',
+ ],
+ 'dependencies': [
+ '<(EXTERNALS):gtest',
+ 'queue_testutils',
+ '<(AOS)/build/aos.gyp:logging',
+ ],
+ },
+ {
+ 'target_name': 'mutex_test',
+ 'type': 'executable',
+ 'sources': [
+ 'mutex_test.cc',
+ ],
+ 'dependencies': [
+ '<(EXTERNALS):gtest',
+ 'mutex',
+ 'die',
+ '<(AOS)/build/aos.gyp:logging',
+ '<(AOS)/common/util/util.gyp:death_test_log_implementation',
+ '<(AOS)/common/util/util.gyp:thread',
+ '<(AOS)/common/common.gyp:time',
+ 'queue_testutils',
+ ],
+ },
+ {
+ 'target_name': 'event_test',
+ 'type': 'executable',
+ 'sources': [
+ 'event_test.cc',
+ ],
+ 'dependencies': [
+ '<(EXTERNALS):gtest',
+ 'event',
+ 'queue_testutils',
+ 'time',
+ ],
+ },
+ {
+ 'target_name': 'condition_test',
+ 'type': 'executable',
+ 'sources': [
+ 'condition_test.cc',
+ ],
+ 'dependencies': [
+ '<(EXTERNALS):gtest',
+ 'condition',
+ '<(AOS)/common/util/util.gyp:thread',
+ 'time',
+ 'mutex',
+ '<(AOS)/build/aos.gyp:logging',
+ 'queue_testutils',
+ '<(AOS)/linux_code/ipc_lib/ipc_lib.gyp:core_lib',
+ '<(AOS)/linux_code/ipc_lib/ipc_lib.gyp:aos_sync',
+ 'die',
+ '<(AOS)/common/util/util.gyp:thread',
+ ],
+ },
+ {
+ 'target_name': 'die_test',
+ 'type': 'executable',
+ 'sources': [
+ 'die_test.cc',
+ ],
+ 'dependencies': [
+ '<(EXTERNALS):gtest',
+ 'die',
+ ],
+ },
+ {
+ 'target_name': 'stl_mutex',
+ 'type': 'static_library',
+ 'sources': [
+ #'stl_mutex.h'
+ ],
+ 'dependencies': [
+ '<(AOS)/linux_code/ipc_lib/ipc_lib.gyp:aos_sync',
+ '<(AOS)/build/aos.gyp:logging',
+ ],
+ 'export_dependent_settings': [
+ '<(AOS)/linux_code/ipc_lib/ipc_lib.gyp:aos_sync',
+ '<(AOS)/build/aos.gyp:logging',
+ ],
+ },
+ {
+ 'target_name': 'stl_mutex_test',
+ 'type': 'executable',
+ 'sources': [
+ 'stl_mutex_test.cc',
+ ],
+ 'dependencies': [
+ 'stl_mutex',
+ '<(EXTERNALS):gtest',
+ 'queue_testutils',
+ '<(AOS)/common/util/util.gyp:thread',
+ 'die',
+ ],
+ },
+ ],
+}
diff --git a/aos/common/commonmath.h b/aos/common/commonmath.h
new file mode 100644
index 0000000..f9337b2
--- /dev/null
+++ b/aos/common/commonmath.h
@@ -0,0 +1,27 @@
+#ifndef AOS_COMMON_MATH_H_
+#define AOS_COMMON_MATH_H_
+
+namespace aos {
+
+// Clips a value so that it is in [min, max]
+static inline double Clip(double value, double min, double max) {
+ if (value > max) {
+ value = max;
+ } else if (value < min) {
+ value = min;
+ }
+ return value;
+}
+
+template <typename T>
+static inline int sign(T val) {
+ if (val > T(0)) {
+ return 1;
+ } else {
+ return -1;
+ }
+}
+
+} // namespace aos
+
+#endif // AOS_COMMON_MATH_H_
diff --git a/aos/common/condition.h b/aos/common/condition.h
new file mode 100644
index 0000000..eccc0da
--- /dev/null
+++ b/aos/common/condition.h
@@ -0,0 +1,83 @@
+#ifndef AOS_COMMON_CONDITION_H_
+#define AOS_COMMON_CONDITION_H_
+
+#include "aos/linux_code/ipc_lib/aos_sync.h"
+
+namespace aos {
+
+class Mutex;
+
+// A condition variable (IPC mechanism where 1 process/task can notify all
+// others that are waiting for something to happen) without the race condition
+// where a notification is sent after some process has checked if the thing has
+// happened but before it has started listening for notifications.
+//
+// This implementation will LOG(FATAL) if anything weird happens.
+//
+// A simple example of the use of a condition variable (adapted from
+// pthread_cond(3)):
+//
+// int x, y;
+// Mutex mutex;
+// Condition condition(&mutex);
+//
+// // Waiting until x is greater than y:
+// {
+// MutexLocker locker(&mutex);
+// while (!(x > y)) condition.Wait();
+// // do whatever
+// }
+//
+// // Modifying x and/or y:
+// {
+// MutexLocker locker(&mutex);
+// // modify x and y
+// if (x > y) condition.Broadcast();
+// }
+//
+// Notice the loop around the Wait(). This is very important because some other
+// process can lock the mutex and modify the shared state (possibly undoing
+// whatever the Wait()er was waiting for) in between the Broadcast()er unlocking
+// the mutex and the Wait()er(s) relocking it.
+//
+// Multiple condition variables may be associated with the same mutex but
+// exactly one mutex must be associated with each condition variable.
+class Condition {
+ public:
+ // m is the mutex that will be associated with this condition variable. This
+ // object will hold on to a reference to it but does not take ownership.
+ explicit Condition(Mutex *m);
+
+ // Waits for the condition variable to be signalled, atomically unlocking the
+ // mutex associated with this condition variable at the same time. The mutex
+ // associated with this condition variable must be locked when this is called
+ // and will be locked when this method returns.
+ // NOTE: The relocking of the mutex is not performed atomically with waking
+ // up.
+ // Returns false.
+ bool Wait() __attribute__((warn_unused_result));
+
+ // Signals approximately 1 other process currently Wait()ing on this condition
+ // variable. Calling this does not require the mutex associated with this
+ // condition variable to be locked.
+ // One of the processes with the highest priority level will be woken.
+ // If there aren't any waiting at the time, none will be woken. There is a
+ // small race condition in the Linux implementation that can result in more
+ // than 1 being woken.
+ void Signal();
+ // Wakes all processes that are currently Wait()ing on this condition
+ // variable. Calling this does not require the mutex associated with this
+ // condition variable to be locked.
+ void Broadcast();
+
+ // Retrieves the mutex associated with this condition variable.
+ Mutex *m() { return m_; }
+
+ private:
+ aos_condition impl_;
+ Mutex *m_;
+};
+
+} // namespace aos
+
+#endif // AOS_COMMON_CONDITION_H_
diff --git a/aos/common/condition_test.cc b/aos/common/condition_test.cc
new file mode 100644
index 0000000..2310273
--- /dev/null
+++ b/aos/common/condition_test.cc
@@ -0,0 +1,367 @@
+#include "aos/common/condition.h"
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <atomic>
+
+#include "gtest/gtest.h"
+
+#include "aos/common/time.h"
+#include "aos/common/mutex.h"
+#include "aos/common/queue_testutils.h"
+#include "aos/common/type_traits.h"
+#include "aos/linux_code/ipc_lib/core_lib.h"
+#include "aos/common/logging/logging.h"
+#include "aos/common/macros.h"
+#include "aos/linux_code/ipc_lib/aos_sync.h"
+#include "aos/common/die.h"
+#include "aos/common/util/thread.h"
+
+using ::aos::time::Time;
+using ::aos::common::testing::GlobalCoreInstance;
+
+namespace aos {
+namespace testing {
+
+class ConditionTestCommon : public ::testing::Test {
+ public:
+ ConditionTestCommon() {}
+
+ void Settle() {
+ time::SleepFor(::Time::InSeconds(0.008));
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ConditionTestCommon);
+};
+
+// Some simple tests that don't rely on a GlobalCoreInstance to help with
+// debugging problems that cause tests using that to just completely lock up.
+class SimpleConditionTest : public ConditionTestCommon {
+ public:
+ SimpleConditionTest() : condition_(&mutex_) {}
+
+ Mutex mutex_;
+ Condition condition_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SimpleConditionTest);
+};
+
+// Makes sure that nothing crashes or anything with a basic Wait() and then
+// Signal().
+// This test is written to hopefully fail instead of deadlocking on failure, but
+// it's tricky because there's no way to kill the child in the middle.
+TEST_F(SimpleConditionTest, Basic) {
+ ::std::atomic_bool child_finished(false);
+ Condition child_ready(&mutex_);
+ ASSERT_FALSE(mutex_.Lock());
+ util::FunctionThread child([this, &child_finished, &child_ready](
+ util::FunctionThread *) {
+ ASSERT_FALSE(mutex_.Lock());
+ child_ready.Broadcast();
+ ASSERT_FALSE(condition_.Wait());
+ child_finished.store(true);
+ mutex_.Unlock();
+ });
+ child.Start();
+ ASSERT_FALSE(child_ready.Wait());
+ EXPECT_FALSE(child_finished.load());
+ condition_.Signal();
+ mutex_.Unlock();
+ child.Join();
+ EXPECT_TRUE(child_finished.load());
+}
+
+class ConditionTest : public ConditionTestCommon {
+ public:
+ struct Shared {
+ Shared() : condition(&mutex) {}
+
+ Mutex mutex;
+ Condition condition;
+ };
+ static_assert(shm_ok<Shared>::value,
+ "it's going to get shared between forked processes");
+
+ ConditionTest() : shared_(static_cast<Shared *>(shm_malloc(sizeof(Shared)))) {
+ new (shared_) Shared();
+ }
+ ~ConditionTest() {
+ shared_->~Shared();
+ }
+
+ GlobalCoreInstance my_core;
+
+ Shared *const shared_;
+
+ protected:
+ void SetUp() override {
+ SetDieTestMode(true);
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ConditionTest);
+};
+
+class ConditionTestProcess {
+ public:
+ enum class Action {
+ kWaitLockStart, // lock, delay, wait, unlock
+ kWait, // delay, lock, wait, unlock
+ kWaitNoUnlock, // delay, lock, wait
+ };
+
+ // This amount gets added to any passed in delay to make the test repeatable.
+ static constexpr ::Time kMinimumDelay = ::Time::InSeconds(0.15);
+ static constexpr ::Time kDefaultTimeout = ::Time::InSeconds(0.15);
+
+ // delay is how long to wait before doing action to condition.
+ // timeout is how long to wait after delay before deciding that it's hung.
+ ConditionTestProcess(const ::Time &delay, Action action, Condition *condition,
+ const ::Time &timeout = kDefaultTimeout)
+ : delay_(kMinimumDelay + delay), action_(action), condition_(condition),
+ timeout_(delay_ + timeout), child_(-1),
+ shared_(static_cast<Shared *>(shm_malloc(sizeof(Shared)))) {
+ new (shared_) Shared();
+ }
+ ~ConditionTestProcess() {
+ CHECK_EQ(child_, -1);
+ }
+
+ void Start() {
+ ASSERT_FALSE(shared_->started);
+
+ child_ = fork();
+ if (child_ == 0) { // in child
+ ::aos::common::testing::PreventExit();
+ Run();
+ exit(EXIT_SUCCESS);
+ } else { // in parent
+ CHECK_NE(child_, -1);
+
+ ASSERT_EQ(0, futex_wait(&shared_->ready));
+
+ shared_->started = true;
+ }
+ }
+
+ bool IsFinished() {
+ return shared_->finished;
+ }
+
+ ::testing::AssertionResult Hung() {
+ if (!shared_->started) {
+ ADD_FAILURE();
+ return ::testing::AssertionFailure() << "not started yet";
+ }
+ if (shared_->finished) {
+ Join();
+ return ::testing::AssertionFailure() << "already returned";
+ }
+ if (shared_->delayed) {
+ if (shared_->start_time > ::Time::Now() + timeout_) {
+ Kill();
+ return ::testing::AssertionSuccess() << "already been too long";
+ }
+ } else {
+ CHECK_EQ(0, futex_wait(&shared_->done_delaying));
+ }
+ time::SleepFor(::Time::InSeconds(0.01));
+ if (!shared_->finished) time::SleepUntil(shared_->start_time + timeout_);
+ if (shared_->finished) {
+ Join();
+ return ::testing::AssertionFailure() << "completed within timeout";
+ } else {
+ Kill();
+ return ::testing::AssertionSuccess() << "took too long";
+ }
+ }
+ ::testing::AssertionResult Test() {
+ Start();
+ return Hung();
+ }
+
+ private:
+ struct Shared {
+ Shared()
+ : started(false), delayed(false), done_delaying(0), start_time(0, 0),
+ finished(false), ready(0) {
+ }
+
+ volatile bool started;
+ volatile bool delayed;
+ aos_futex done_delaying;
+ ::Time start_time;
+ volatile bool finished;
+ aos_futex ready;
+ };
+ static_assert(shm_ok<Shared>::value,
+ "it's going to get shared between forked processes");
+
+ void Run() {
+ if (action_ == Action::kWaitLockStart) {
+ ASSERT_EQ(1, futex_set(&shared_->ready));
+ ASSERT_FALSE(condition_->m()->Lock());
+ }
+ time::SleepFor(delay_);
+ shared_->start_time = ::Time::Now();
+ shared_->delayed = true;
+ ASSERT_NE(-1, futex_set(&shared_->done_delaying));
+ if (action_ != Action::kWaitLockStart) {
+ ASSERT_EQ(1, futex_set(&shared_->ready));
+ ASSERT_FALSE(condition_->m()->Lock());
+ }
+ // TODO(brians): Test this returning true (aka the owner dying).
+ ASSERT_FALSE(condition_->Wait());
+ shared_->finished = true;
+ if (action_ != Action::kWaitNoUnlock) {
+ condition_->m()->Unlock();
+ }
+ }
+
+ void Join() {
+ CHECK_NE(child_, -1);
+ int status;
+ do {
+ CHECK_EQ(waitpid(child_, &status, 0), child_);
+ } while (!(WIFEXITED(status) || WIFSIGNALED(status)));
+ child_ = -1;
+ }
+ void Kill() {
+ CHECK_NE(child_, -1);
+ PCHECK(kill(child_, SIGTERM));
+ Join();
+ }
+
+ const ::Time delay_;
+ const Action action_;
+ Condition *const condition_;
+ const ::Time timeout_;
+
+ pid_t child_;
+
+ Shared *const shared_;
+
+ DISALLOW_COPY_AND_ASSIGN(ConditionTestProcess);
+};
+constexpr ::Time ConditionTestProcess::kMinimumDelay;
+constexpr ::Time ConditionTestProcess::kDefaultTimeout;
+
+// Makes sure that the testing framework and everything work for a really simple
+// Wait() and then Signal().
+TEST_F(ConditionTest, Basic) {
+ ConditionTestProcess child(::Time(0, 0),
+ ConditionTestProcess::Action::kWait,
+ &shared_->condition);
+ child.Start();
+ Settle();
+ EXPECT_FALSE(child.IsFinished());
+ shared_->condition.Signal();
+ EXPECT_FALSE(child.Hung());
+}
+
+// Makes sure that the worker child locks before it tries to Wait() etc.
+TEST_F(ConditionTest, Locking) {
+ ConditionTestProcess child(::Time(0, 0),
+ ConditionTestProcess::Action::kWait,
+ &shared_->condition);
+ ASSERT_FALSE(shared_->mutex.Lock());
+ child.Start();
+ Settle();
+ // This Signal() shouldn't do anything because the child should still be
+ // waiting to lock the mutex.
+ shared_->condition.Signal();
+ Settle();
+ shared_->mutex.Unlock();
+ EXPECT_TRUE(child.Hung());
+}
+
+// Tests that the work child only catches a Signal() after the mutex gets
+// unlocked.
+TEST_F(ConditionTest, LockFirst) {
+ ConditionTestProcess child(::Time(0, 0),
+ ConditionTestProcess::Action::kWait,
+ &shared_->condition);
+ ASSERT_FALSE(shared_->mutex.Lock());
+ child.Start();
+ Settle();
+ shared_->condition.Signal();
+ Settle();
+ EXPECT_FALSE(child.IsFinished());
+ shared_->mutex.Unlock();
+ Settle();
+ EXPECT_FALSE(child.IsFinished());
+ shared_->condition.Signal();
+ EXPECT_FALSE(child.Hung());
+}
+
+// Tests that the mutex gets relocked after Wait() returns.
+TEST_F(ConditionTest, Relocking) {
+ ConditionTestProcess child(::Time(0, 0),
+ ConditionTestProcess::Action::kWaitNoUnlock,
+ &shared_->condition);
+ child.Start();
+ Settle();
+ shared_->condition.Signal();
+ EXPECT_FALSE(child.Hung());
+ EXPECT_EQ(Mutex::State::kLockFailed, shared_->mutex.TryLock());
+}
+
+// Tests that Signal() stops exactly 1 Wait()er.
+TEST_F(ConditionTest, SignalOne) {
+ ConditionTestProcess child1(::Time(0, 0),
+ ConditionTestProcess::Action::kWait,
+ &shared_->condition);
+ ConditionTestProcess child2(::Time(0, 0),
+ ConditionTestProcess::Action::kWait,
+ &shared_->condition);
+ ConditionTestProcess child3(::Time(0, 0),
+ ConditionTestProcess::Action::kWait,
+ &shared_->condition);
+ auto number_finished = [&]() { return (child1.IsFinished() ? 1 : 0) +
+ (child2.IsFinished() ? 1 : 0) + (child3.IsFinished() ? 1 : 0); };
+ child1.Start();
+ child2.Start();
+ child3.Start();
+ Settle();
+ EXPECT_EQ(0, number_finished());
+ shared_->condition.Signal();
+ Settle();
+ EXPECT_EQ(1, number_finished());
+ shared_->condition.Signal();
+ Settle();
+ EXPECT_EQ(2, number_finished());
+ shared_->condition.Signal();
+ Settle();
+ EXPECT_EQ(3, number_finished());
+ EXPECT_FALSE(child1.Hung());
+ EXPECT_FALSE(child2.Hung());
+ EXPECT_FALSE(child3.Hung());
+}
+
+// Tests that Brodcast() wakes multiple Wait()ers.
+TEST_F(ConditionTest, Broadcast) {
+ ConditionTestProcess child1(::Time(0, 0),
+ ConditionTestProcess::Action::kWait,
+ &shared_->condition);
+ ConditionTestProcess child2(::Time(0, 0),
+ ConditionTestProcess::Action::kWait,
+ &shared_->condition);
+ ConditionTestProcess child3(::Time(0, 0),
+ ConditionTestProcess::Action::kWait,
+ &shared_->condition);
+ child1.Start();
+ child2.Start();
+ child3.Start();
+ Settle();
+ shared_->condition.Broadcast();
+ EXPECT_FALSE(child1.Hung());
+ EXPECT_FALSE(child2.Hung());
+ EXPECT_FALSE(child3.Hung());
+}
+
+} // namespace testing
+} // namespace aos
diff --git a/aos/common/controls/control_loop-tmpl.h b/aos/common/controls/control_loop-tmpl.h
new file mode 100644
index 0000000..9bf395e
--- /dev/null
+++ b/aos/common/controls/control_loop-tmpl.h
@@ -0,0 +1,130 @@
+#include <stddef.h>
+#include <inttypes.h>
+
+#include "aos/common/logging/logging.h"
+#include "aos/common/messages/robot_state.q.h"
+#include "aos/common/logging/queue_logging.h"
+#include "aos/common/util/phased_loop.h"
+
+namespace aos {
+namespace controls {
+
+// TODO(aschuh): Tests.
+
+template <class T>
+constexpr ::aos::time::Time ControlLoop<T>::kStaleLogInterval;
+template <class T>
+constexpr ::aos::time::Time ControlLoop<T>::kPwmDisableTime;
+
+template <class T>
+void ControlLoop<T>::ZeroOutputs() {
+ aos::ScopedMessagePtr<OutputType> output =
+ control_loop_->output.MakeMessage();
+ Zero(output.get());
+ output.Send();
+}
+
+template <class T>
+void ControlLoop<T>::Iterate() {
+ no_goal_.Print();
+ driver_station_old_.Print();
+ no_sensor_state_.Print();
+ no_driver_station_.Print();
+ motors_off_log_.Print();
+
+ control_loop_->position.FetchAnother();
+ const PositionType *const position = control_loop_->position.get();
+ LOG_STRUCT(DEBUG, "position", *position);
+
+ // Fetch the latest control loop goal. If there is no new
+ // goal, we will just reuse the old one.
+ control_loop_->goal.FetchLatest();
+ const GoalType *goal = control_loop_->goal.get();
+ if (goal) {
+ LOG_STRUCT(DEBUG, "goal", *goal);
+ } else {
+ LOG_INTERVAL(no_goal_);
+ }
+
+ ::aos::robot_state.FetchLatest();
+ if (!::aos::robot_state.get()) {
+ LOG_INTERVAL(no_sensor_state_);
+ return;
+ }
+ if (sensor_reader_pid_ != ::aos::robot_state->reader_pid) {
+ LOG(INFO, "new sensor reader PID %" PRId32 ", old was %" PRId32 "\n",
+ ::aos::robot_state->reader_pid, sensor_reader_pid_);
+ reset_ = true;
+ sensor_reader_pid_ = ::aos::robot_state->reader_pid;
+ }
+
+ bool outputs_enabled = false;
+
+ // Check to see if we got a driver station packet recently.
+ if (::aos::joystick_state.FetchLatest()) {
+ if (::aos::joystick_state->enabled) outputs_enabled = true;
+ if (::aos::robot_state->outputs_enabled) {
+ // If the driver's station reports being disabled, we're probably not
+ // actually going to send motor values regardless of what the FPGA
+ // reports.
+ if (::aos::joystick_state->enabled) {
+ last_pwm_sent_ = ::aos::robot_state->sent_time;
+ } else {
+ LOG(WARNING, "outputs enabled while disabled\n");
+ }
+ } else if (::aos::joystick_state->enabled) {
+ LOG(WARNING, "outputs disabled while enabled\n");
+ }
+ } else if (::aos::joystick_state.IsNewerThanMS(kDSPacketTimeoutMs)) {
+ if (::aos::joystick_state->enabled) {
+ outputs_enabled = true;
+ }
+ } else {
+ if (::aos::joystick_state.get()) {
+ LOG_INTERVAL(driver_station_old_);
+ } else {
+ LOG_INTERVAL(no_driver_station_);
+ }
+ }
+
+ const bool motors_off =
+ (::aos::time::Time::Now() - last_pwm_sent_) >= kPwmDisableTime;
+ if (motors_off) {
+ if (::aos::joystick_state.get() && ::aos::joystick_state->enabled) {
+ LOG_INTERVAL(motors_off_log_);
+ }
+ outputs_enabled = false;
+ }
+
+ aos::ScopedMessagePtr<StatusType> status =
+ control_loop_->status.MakeMessage();
+ if (status.get() == nullptr) {
+ return;
+ }
+
+ if (outputs_enabled) {
+ aos::ScopedMessagePtr<OutputType> output =
+ control_loop_->output.MakeMessage();
+ RunIteration(goal, position, output.get(), status.get());
+
+ LOG_STRUCT(DEBUG, "output", *output);
+ output.Send();
+ } else {
+ // The outputs are disabled, so pass nullptr in for the output.
+ RunIteration(goal, position, nullptr, status.get());
+ ZeroOutputs();
+ }
+
+ LOG_STRUCT(DEBUG, "status", *status);
+ status.Send();
+}
+
+template <class T>
+void ControlLoop<T>::Run() {
+ while (true) {
+ Iterate();
+ }
+}
+
+} // namespace controls
+} // namespace aos
diff --git a/aos/common/controls/control_loop.cc b/aos/common/controls/control_loop.cc
new file mode 100644
index 0000000..c314254
--- /dev/null
+++ b/aos/common/controls/control_loop.cc
@@ -0,0 +1,7 @@
+#include "aos/common/controls/control_loop.h"
+
+namespace aos {
+namespace controls {
+
+} // namespace controls
+} // namespace aos
diff --git a/aos/common/controls/control_loop.h b/aos/common/controls/control_loop.h
new file mode 100644
index 0000000..d56e5d8
--- /dev/null
+++ b/aos/common/controls/control_loop.h
@@ -0,0 +1,176 @@
+#ifndef AOS_CONTROL_LOOP_CONTROL_LOOP_H_
+#define AOS_CONTROL_LOOP_CONTROL_LOOP_H_
+
+#include <string.h>
+
+#include "aos/common/type_traits.h"
+#include "aos/common/queue.h"
+#include "aos/common/time.h"
+#include "aos/common/util/log_interval.h"
+
+namespace aos {
+namespace controls {
+
+// Interface to describe runnable jobs.
+class Runnable {
+ public:
+ virtual ~Runnable() {}
+ // Runs forever.
+ virtual void Run() = 0;
+ // Does one quick piece of work and return. Does _not_ block.
+ virtual void Iterate() = 0;
+};
+
+class SerializableControlLoop : public Runnable {
+ public:
+ // Returns the size of all the data to be sent when serialized.
+ virtual size_t SeralizedSize() = 0;
+ // Serialize the current data.
+ virtual void Serialize(char *buffer) const = 0;
+ // Serialize zeroed data in case the data is out of date.
+ virtual void SerializeZeroMessage(char *buffer) const = 0;
+ // Deserialize data into the control loop.
+ virtual void Deserialize(const char *buffer) = 0;
+ // Unique identifier for the control loop.
+ // Most likely the hash of the queue group.
+ virtual uint32_t UniqueID() = 0;
+};
+
+// Control loops run this often, "starting" at time 0.
+constexpr time::Time kLoopFrequency = time::Time::InSeconds(0.005);
+
+// Provides helper methods to assist in writing control loops.
+// This template expects to be constructed with a queue group as an argument
+// that has a goal, position, status, and output queue.
+// It will then call the RunIteration method every cycle that it has enough
+// valid data for the control loop to run.
+template <class T>
+class ControlLoop : public SerializableControlLoop {
+ public:
+ // Create some convenient typedefs to reference the Goal, Position, Status,
+ // and Output structures.
+ typedef typename std::remove_reference<
+ decltype(*(static_cast<T *>(NULL)->goal.MakeMessage().get()))>::type
+ GoalType;
+ typedef typename std::remove_reference<
+ decltype(*(static_cast<T *>(NULL)->position.MakeMessage().get()))>::type
+ PositionType;
+ typedef typename std::remove_reference<
+ decltype(*(static_cast<T *>(NULL)->status.MakeMessage().get()))>::type
+ StatusType;
+ typedef typename std::remove_reference<
+ decltype(*(static_cast<T *>(NULL)->output.MakeMessage().get()))>::type
+ OutputType;
+
+ ControlLoop(T *control_loop) : control_loop_(control_loop) {}
+
+ // Returns true if all the counters etc in the sensor data have been reset.
+ // This will return true only a single time per reset.
+ bool WasReset() {
+ if (reset_) {
+ reset_ = false;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ // Constructs and sends a message on the output queue which sets everything to
+ // a safe state (generally motors off). For some subclasses, this will be a
+ // bit different (ie pistons).
+ // The implementation here creates a new Output message, calls Zero() on it,
+ // and then sends it.
+ virtual void ZeroOutputs();
+
+ // Sets the output to zero.
+ // Over-ride if a value of zero is not "off" for this subsystem.
+ virtual void Zero(OutputType *output) { output->Zero(); }
+
+ // Runs the loop forever.
+ void Run() override;
+
+ // Runs one cycle of the loop.
+ void Iterate() override;
+
+ // Returns the name of the queue group.
+ const char *name() { return control_loop_->name(); }
+
+ // Methods to serialize all the data that should be sent over the network.
+ size_t SeralizedSize() override { return control_loop_->goal->Size(); }
+ void Serialize(char *buffer) const override {
+ control_loop_->goal->Serialize(buffer);
+ }
+ void SerializeZeroMessage(char *buffer) const override {
+ GoalType zero_goal;
+ zero_goal.Zero();
+ zero_goal.Serialize(buffer);
+ }
+
+ void Deserialize(const char *buffer) override {
+ ScopedMessagePtr<GoalType> new_msg = control_loop_->goal.MakeMessage();
+ new_msg->Deserialize(buffer);
+ new_msg.Send();
+ }
+
+ uint32_t UniqueID() override { return control_loop_->hash(); }
+
+ protected:
+ // Runs an iteration of the control loop.
+ // goal is the last goal that was sent. It might be any number of cycles old
+ // or nullptr if we haven't ever received a goal.
+ // position is the current position, or nullptr if we didn't get a position
+ // this cycle.
+ // output is the values to be sent to the motors. This is nullptr if the
+ // output is going to be ignored and set to 0.
+ // status is the status of the control loop.
+ // Both output and status should be filled in by the implementation.
+ virtual void RunIteration(const GoalType *goal,
+ const PositionType *position,
+ OutputType *output,
+ StatusType *status) = 0;
+
+ T *queue_group() { return control_loop_; }
+ const T *queue_group() const { return control_loop_; }
+
+ private:
+ static constexpr ::aos::time::Time kStaleLogInterval =
+ ::aos::time::Time::InSeconds(0.1);
+ // The amount of time after the last PWM pulse we consider motors enabled for.
+ // 100ms is the result of using an oscilliscope to look at the input and
+ // output of a Talon. The Info Sheet also lists 100ms for Talon SR, Talon SRX,
+ // and Victor SP.
+ static constexpr ::aos::time::Time kPwmDisableTime =
+ ::aos::time::Time::InMS(100);
+
+ // Maximum age of driver station packets before the loop will be disabled.
+ static const int kDSPacketTimeoutMs = 500;
+
+ // Pointer to the queue group
+ T *control_loop_;
+
+ bool reset_ = false;
+ int32_t sensor_reader_pid_ = 0;
+
+ ::aos::time::Time last_pwm_sent_{0, 0};
+
+ typedef ::aos::util::SimpleLogInterval SimpleLogInterval;
+ SimpleLogInterval no_driver_station_ =
+ SimpleLogInterval(kStaleLogInterval, ERROR,
+ "no driver station packet");
+ SimpleLogInterval driver_station_old_ =
+ SimpleLogInterval(kStaleLogInterval, ERROR,
+ "driver station packet is too old");
+ SimpleLogInterval no_sensor_state_ =
+ SimpleLogInterval(kStaleLogInterval, ERROR, "no sensor state");
+ SimpleLogInterval motors_off_log_ =
+ SimpleLogInterval(kStaleLogInterval, WARNING, "motors disabled");
+ SimpleLogInterval no_goal_ =
+ SimpleLogInterval(kStaleLogInterval, ERROR, "no goal");
+};
+
+} // namespace controls
+} // namespace aos
+
+#include "aos/common/controls/control_loop-tmpl.h" // IWYU pragma: export
+
+#endif
diff --git a/aos/common/controls/control_loop_test.cc b/aos/common/controls/control_loop_test.cc
new file mode 100644
index 0000000..fd461b6
--- /dev/null
+++ b/aos/common/controls/control_loop_test.cc
@@ -0,0 +1,59 @@
+#include "aos/common/controls/control_loop_test.h"
+
+#include "aos/common/messages/robot_state.q.h"
+
+namespace aos {
+namespace testing {
+
+constexpr ::aos::time::Time ControlLoopTest::kTimeTick;
+constexpr ::aos::time::Time ControlLoopTest::kDSPacketTime;
+
+ControlLoopTest::ControlLoopTest() {
+ ::aos::joystick_state.Clear();
+ ::aos::robot_state.Clear();
+
+ ::aos::time::Time::EnableMockTime(current_time_);
+
+ SendMessages(false);
+}
+
+ControlLoopTest::~ControlLoopTest() {
+ ::aos::joystick_state.Clear();
+ ::aos::robot_state.Clear();
+
+ ::aos::time::Time::DisableMockTime();
+}
+
+void ControlLoopTest::SendMessages(bool enabled) {
+ if (current_time_ - last_ds_time_ >= kDSPacketTime) {
+ last_ds_time_ = current_time_;
+ auto new_state = ::aos::joystick_state.MakeMessage();
+ new_state->fake = true;
+
+ new_state->enabled = enabled;
+ new_state->autonomous = false;
+ new_state->team_id = team_id_;
+
+ new_state.Send();
+ }
+
+ {
+ auto new_state = ::aos::robot_state.MakeMessage();
+
+ new_state->reader_pid = reader_pid_;
+ new_state->outputs_enabled = enabled;
+ new_state->browned_out = false;
+
+ new_state->is_3v3_active = true;
+ new_state->is_5v_active = true;
+ new_state->voltage_3v3 = 3.3;
+ new_state->voltage_5v = 5.0;
+
+ new_state->voltage_roborio_in = 12.4;
+
+ new_state.Send();
+ }
+}
+
+} // namespace testing
+} // namespace aos
diff --git a/aos/common/controls/control_loop_test.h b/aos/common/controls/control_loop_test.h
new file mode 100644
index 0000000..4fd13a3
--- /dev/null
+++ b/aos/common/controls/control_loop_test.h
@@ -0,0 +1,61 @@
+#ifndef AOS_COMMON_CONTROLS_CONTROL_LOOP_TEST_H_
+#define AOS_COMMON_CONTROLS_CONTROL_LOOP_TEST_H_
+
+#include "gtest/gtest.h"
+
+#include "aos/common/queue_testutils.h"
+#include "aos/common/time.h"
+
+namespace aos {
+namespace testing {
+
+// Handles setting up the environment that all control loops need to actually
+// run.
+// This includes sending the queue messages and Clear()ing the queues when
+// appropriate.
+// It also includes dealing with ::aos::time.
+class ControlLoopTest : public ::testing::Test {
+ public:
+ ControlLoopTest();
+ virtual ~ControlLoopTest();
+
+ void set_team_id(uint16_t team_id) { team_id_ = team_id; }
+ uint16_t team_id() const { return team_id_; }
+
+ // Sends out all of the required queue messages.
+ void SendMessages(bool enabled);
+ // Ticks time for a single control loop cycle.
+ void TickTime() {
+ ::aos::time::Time::SetMockTime(current_time_ += kTimeTick);
+ }
+
+ // Simulates everything that happens during 1 loop time step.
+ void SimulateTimestep(bool enabled) {
+ SendMessages(enabled);
+ TickTime();
+ }
+
+ // Simulate a reset of the process reading sensors, which tells loops that all
+ // index counts etc will be reset.
+ void SimulateSensorReset() {
+ ++reader_pid_;
+ }
+
+ private:
+ static constexpr ::aos::time::Time kTimeTick = ::aos::time::Time::InUS(5000);
+ static constexpr ::aos::time::Time kDSPacketTime =
+ ::aos::time::Time::InMS(20);
+
+ uint16_t team_id_ = 971;
+ int32_t reader_pid_ = 1;
+
+ ::aos::time::Time last_ds_time_ = ::aos::time::Time::InSeconds(0);
+ ::aos::time::Time current_time_ = ::aos::time::Time::InSeconds(0);
+
+ ::aos::common::testing::GlobalCoreInstance my_core;
+};
+
+} // namespace testing
+} // namespace aos
+
+#endif // AOS_COMMON_CONTROLS_CONTROL_LOOP_TEST_H_
diff --git a/aos/common/controls/control_loops.q b/aos/common/controls/control_loops.q
new file mode 100644
index 0000000..2b0bfb1
--- /dev/null
+++ b/aos/common/controls/control_loops.q
@@ -0,0 +1,34 @@
+package aos.control_loops;
+
+interface ControlLoop {
+ queue goal;
+ queue position;
+ queue output;
+ queue status;
+};
+
+message Goal {
+ double goal;
+};
+
+message Position {
+ double position;
+};
+
+message Output {
+ double voltage;
+};
+
+message Status {
+ bool done;
+};
+
+// Single Input Single Output control loop.
+queue_group SISO {
+ implements ControlLoop;
+
+ queue Goal goal;
+ queue Position position;
+ queue Output output;
+ queue Status status;
+};
diff --git a/aos/common/controls/controls.gyp b/aos/common/controls/controls.gyp
new file mode 100644
index 0000000..4e0bc79
--- /dev/null
+++ b/aos/common/controls/controls.gyp
@@ -0,0 +1,94 @@
+{
+ 'targets': [
+ {
+ 'target_name': 'replay_control_loop',
+ 'type': 'static_library',
+ 'sources': [
+ #'replay_control_loop.h',
+ ],
+ 'dependencies': [
+ '<(AOS)/common/common.gyp:queues',
+ 'control_loop',
+ '<(AOS)/linux_code/logging/logging.gyp:log_replay',
+ '<(AOS)/common/logging/logging.gyp:queue_logging',
+ '<(AOS)/common/common.gyp:time',
+ ],
+ 'export_dependent_settings': [
+ '<(AOS)/common/common.gyp:queues',
+ 'control_loop',
+ '<(AOS)/linux_code/logging/logging.gyp:log_replay',
+ '<(AOS)/common/logging/logging.gyp:queue_logging',
+ '<(AOS)/common/common.gyp:time',
+ ],
+ },
+ {
+ 'target_name': 'control_loop_test',
+ 'type': 'static_library',
+ 'sources': [
+ 'control_loop_test.cc',
+ ],
+ 'dependencies': [
+ '<(AOS)/common/common.gyp:time',
+ '<(AOS)/common/messages/messages.gyp:robot_state',
+ '<(EXTERNALS):gtest',
+ '<(AOS)/common/common.gyp:queue_testutils',
+ ],
+ 'export_dependent_settings': [
+ '<(EXTERNALS):gtest',
+ '<(AOS)/common/common.gyp:time',
+ '<(AOS)/common/common.gyp:queue_testutils',
+ ],
+ },
+ {
+ 'target_name': 'polytope',
+ 'type': 'static_library',
+ 'sources': [
+ #'polytope.h',
+ ],
+ 'dependencies': [
+ '<(EXTERNALS):eigen',
+ '<(EXTERNALS):libcdd',
+ ],
+ 'export_dependent_settings': [
+ '<(EXTERNALS):eigen',
+ '<(EXTERNALS):libcdd',
+ ],
+ },
+ {
+ 'target_name': 'control_loop_queues',
+ 'type': 'static_library',
+ 'sources': [ '<(AOS)/common/controls/control_loops.q' ],
+ 'variables': {
+ 'header_path': 'aos/common/controls',
+ },
+ 'includes': ['../../../aos/build/queues.gypi'],
+ },
+ {
+ 'target_name': 'control_loop',
+ 'type': 'static_library',
+ 'sources': [
+ 'control_loop.cc',
+ ],
+ 'dependencies': [
+ '<(AOS)/common/messages/messages.gyp:robot_state',
+ '<(AOS)/build/aos.gyp:logging',
+ '<(AOS)/common/util/util.gyp:phased_loop',
+ '<(AOS)/common/common.gyp:time',
+ 'control_loop_queues',
+ '<(AOS)/common/logging/logging.gyp:queue_logging',
+ '<(AOS)/common/util/util.gyp:log_interval',
+ '<(AOS)/common/common.gyp:queues',
+ ],
+ 'export_dependent_settings': [
+ '<(AOS)/common/messages/messages.gyp:robot_state',
+ '<(AOS)/build/aos.gyp:logging',
+ '<(AOS)/common/util/util.gyp:phased_loop',
+ '<(AOS)/common/common.gyp:time',
+ 'control_loop_queues',
+ '<(AOS)/common/logging/logging.gyp:queue_logging',
+ '<(AOS)/common/util/util.gyp:log_interval',
+ '<(AOS)/common/common.gyp:queues',
+ ],
+ },
+ ],
+}
diff --git a/aos/common/controls/polytope.h b/aos/common/controls/polytope.h
new file mode 100644
index 0000000..e37346e
--- /dev/null
+++ b/aos/common/controls/polytope.h
@@ -0,0 +1,130 @@
+#ifndef AOS_COMMON_CONTROLS_POLYTOPE_H_
+#define AOS_COMMON_CONTROLS_POLYTOPE_H_
+
+#include "Eigen/Dense"
+#include "libcdd-094g-prefix/include/setoper.h"
+#include "libcdd-094g-prefix/include/cdd.h"
+
+namespace aos {
+namespace controls {
+
+// A n dimension polytope.
+template <int number_of_dimensions>
+class HPolytope {
+ public:
+ // Constructs a polytope given the H and k matricies.
+ HPolytope(Eigen::Matrix<double, Eigen::Dynamic, number_of_dimensions> H,
+ Eigen::Matrix<double, Eigen::Dynamic, 1> k)
+ : H_(H),
+ k_(k) {
+ }
+
+ // This is an initialization function shared across all instantiations of this
+ // template.
+ // This must be called at least once before calling any of the methods. It is
+ // not thread-safe.
+ static void Init() {
+ dd_set_global_constants();
+ }
+
+ // Returns a reference to H.
+ const Eigen::Matrix<double, Eigen::Dynamic,
+ number_of_dimensions> &H() const {
+ return H_;
+ }
+
+ // Returns a reference to k.
+ const Eigen::Matrix<double, Eigen::Dynamic,
+ 1> &k() const {
+ return k_;
+ }
+
+ // Returns the number of dimensions in the polytope.
+ int ndim() const { return number_of_dimensions; }
+
+ // Returns the number of constraints currently in the polytope.
+ int num_constraints() const { return k_.rows(); }
+
+ // Returns true if the point is inside the polytope.
+ bool IsInside(Eigen::Matrix<double, number_of_dimensions, 1> point) const;
+
+ // Returns the list of vertices inside the polytope.
+ Eigen::Matrix<double, number_of_dimensions, Eigen::Dynamic> Vertices() const;
+
+ private:
+ Eigen::Matrix<double, Eigen::Dynamic, number_of_dimensions> H_;
+ Eigen::Matrix<double, Eigen::Dynamic, 1> k_;
+};
+
+template <int number_of_dimensions>
+bool HPolytope<number_of_dimensions>::IsInside(
+ Eigen::Matrix<double, number_of_dimensions, 1> point) const {
+ auto ev = H_ * point;
+ for (int i = 0; i < num_constraints(); ++i) {
+ if (ev(i, 0) > k_(i, 0)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+template <int number_of_dimensions>
+Eigen::Matrix<double, number_of_dimensions, Eigen::Dynamic>
+ HPolytope<number_of_dimensions>::Vertices() const {
+ dd_MatrixPtr matrix = dd_CreateMatrix(num_constraints(), ndim() + 1);
+
+ // Copy the data over. TODO(aschuh): Is there a better way? I hate copying...
+ for (int i = 0; i < num_constraints(); ++i) {
+ dd_set_d(matrix->matrix[i][0], k_(i, 0));
+ for (int j = 0; j < ndim(); ++j) {
+ dd_set_d(matrix->matrix[i][j + 1], -H_(i, j));
+ }
+ }
+
+ matrix->representation = dd_Inequality;
+ matrix->numbtype = dd_Real;
+
+ dd_ErrorType error;
+ dd_PolyhedraPtr polyhedra = dd_DDMatrix2Poly(matrix, &error);
+ if (error != dd_NoError || polyhedra == NULL) {
+ dd_WriteErrorMessages(stderr, error);
+ dd_FreeMatrix(matrix);
+ Eigen::Matrix<double, number_of_dimensions, Eigen::Dynamic> ans(0, 0);
+ return ans;
+ }
+
+ dd_MatrixPtr vertex_matrix = dd_CopyGenerators(polyhedra);
+
+ int num_vertices = 0;
+ int num_rays = 0;
+ for (int i = 0; i < vertex_matrix->rowsize; ++i) {
+ if (dd_get_d(vertex_matrix->matrix[i][0]) == 0) {
+ num_rays += 1;
+ } else {
+ num_vertices += 1;
+ }
+ }
+
+ Eigen::Matrix<double, number_of_dimensions, Eigen::Dynamic> vertices(
+ number_of_dimensions, num_vertices);
+
+ int vertex_index = 0;
+ for (int i = 0; i < vertex_matrix->rowsize; ++i) {
+ if (dd_get_d(vertex_matrix->matrix[i][0]) != 0) {
+ for (int j = 0; j < number_of_dimensions; ++j) {
+ vertices(j, vertex_index) = dd_get_d(vertex_matrix->matrix[i][j + 1]);
+ }
+ ++vertex_index;
+ }
+ }
+ dd_FreeMatrix(vertex_matrix);
+ dd_FreePolyhedra(polyhedra);
+ dd_FreeMatrix(matrix);
+
+ return vertices;
+}
+
+} // namespace controls
+} // namespace aos
+
+#endif // AOS_COMMON_CONTROLS_POLYTOPE_H_
diff --git a/aos/common/controls/replay_control_loop.h b/aos/common/controls/replay_control_loop.h
new file mode 100644
index 0000000..e258267
--- /dev/null
+++ b/aos/common/controls/replay_control_loop.h
@@ -0,0 +1,185 @@
+#ifndef AOS_COMMON_CONTROLS_REPLAY_CONTROL_LOOP_H_
+#define AOS_COMMON_CONTROLS_REPLAY_CONTROL_LOOP_H_
+
+#include <fcntl.h>
+
+#include "aos/common/queue.h"
+#include "aos/common/controls/control_loop.h"
+#include "aos/linux_code/logging/log_replay.h"
+#include "aos/common/logging/queue_logging.h"
+#include "aos/common/time.h"
+#include "aos/common/macros.h"
+
+namespace aos {
+namespace controls {
+
+// Handles reading logged messages and running them through a control loop
+// again.
+// T should be a queue group suitable for use with ControlLoop.
+template <class T>
+class ControlLoopReplayer {
+ public:
+ typedef typename ControlLoop<T>::GoalType GoalType;
+ typedef typename ControlLoop<T>::PositionType PositionType;
+ typedef typename ControlLoop<T>::StatusType StatusType;
+ typedef typename ControlLoop<T>::OutputType OutputType;
+
+ // loop_group is where to send the messages out.
+ // process_name is the name of the process which wrote the log messages in the
+ // file(s).
+ ControlLoopReplayer(T *loop_group, const ::std::string &process_name)
+ : loop_group_(loop_group) {
+ // Clear out any old messages which will confuse the code.
+ loop_group_->status.FetchLatest();
+ loop_group_->output.FetchLatest();
+
+ replayer_.AddDirectQueueSender("wpilib_interface.DSReader",
+ "joystick_state", ::aos::joystick_state);
+ replayer_.AddDirectQueueSender("wpilib_interface.SensorReader",
+ "robot_state", ::aos::robot_state);
+
+ replayer_.AddHandler(
+ process_name, "position",
+ ::std::function<void(const PositionType &)>(::std::ref(position_)));
+ replayer_.AddHandler(
+ process_name, "output",
+ ::std::function<void(const OutputType &)>(::std::ref(output_)));
+ replayer_.AddHandler(
+ process_name, "status",
+ ::std::function<void(const StatusType &)>(::std::ref(status_)));
+ // The timing of goal messages doesn't matter, and we don't need to look
+ // back at them after running the loop.
+ replayer_.AddDirectQueueSender(process_name, "goal", loop_group_->goal);
+ }
+
+ // Replays messages from a file.
+ // filename can be straight from the command line; all sanity checking etc is
+ // handled by this function.
+ void ProcessFile(const char *filename);
+
+ private:
+ // A message handler which saves off messages and records whether it currently
+ // has a new one or not.
+ template <class S>
+ class CaptureMessage {
+ public:
+ CaptureMessage() {}
+
+ void operator()(const S &message) {
+ CHECK(!have_new_message_);
+ saved_message_ = message;
+ have_new_message_ = true;
+ }
+
+ const S &saved_message() const { return saved_message_; }
+ bool have_new_message() const { return have_new_message_; }
+ void clear_new_message() { have_new_message_ = false; }
+
+ private:
+ S saved_message_;
+ bool have_new_message_ = false;
+
+ DISALLOW_COPY_AND_ASSIGN(CaptureMessage);
+ };
+
+ // Runs through the file currently loaded in replayer_.
+ // Returns after going through the entire file.
+ void DoProcessFile();
+
+ T *const loop_group_;
+
+ CaptureMessage<PositionType> position_;
+ CaptureMessage<OutputType> output_;
+ CaptureMessage<StatusType> status_;
+
+ // The output that the loop sends for ZeroOutputs(). It might not actually be
+ // all fields zeroed, so we pick the first one and remember it to compare.
+ CaptureMessage<OutputType> zero_output_;
+
+ ::aos::logging::linux_code::LogReplayer replayer_;
+};
+
+template <class T>
+void ControlLoopReplayer<T>::ProcessFile(const char *filename) {
+ int fd;
+ if (strcmp(filename, "-") == 0) {
+ fd = STDIN_FILENO;
+ } else {
+ fd = open(filename, O_RDONLY);
+ }
+ if (fd == -1) {
+ PLOG(FATAL, "couldn't open file '%s' for reading", filename);
+ }
+
+ replayer_.OpenFile(fd);
+ DoProcessFile();
+ replayer_.CloseCurrentFile();
+
+ PCHECK(close(fd));
+}
+
+template <class T>
+void ControlLoopReplayer<T>::DoProcessFile() {
+ while (true) {
+ // Dig through messages until we get a status, which indicates the end of
+ // the control loop cycle.
+ while (!status_.have_new_message()) {
+ if (replayer_.ProcessMessage()) return;
+ }
+
+ // Send out the position message (after adjusting the time offset) so the
+ // loop will run right now.
+ if (!position_.have_new_message()) {
+ LOG(WARNING, "don't have a new position this cycle -> skipping\n");
+ status_.clear_new_message();
+ position_.clear_new_message();
+ output_.clear_new_message();
+ continue;
+ }
+ ::aos::time::OffsetToNow(position_.saved_message().sent_time);
+ {
+ auto position_message = loop_group_->position.MakeMessage();
+ *position_message = position_.saved_message();
+ CHECK(position_message.Send());
+ }
+ position_.clear_new_message();
+
+ // Wait for the loop to finish running.
+ loop_group_->status.FetchNextBlocking();
+
+ // Point out if the status is different.
+ if (!loop_group_->status->EqualsNoTime(status_.saved_message())) {
+ LOG_STRUCT(WARNING, "expected status", status_.saved_message());
+ LOG_STRUCT(WARNING, "got status", *loop_group_->status);
+ }
+ status_.clear_new_message();
+
+ // Point out if the output is different. This is a lot more complicated than
+ // for the status because there isn't always an output logged.
+ bool loop_new_output = loop_group_->output.FetchLatest();
+ if (output_.have_new_message()) {
+ if (!loop_new_output) {
+ LOG_STRUCT(WARNING, "no output, expected", output_.saved_message());
+ } else if (!loop_group_->output->EqualsNoTime(output_.saved_message())) {
+ LOG_STRUCT(WARNING, "expected output", output_.saved_message());
+ LOG_STRUCT(WARNING, "got output", *loop_group_->output);
+ }
+ } else if (loop_new_output) {
+ if (zero_output_.have_new_message()) {
+ if (!loop_group_->output->EqualsNoTime(zero_output_.saved_message())) {
+ LOG_STRUCT(WARNING, "expected null output",
+ zero_output_.saved_message());
+ LOG_STRUCT(WARNING, "got output", *loop_group_->output);
+ }
+ } else {
+ zero_output_(*loop_group_->output);
+ }
+ }
+ output_.clear_new_message();
+ }
+}
+
+} // namespace controls
+} // namespace aos
+
+#endif // AOS_COMMON_CONTROLS_REPLAY_CONTROL_LOOP_H_
diff --git a/aos/common/debugging-tips.txt b/aos/common/debugging-tips.txt
new file mode 100644
index 0000000..b9a50f3
--- /dev/null
+++ b/aos/common/debugging-tips.txt
@@ -0,0 +1,45 @@
+[General]
+Check the logs! Very few things will fail without putting something in the logs.
+ If they do, that is a bug unless our code is never getting run (there are
+ innumerable ways that could happen, but it generally doesn't).
+ To check the logs, run `log_displayer` if `binary_log_writer` has been started
+ or just run `log_streamer` if you want to do simple testing without writing
+ logs to a file. See `log_displayer --help` for options.
+All of the binaries get put in the same place. That is
+ src/outputs/prime/outputs on the build machine and
+ /home/driver/robot_code/bin on linux.
+
+[Startup]
+Low level startup errors often end up in
+ /tmp/aos_fatal_error.* under linux. Also helpful are the /tmp/starter*_std*
+ files (if the standard start scripts are being used).
+ If lots of the /tmp/starter_*std* files (with different numbers) are being
+ created, that means that starter_exe is dying constantly.
+
+[Anything Not Running]
+Check starter_exe's log messages. They might mention that it is constantly dying
+ on startup and being restarted.
+
+[Control Loop(s) Not Working]
+Are robot_state messages going out? An aos::JoystickInput (often
+ joystick_reader) should be sending them.
+ Also, kFakeJoysticks in aos/prime/input/joystick_reader.cc has to be set to
+ false in order for anything to get output.
+Is it being fed goal messages?
+Is it getting position messages?
+Is something looking at the output and doing something useful with it?
+
+[No Driver Station Packets]
+TODO(brians): This is out of date.
+Check to make sure that JSR (a cRIO-side task) is saying that it's sending
+ messages. Also check JoystickReader (or whatever inherits from
+ aos::JoystickInput) is running and see if it's receiving anything.
+
+[Attaching a Debugger]
+Attaching GDB to an existing robot code process works like normal (you have to
+ su root first because that's what all of the code currently runs as).
+If a process is dying repeatedly, the easiest way to debug it is to kill
+ starter_loop.sh and (it has to be after that) starter_exe. After doing that,
+ NOTHING will get restarted (including the normal restart after changing a
+ binary) so that you can start a process under GDB like usual (shouldn't need
+ anything special if done as root).
diff --git a/aos/common/die.cc b/aos/common/die.cc
new file mode 100644
index 0000000..69b2aef
--- /dev/null
+++ b/aos/common/die.cc
@@ -0,0 +1,85 @@
+#include "aos/common/die.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <string.h>
+#include <signal.h>
+#include <stdint.h>
+
+#include <string>
+#include <atomic>
+
+namespace aos {
+
+void Die(const char *format, ...) {
+ va_list args;
+ va_start(args, format);
+ VDie(format, args);
+ // va_end(args) // not because VDie never returns
+}
+
+namespace {
+
+// Calculates the filename to dump the message into.
+const std::string GetFilename() {
+#ifdef __VXWORKS__
+ const char *name = taskName(0); // get the name of this task
+ if (name == NULL) name = "<unknown>";
+ const std::string first_part = "/aos_fatal_error.";
+ return first_part + std::string(name);
+#else
+ char *filename;
+ if (asprintf(&filename, "/tmp/aos_fatal_error.%jd",
+ static_cast<intmax_t>(getpid())) > 0) {
+ std::string r(filename);
+ free(filename);
+ return r;
+ } else {
+ fprintf(stderr, "aos fatal: asprintf(%p, \"thingie with %%jd\", %jd)"
+ " failed with %d\n", &filename,
+ static_cast<intmax_t>(getpid()), errno);
+ return std::string();
+ }
+#endif
+}
+
+::std::atomic_bool test_mode(false);
+
+} // namespace
+
+void VDie(const char *format, va_list args_in) {
+ // We don't bother va_ending either of these because we're going nowhere and
+ // vxworks has some weird bugs that sometimes show up...
+ va_list args1, args2;
+
+ fputs("aos fatal: ERROR!! details following\n", stderr);
+ va_copy(args1, args_in);
+ vfprintf(stderr, format, args1);
+ if (!test_mode.load()) {
+ fputs("aos fatal: ERROR!! see stderr for details\n", stdout);
+
+ const std::string filename = GetFilename();
+ if (!filename.empty()) {
+ FILE *error_file = fopen(filename.c_str(), "w");
+ if (error_file != NULL) {
+ va_copy(args2, args_in);
+ vfprintf(error_file, format, args2);
+ fclose(error_file);
+ } else {
+ fprintf(stderr, "aos fatal: fopen('%s', \"w\") failed with %d\n",
+ filename.c_str(), errno);
+ }
+ }
+ }
+
+ abort();
+}
+
+void SetDieTestMode(bool new_test_mode) {
+ test_mode.store(new_test_mode);
+}
+
+} // namespace aos
diff --git a/aos/common/die.h b/aos/common/die.h
new file mode 100644
index 0000000..f9ff457
--- /dev/null
+++ b/aos/common/die.h
@@ -0,0 +1,51 @@
+#ifndef AOS_COMMON_DIE_H_
+#define AOS_COMMON_DIE_H_
+
+#include <stdarg.h>
+
+#include "aos/common/macros.h"
+#include "aos/common/libc/aos_strerror.h"
+
+namespace aos {
+
+// Terminates the task/process and logs a message (without using the logging
+// framework). Designed for use in code that can't use the logging framework
+// (code that can should LOG(FATAL), which calls this).
+void Die(const char *format, ...)
+ __attribute__((noreturn))
+ __attribute__((format(GOOD_PRINTF_FORMAT_TYPE, 1, 2)));
+void VDie(const char *format, va_list args)
+ __attribute__((noreturn))
+ __attribute__((format(GOOD_PRINTF_FORMAT_TYPE, 1, 0)));
+
+
+// The same as Die except appends " because of %d (%s)" (formatted with errno
+// and aos_strerror(errno)) to the message.
+#define PDie(format, args...) \
+ do { \
+ const int error = errno; \
+ ::aos::Die(format " because of %d (%s)", ##args, error, \
+ aos_strerror(error)); \
+ } while (false)
+
+// The same as Die except appends " because of %d (%s)" (formatted with error
+// and aos_strerror(error)) to the message.
+// PCHECK is to PDie as PRCHECK is to PRDie
+//
+// Example:
+// const int ret = pthread_mutex_lock(whatever);
+// if (ret != 0) PRDie(ret, "pthread_mutex_lock(%p) failed", whatever);
+#define PRDie(error, format, args...) \
+ do { \
+ ::aos::Die(format " because of %d (%s)", ##args, error, \
+ aos_strerror(error)); \
+ } while (false)
+
+// Turns on (or off) "test mode", where (V)Die doesn't write out files and
+// doesn't print to stdout.
+// Test mode defaults to false.
+void SetDieTestMode(bool test_mode);
+
+} // namespace aos
+
+#endif // AOS_COMMON_DIE_H_
diff --git a/aos/common/die_test.cc b/aos/common/die_test.cc
new file mode 100644
index 0000000..687543c
--- /dev/null
+++ b/aos/common/die_test.cc
@@ -0,0 +1,14 @@
+#include "aos/common/die.h"
+
+#include "gtest/gtest.h"
+
+namespace aos {
+namespace testing {
+
+TEST(DieDeathTest, Works) {
+ EXPECT_EXIT(Die("str=%s num=%d\n", "hi", 5),
+ ::testing::KilledBySignal(SIGABRT), ".*str=hi num=5\n");
+}
+
+} // namespace testing
+} // namespace aos
diff --git a/aos/common/event.h b/aos/common/event.h
new file mode 100644
index 0000000..9839d41
--- /dev/null
+++ b/aos/common/event.h
@@ -0,0 +1,53 @@
+#ifndef AOS_COMMON_EVENT_H_
+#define AOS_COMMON_EVENT_H_
+
+#include "aos/linux_code/ipc_lib/aos_sync.h"
+
+namespace aos {
+
+// An abstraction of an event which is easy to implement for Linux and in other
+// environments.
+// On Linux at least, this is definitely safe for passing through C code with
+// memcpy etc.
+//
+// An event is either "set" or "unset". Any thread can transition it between
+// these two states and other threads can wait for an unset->set transition.
+// This is not a particularly powerful synchronization primitive, but it can be
+// much easier to use than more complicated ones in some situations. The name is
+// taken from Python's implementation of the same thing.
+//
+// An event is equivalent to a semaphore which is either set to 0 or infinity.
+// It is also equivalent to a condition variable with the monitored condition
+// being "is it set or not".
+//
+// IMPORTANT: You can NOT use this to successfully replace a standard condition
+// variable in most cases. When the condition being monitored changes separately
+// from the actual state of the condition variable/event, there WILL be race
+// conditions if you try to use this class.
+//
+// It is undefined behavior to destroy an Event while there are current
+// Wait()ers.
+class Event {
+ public:
+ // Creates an unset event.
+ Event();
+ // There must be no waiters when an Event is destroyed.
+ ~Event() = default;
+
+ // Waits for the event to be set. Returns immediately if it is already set.
+ void Wait();
+
+ // Wakes up all Wait()ers and sets the event (atomically).
+ void Set();
+ // Unsets the event so future Wait() callers will block instead of returning
+ // immediately.
+ // Returns true if the event was previously set.
+ bool Clear();
+
+ private:
+ aos_futex impl_;
+};
+
+} // namespace aos
+
+#endif // AOS_COMMON_EVENT_H_
diff --git a/aos/common/event_test.cc b/aos/common/event_test.cc
new file mode 100644
index 0000000..d8671eb
--- /dev/null
+++ b/aos/common/event_test.cc
@@ -0,0 +1,71 @@
+#include "aos/common/event.h"
+
+#include <thread>
+
+#include "gtest/gtest.h"
+
+#include "aos/common/queue_testutils.h"
+#include "aos/common/time.h"
+
+namespace aos {
+namespace testing {
+
+class EventTest : public ::testing::Test {
+ public:
+ Event test_event;
+
+ protected:
+ void SetUp() override {
+ ::aos::common::testing::EnableTestLogging();
+ }
+};
+
+// Makes sure that basic operations with no blocking or anything work.
+TEST_F(EventTest, Basic) {
+ EXPECT_FALSE(test_event.Clear());
+ EXPECT_FALSE(test_event.Clear());
+
+ test_event.Set();
+ test_event.Wait();
+ EXPECT_TRUE(test_event.Clear());
+ EXPECT_FALSE(test_event.Clear());
+}
+
+// Tests that tsan understands that events establish a happens-before
+// relationship.
+TEST_F(EventTest, ThreadSanitizer) {
+ for (int i = 0; i < 3000; ++i) {
+ int variable = 0;
+ test_event.Clear();
+ ::std::thread thread([this, &variable]() {
+ test_event.Wait();
+ --variable;
+ });
+ ++variable;
+ test_event.Set();
+ thread.join();
+ EXPECT_EQ(0, variable);
+ }
+}
+
+// Tests that an event blocks correctly.
+TEST_F(EventTest, Blocks) {
+ time::Time start_time, finish_time;
+ // Without this, it sometimes manages to fail under tsan.
+ Event started;
+ ::std::thread thread([this, &start_time, &finish_time, &started]() {
+ start_time = time::Time::Now();
+ started.Set();
+ test_event.Wait();
+ finish_time = time::Time::Now();
+ });
+ static const time::Time kWaitTime = time::Time::InSeconds(0.05);
+ started.Wait();
+ time::SleepFor(kWaitTime);
+ test_event.Set();
+ thread.join();
+ EXPECT_GE(finish_time - start_time, kWaitTime);
+}
+
+} // namespace testing
+} // namespace aos
diff --git a/aos/common/gtest_prod.h b/aos/common/gtest_prod.h
new file mode 100644
index 0000000..fe0b056
--- /dev/null
+++ b/aos/common/gtest_prod.h
@@ -0,0 +1,37 @@
+#ifndef AOS_COMMON_GTEST_PROD_H_
+#define AOS_COMMON_GTEST_PROD_H_
+
+#include "gtest/gtest_prod.h"
+
+// These macros replace gtest's FRIEND_TEST if the test is in a different
+// namespace than the code that needs to make it a friend.
+// Example:
+// foo.h:
+// namespace bla {
+// namespace testing {
+//
+// FORWARD_DECLARE_TEST_CASE(FooTest, Bar);
+//
+// } // namespace testing
+//
+// class Foo {
+// FRIEND_TEST_NAMESPACE(FooTest, Bar, testing);
+// };
+//
+// } // namespace bla
+// foo_test.cc:
+// namespace bla {
+// namespace testing {
+//
+// TEST(FooTest, Bar) {
+// access private members of Foo
+// }
+//
+// } // namespace testing
+// } // namespace bla
+#define FORWARD_DECLARE_TEST_CASE(test_case_name, test_name) \
+ class test_case_name##_##test_name##_Test;
+#define FRIEND_TEST_NAMESPACE(test_case_name, test_name, namespace_name) \
+ friend class namespace_name::test_case_name##_##test_name##_Test
+
+#endif // AOS_COMMON_GTEST_PROD_H_
diff --git a/aos/common/input/driver_station_data.cc b/aos/common/input/driver_station_data.cc
new file mode 100644
index 0000000..2df98be
--- /dev/null
+++ b/aos/common/input/driver_station_data.cc
@@ -0,0 +1,100 @@
+#include "aos/common/input/driver_station_data.h"
+
+namespace aos {
+namespace input {
+namespace driver_station {
+
+Data::Data() : current_values_(), old_values_() {}
+
+void Data::Update(const JoystickState &new_values) {
+ old_values_ = current_values_;
+ current_values_ = new_values;
+}
+
+namespace {
+
+bool GetButton(const ButtonLocation location,
+ const JoystickState &values) {
+ return values.joysticks[location.joystick() - 1].buttons &
+ (1 << (location.number() - 1));
+}
+
+bool DoGetPOV(const POVLocation location, const JoystickState &values) {
+ return values.joysticks[location.joystick() - 1].pov == location.number();
+}
+
+bool GetControlBitValue(const ControlBit bit,
+ const JoystickState &values) {
+ switch (bit) {
+ case ControlBit::kTestMode:
+ return values.test_mode;
+ case ControlBit::kFmsAttached:
+ return values.fms_attached;
+ case ControlBit::kAutonomous:
+ return values.autonomous;
+ case ControlBit::kEnabled:
+ return values.enabled;
+ default:
+ __builtin_unreachable();
+ }
+}
+
+} // namespace
+
+bool Data::IsPressed(const ButtonLocation location) const {
+ return GetButton(location, current_values_);
+}
+
+bool Data::PosEdge(const ButtonLocation location) const {
+ return !GetButton(location, old_values_) &&
+ GetButton(location, current_values_);
+}
+
+bool Data::NegEdge(const ButtonLocation location) const {
+ return GetButton(location, old_values_) &&
+ !GetButton(location, current_values_);
+}
+
+bool Data::IsPressed(const POVLocation location) const {
+ return DoGetPOV(location, current_values_);
+}
+
+bool Data::PosEdge(const POVLocation location) const {
+ return !DoGetPOV(location, old_values_) &&
+ DoGetPOV(location, current_values_);
+}
+
+bool Data::NegEdge(const POVLocation location) const {
+ return DoGetPOV(location, old_values_) &&
+ !DoGetPOV(location, current_values_);
+}
+
+int32_t Data::GetPOV(int joystick) const {
+ return current_values_.joysticks[joystick - 1].pov;
+}
+
+int32_t Data::GetOldPOV(int joystick) const {
+ return old_values_.joysticks[joystick - 1].pov;
+}
+
+bool Data::GetControlBit(const ControlBit bit) const {
+ return GetControlBitValue(bit, current_values_);
+}
+
+bool Data::PosEdge(const ControlBit bit) const {
+ return !GetControlBitValue(bit, old_values_) &&
+ GetControlBitValue(bit, current_values_);
+}
+
+bool Data::NegEdge(const ControlBit bit) const {
+ return GetControlBitValue(bit, old_values_) &&
+ !GetControlBitValue(bit, current_values_);
+}
+
+float Data::GetAxis(JoystickAxis axis) const {
+ return current_values_.joysticks[axis.joystick() - 1].axis[axis.number() - 1];
+}
+
+} // namespace driver_station
+} // namespace input
+} // namespace aos
diff --git a/aos/common/input/driver_station_data.h b/aos/common/input/driver_station_data.h
new file mode 100644
index 0000000..507ad61
--- /dev/null
+++ b/aos/common/input/driver_station_data.h
@@ -0,0 +1,111 @@
+#ifndef AOS_COMMON_INPUT_DRIVER_STATION_DATA_H_
+#define AOS_COMMON_INPUT_DRIVER_STATION_DATA_H_
+
+// This file defines several types to support nicely looking at the data
+// received from the driver's station.
+
+#include <memory>
+
+#include "aos/common/messages/robot_state.q.h"
+
+namespace aos {
+namespace input {
+namespace driver_station {
+
+// Represents a feature of a joystick (a button or an axis).
+// All indices are 1-based.
+class JoystickFeature {
+ public:
+ JoystickFeature(int joystick, int number)
+ : joystick_(joystick), number_(number) {}
+
+ // How many joysticks there are.
+ static const int kJoysticks = sizeof(JoystickState::joysticks) /
+ sizeof(JoystickState::joysticks[0]);
+
+ // Which joystick number this is (1-based).
+ int joystick() const { return joystick_; }
+ // Which feature on joystick() this is (1-based).
+ int number() const { return number_; }
+
+ private:
+ const int joystick_, number_;
+};
+
+// Represents the location of a button.
+// Use Data to actually get the value.
+// Safe for static initialization.
+class ButtonLocation : public JoystickFeature {
+ public:
+ ButtonLocation(int joystick, int number)
+ : JoystickFeature(joystick, number) {}
+
+ // How many buttons there are available on each joystick.
+ static const int kButtons = 12;
+};
+
+// Represents the direction of a POV on a joystick.
+// Use Data to actually get the value.
+// Safe for static initialization.
+class POVLocation : public JoystickFeature {
+ public:
+ POVLocation(int joystick, int number)
+ : JoystickFeature(joystick, number) {}
+};
+
+// Represents various bits of control information that the DS sends.
+// Use Data to actually get the value.
+enum class ControlBit {
+ kTestMode, kFmsAttached, kAutonomous, kEnabled
+};
+
+// Represents a single axis of a joystick.
+// Use Data to actually get the value.
+// Safe for static initialization.
+class JoystickAxis : public JoystickFeature {
+ public:
+ JoystickAxis(int joystick, int number)
+ : JoystickFeature(joystick, number) {}
+
+ // How many axes there are available on each joystick.
+ static const int kAxes = sizeof(Joystick::axis) /
+ sizeof(Joystick::axis[0]);
+};
+
+class Data {
+ public:
+ // Initializes the data to all buttons and control bits off and all joysticks
+ // at 0.
+ Data();
+
+ // Updates the current information with a new set of values.
+ void Update(const JoystickState &new_values);
+
+ bool IsPressed(POVLocation location) const;
+ bool PosEdge(POVLocation location) const;
+ bool NegEdge(POVLocation location) const;
+
+ // Returns the current and previous "values" for the POV.
+ int32_t GetPOV(int joystick) const;
+ int32_t GetOldPOV(int joystick) const;
+
+ bool IsPressed(ButtonLocation location) const;
+ bool PosEdge(ButtonLocation location) const;
+ bool NegEdge(ButtonLocation location) const;
+
+ bool GetControlBit(ControlBit bit) const;
+ bool PosEdge(ControlBit bit) const;
+ bool NegEdge(ControlBit bit) const;
+
+ // Returns the value in the range [-1.0, 1.0].
+ float GetAxis(JoystickAxis axis) const;
+
+ private:
+ JoystickState current_values_, old_values_;
+};
+
+} // namespace driver_station
+} // namespace input
+} // namespace aos
+
+#endif // AOS_COMMON_INPUT_DRIVER_STATION_DATA_H_
diff --git a/aos/common/input/input.gyp b/aos/common/input/input.gyp
new file mode 100644
index 0000000..5674ce7
--- /dev/null
+++ b/aos/common/input/input.gyp
@@ -0,0 +1,17 @@
+{
+ 'targets': [
+ {
+ 'target_name': 'driver_station_data',
+ 'type': 'static_library',
+ 'sources': [
+ 'driver_station_data.cc',
+ ],
+ 'dependencies': [
+ '<(AOS)/common/messages/messages.gyp:robot_state',
+ ],
+ 'export_dependent_settings': [
+ '<(AOS)/common/messages/messages.gyp:robot_state',
+ ],
+ },
+ ],
+}
diff --git a/aos/common/libc/README b/aos/common/libc/README
new file mode 100644
index 0000000..c4ee817
--- /dev/null
+++ b/aos/common/libc/README
@@ -0,0 +1,18 @@
+This directory has replacements for some libc functions that we don't want to
+use because they're not thread-safe and/or they allocate memory.
+
+Some of them are implemented as C++ functions because it makes fixing the
+memory management issues that cause the standard versions to be not thread-safe
+possible and some of them are still callable from C when it makes sense.
+
+<http://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html#tag_15_09_01>
+has a list of the not-thread-safe POSIX functions. We've gotten rid of all of
+those except for the following:
+ getenv(3): <http://austingroupbugs.net/view.php?id=188> proposes changing
+ POSIX to require it to return a pointer into environ, making it thread-safe,
+ which glibc already does.
+ inet_ntoa(3): Glibc uses a thread-local buffer, which makes it thread-safe,
+ and uses of this function should go away soon.
+ readdir(3): Glibc already guarantees that only invocations on the same
+ directory stream aren't thread-safe, and there's talk of changing POSIX to
+ say the same thing.
diff --git a/aos/common/libc/aos_strerror.cc b/aos/common/libc/aos_strerror.cc
new file mode 100644
index 0000000..2c9735d
--- /dev/null
+++ b/aos/common/libc/aos_strerror.cc
@@ -0,0 +1,41 @@
+#include "aos/common/libc/aos_strerror.h"
+
+#include <assert.h>
+#include <sys/types.h>
+#include <string.h>
+#include <stdio.h>
+
+// This code uses an overloaded function to handle the result from either
+// version of strerror_r correctly without needing a way to get the choice out
+// of the compiler/glibc/whatever explicitly.
+
+namespace {
+
+const size_t kBufferSize = 128;
+
+// Handle the result from the GNU version of strerror_r. It never fails, so
+// that's pretty easy...
+__attribute__((unused))
+char *aos_strerror_handle_result(int /*error*/, char *ret, char * /*buffer*/) {
+ return ret;
+}
+
+// Handle the result from the POSIX version of strerror_r.
+__attribute__((unused))
+char *aos_strerror_handle_result(int error, int ret, char *buffer) {
+ if (ret != 0) {
+ const int r = snprintf(buffer, kBufferSize, "Unknown error %d", error);
+ assert(r > 0);
+ }
+ return buffer;
+}
+
+} // namespace
+
+const char *aos_strerror(int error) {
+ static thread_local char buffer[kBufferSize];
+
+ // Call the overload for whichever version we're using.
+ return aos_strerror_handle_result(
+ error, strerror_r(error, buffer, sizeof(buffer)), buffer);
+}
diff --git a/aos/common/libc/aos_strerror.h b/aos/common/libc/aos_strerror.h
new file mode 100644
index 0000000..acce427
--- /dev/null
+++ b/aos/common/libc/aos_strerror.h
@@ -0,0 +1,23 @@
+#ifndef AOS_COMMON_LIBC_AOS_STRERROR_H_
+#define AOS_COMMON_LIBC_AOS_STRERROR_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Thread-safe version of strerror(3) (except it may change errno).
+//
+// Returns a pointer to static data or a thread-local buffer.
+//
+// Necessary because strerror_r(3) is such a mess (which version you get is
+// determined at compile time by black magic related to feature macro
+// definitions, compiler flags, glibc version, and even whether you're using g++
+// or clang++) and strerror_l(3) might not work if you end up with the magic
+// LC_GLOBAL_LOCALE locale.
+const char *aos_strerror(int error);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // AOS_COMMON_LIBC_AOS_STRERROR_H_
diff --git a/aos/common/libc/aos_strerror_test.cc b/aos/common/libc/aos_strerror_test.cc
new file mode 100644
index 0000000..c4574fe
--- /dev/null
+++ b/aos/common/libc/aos_strerror_test.cc
@@ -0,0 +1,29 @@
+#include "aos/common/libc/aos_strerror.h"
+
+#include <errno.h>
+
+#include "gtest/gtest.h"
+
+namespace aos {
+namespace libc {
+namespace testing {
+
+// Tries a couple of easy ones.
+TEST(StrerrorTest, Basic) {
+ EXPECT_STREQ("Argument list too long", aos_strerror(E2BIG));
+ EXPECT_STREQ("Bad file descriptor", aos_strerror(EBADF));
+ EXPECT_STREQ("Unknown error 4021", aos_strerror(4021));
+}
+
+// Runs through all errno values and makes sure it gives the same result as
+// strerror(3).
+TEST(StrerrorTest, All) {
+ for (int i = 0; i < 4095; ++i) {
+ SCOPED_TRACE("iteration " + ::std::to_string(i));
+ EXPECT_STREQ(strerror(i), aos_strerror(i));
+ }
+}
+
+} // namespace testing
+} // namespace libc
+} // namespace aos
diff --git a/aos/common/libc/aos_strsignal.cc b/aos/common/libc/aos_strsignal.cc
new file mode 100644
index 0000000..2321a07
--- /dev/null
+++ b/aos/common/libc/aos_strsignal.cc
@@ -0,0 +1,22 @@
+#include "aos/common/libc/aos_strsignal.h"
+
+#include <signal.h>
+
+#include "aos/common/logging/logging.h"
+
+const char *aos_strsignal(int signal) {
+ static thread_local char buffer[512];
+
+ if (signal >= SIGRTMIN && signal <= SIGRTMAX) {
+ CHECK(snprintf(buffer, sizeof(buffer), "Real-time signal %d",
+ signal - SIGRTMIN) > 0);
+ return buffer;
+ }
+
+ if (signal > 0 && signal < NSIG && sys_siglist[signal] != nullptr) {
+ return sys_siglist[signal];
+ }
+
+ CHECK(snprintf(buffer, sizeof(buffer), "Unknown signal %d", signal) > 0);
+ return buffer;
+}
diff --git a/aos/common/libc/aos_strsignal.h b/aos/common/libc/aos_strsignal.h
new file mode 100644
index 0000000..ef6795c
--- /dev/null
+++ b/aos/common/libc/aos_strsignal.h
@@ -0,0 +1,17 @@
+#ifndef AOS_COMMON_LIBC_AOS_STRSIGNAL_H_
+#define AOS_COMMON_LIBC_AOS_STRSIGNAL_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Thread-safe version of strsignal(3) (except it will never return NULL).
+//
+// Returns a pointer to static data or a thread-local buffer.
+const char *aos_strsignal(int signal);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // AOS_COMMON_LIBC_AOS_STRSIGNAL_H_
diff --git a/aos/common/libc/aos_strsignal_test.cc b/aos/common/libc/aos_strsignal_test.cc
new file mode 100644
index 0000000..3d90b21
--- /dev/null
+++ b/aos/common/libc/aos_strsignal_test.cc
@@ -0,0 +1,47 @@
+#include "aos/common/libc/aos_strsignal.h"
+
+#include <signal.h>
+#include <thread>
+
+#include "gtest/gtest.h"
+
+namespace aos {
+namespace libc {
+namespace testing {
+
+// Tries a couple of easy ones.
+TEST(StrsignalTest, Basic) {
+ EXPECT_STREQ("Hangup", aos_strsignal(SIGHUP));
+ EXPECT_STREQ("Broken pipe", aos_strsignal(SIGPIPE));
+ EXPECT_STREQ("Real-time signal 2", aos_strsignal(SIGRTMIN + 2));
+ EXPECT_STREQ("Unknown signal 155", aos_strsignal(155));
+}
+
+class SignalNameTester {
+ public:
+ void operator()() {
+ for (int i = 0; i < SIGRTMAX + 5; ++i) {
+ EXPECT_STREQ(strsignal(i), aos_strsignal(i));
+ }
+ }
+};
+
+// Tests that all the signals give the same result as strsignal(3).
+TEST(StrsignalTest, All) {
+ // Sigh, strsignal allocates a buffer that uses pthread local storage. This
+ // interacts poorly with asan. Spawning a thread causes the storage to get
+ // cleaned up before asan checks.
+ SignalNameTester t;
+#ifdef AOS_SANITIZER_thread
+ // tsan doesn't like this usage of ::std::thread. It looks like
+ // <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=57507>.
+ t();
+#else
+ ::std::thread thread(::std::ref(t));
+ thread.join();
+#endif
+}
+
+} // namespace testing
+} // namespace libc
+} // namespace aos
diff --git a/aos/common/libc/dirname.cc b/aos/common/libc/dirname.cc
new file mode 100644
index 0000000..c843898
--- /dev/null
+++ b/aos/common/libc/dirname.cc
@@ -0,0 +1,39 @@
+#include "aos/common/libc/dirname.h"
+
+namespace aos {
+namespace libc {
+namespace {
+
+::std::string DoDirname(const ::std::string &path, size_t last_slash) {
+ // If there aren't any other '/'s in it.
+ if (last_slash == ::std::string::npos) return ".";
+
+ // Back up as long as we see '/'s.
+ do {
+ // If we get all the way to the beginning.
+ if (last_slash == 0) return "/";
+ --last_slash;
+ } while (path[last_slash] == '/');
+
+ return path.substr(0, last_slash + 1);
+}
+
+} // namespace
+
+::std::string Dirname(const ::std::string &path) {
+ // Without this, we end up with integer underflows below, which is technically
+ // undefined.
+ if (path.size() == 0) return ".";
+
+ size_t last_slash = path.rfind('/');
+
+ // If the path ends with a '/'.
+ if (last_slash == path.size() - 1) {
+ last_slash = DoDirname(path, last_slash).rfind('/');
+ }
+
+ return DoDirname(path, last_slash);
+}
+
+} // namespace libc
+} // namespace aos
diff --git a/aos/common/libc/dirname.h b/aos/common/libc/dirname.h
new file mode 100644
index 0000000..f05f8f1
--- /dev/null
+++ b/aos/common/libc/dirname.h
@@ -0,0 +1,15 @@
+#ifndef AOS_COMMON_LIBC_DIRNAME_H_
+#define AOS_COMMON_LIBC_DIRNAME_H_
+
+#include <string>
+
+namespace aos {
+namespace libc {
+
+// Thread-safe version of dirname(3).
+::std::string Dirname(const ::std::string &path);
+
+} // namespace libc
+} // namespace aos
+
+#endif // AOS_COMMON_LIBC_DIRNAME_H_
diff --git a/aos/common/libc/dirname_test.cc b/aos/common/libc/dirname_test.cc
new file mode 100644
index 0000000..0207961
--- /dev/null
+++ b/aos/common/libc/dirname_test.cc
@@ -0,0 +1,79 @@
+#include "aos/common/libc/dirname.h"
+
+#include <libgen.h>
+
+#include "gtest/gtest.h"
+
+namespace aos {
+namespace libc {
+namespace testing {
+
+// Tests the examples from the Linux man-pages release 3.44 dirname(3).
+TEST(DirnameTest, ManPageExamples) {
+ EXPECT_EQ("/usr", Dirname("/usr/lib"));
+ EXPECT_EQ("/", Dirname("/usr/"));
+ EXPECT_EQ(".", Dirname("usr"));
+ EXPECT_EQ("/", Dirname("/"));
+ EXPECT_EQ(".", Dirname("."));
+ EXPECT_EQ(".", Dirname(".."));
+}
+
+// Tests that it handles multiple '/'s in a row correctly.
+TEST(DirnameTest, MultipleSlashes) {
+ EXPECT_EQ("//usr", Dirname("//usr//lib"));
+ EXPECT_EQ("//usr/lib", Dirname("//usr/lib//bla"));
+ EXPECT_EQ("/", Dirname("//usr//"));
+ EXPECT_EQ(".", Dirname("usr//"));
+ EXPECT_EQ("/", Dirname("//"));
+ EXPECT_EQ(".", Dirname(".//"));
+ EXPECT_EQ(".", Dirname("..//"));
+}
+
+TEST(DirnameTest, WeirdInputs) {
+ EXPECT_EQ(".", Dirname(""));
+ EXPECT_EQ(".", Dirname("..."));
+}
+
+// Runs through a bunch of randomly constructed pathnames and makes sure it
+// gives the same result as dirname(3).
+TEST(DirnameTest, Random) {
+ static const char kTestBytes[] = "a0//.. ";
+ static const size_t kTestBytesSize = sizeof(kTestBytes) - 1;
+ static const size_t kTestPathSize = 6;
+
+ ::std::string test_string(kTestPathSize, '\0');
+ char test_path[kTestPathSize + 1];
+ for (size_t i0 = 0; i0 < kTestBytesSize; ++i0) {
+ test_string[0] = kTestBytes[i0];
+ for (size_t i1 = 0; i1 < kTestBytesSize; ++i1) {
+ // dirname(3) returns "//" in this case which is weird and our Dirname
+ // doesn't.
+ if (test_string[0] == '/' && kTestBytes[i1] == '/') continue;
+
+ test_string[1] = kTestBytes[i1];
+ for (size_t i2 = 0; i2 < kTestBytesSize; ++i2) {
+ test_string[2] = kTestBytes[i2];
+ for (size_t i3 = 0; i3 < kTestBytesSize; ++i3) {
+ test_string[3] = kTestBytes[i3];
+ for (size_t i4 = 0; i4 < kTestBytesSize; ++i4) {
+ test_string[4] = kTestBytes[i4];
+ for (size_t i5 = 0; i5 < kTestBytesSize; ++i5) {
+ test_string[5] = kTestBytes[i5];
+
+ memcpy(test_path, test_string.c_str(), kTestPathSize);
+ test_path[kTestPathSize] = '\0';
+
+ SCOPED_TRACE("path is '" + test_string + "'");
+ EXPECT_EQ(::std::string(dirname(test_path)),
+ Dirname(test_string));
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+} // namespace testing
+} // namespace libc
+} // namespace aos
diff --git a/aos/common/libc/libc.gyp b/aos/common/libc/libc.gyp
new file mode 100644
index 0000000..d7fc357
--- /dev/null
+++ b/aos/common/libc/libc.gyp
@@ -0,0 +1,62 @@
+{
+ 'targets': [
+ {
+ 'target_name': 'aos_strsignal',
+ 'type': 'static_library',
+ 'sources': [
+ 'aos_strsignal.cc',
+ ],
+ 'dependencies': [
+ '<(AOS)/build/aos.gyp:logging_interface',
+ ],
+ },
+ {
+ 'target_name': 'aos_strsignal_test',
+ 'type': 'executable',
+ 'sources': [
+ 'aos_strsignal_test.cc',
+ ],
+ 'dependencies': [
+ 'aos_strsignal',
+ '<(EXTERNALS):gtest',
+ '<(AOS)/build/aos.gyp:logging',
+ ],
+ },
+ {
+ 'target_name': 'dirname',
+ 'type': 'static_library',
+ 'sources': [
+ 'dirname.cc',
+ ],
+ },
+ {
+ 'target_name': 'dirname_test',
+ 'type': 'executable',
+ 'sources': [
+ 'dirname_test.cc',
+ ],
+ 'dependencies': [
+ 'dirname',
+ '<(EXTERNALS):gtest',
+ ],
+ },
+ {
+ 'target_name': 'aos_strerror',
+ 'type': 'static_library',
+ 'sources': [
+ 'aos_strerror.cc',
+ ],
+ },
+ {
+ 'target_name': 'aos_strerror_test',
+ 'type': 'executable',
+ 'sources': [
+ 'aos_strerror_test.cc',
+ ],
+ 'dependencies': [
+ 'aos_strerror',
+ '<(EXTERNALS):gtest',
+ ],
+ },
+ ],
+}
diff --git a/aos/common/logging/logging.gyp b/aos/common/logging/logging.gyp
new file mode 100644
index 0000000..36d58b7
--- /dev/null
+++ b/aos/common/logging/logging.gyp
@@ -0,0 +1,50 @@
+{
+ 'targets': [
+ {
+ 'target_name': 'logging_impl_test',
+ 'type': 'executable',
+ 'sources': [
+ 'logging_impl_test.cc',
+ ],
+ 'dependencies': [
+ '<(EXTERNALS):gtest',
+ '<(AOS)/build/aos.gyp:logging',
+ ],
+ },
+ {
+ 'target_name': 'queue_logging',
+ 'type': 'static_library',
+ 'sources': [
+ 'queue_logging.cc',
+ ],
+ 'dependencies': [
+ '<(AOS)/build/aos.gyp:logging',
+ '<(AOS)/common/common.gyp:die',
+ '<(AOS)/common/common.gyp:queue_types',
+ ],
+ 'export_dependent_settings': [
+ '<(AOS)/build/aos.gyp:logging',
+ '<(AOS)/common/common.gyp:die',
+ ],
+ },
+ {
+ 'target_name': 'matrix_logging',
+ 'type': 'static_library',
+ 'sources': [
+ 'matrix_logging.cc',
+ ],
+ 'dependencies': [
+ '<(AOS)/build/aos.gyp:logging',
+ '<(AOS)/common/common.gyp:die',
+ '<(AOS)/common/common.gyp:queue_types',
+ '<(EXTERNALS):eigen',
+ ],
+ 'export_dependent_settings': [
+ '<(AOS)/build/aos.gyp:logging',
+ '<(AOS)/common/common.gyp:die',
+ '<(AOS)/common/common.gyp:queue_types',
+ '<(EXTERNALS):eigen',
+ ],
+ },
+ ],
+}
diff --git a/aos/common/logging/logging.h b/aos/common/logging/logging.h
new file mode 100644
index 0000000..66b88c7
--- /dev/null
+++ b/aos/common/logging/logging.h
@@ -0,0 +1,261 @@
+#ifndef AOS_COMMON_LOGGING_LOGGING_H_
+#define AOS_COMMON_LOGGING_LOGGING_H_
+
+// This file contains the logging client interface. It works with both C and C++
+// code.
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include "aos/common/macros.h"
+#include "aos/common/libc/aos_strerror.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef uint8_t log_level;
+
+#define DECL_LEVELS \
+DECL_LEVEL(DEBUG, 0); /* stuff that gets printed out every cycle */ \
+DECL_LEVEL(INFO, 1); /* things like PosEdge/NegEdge */ \
+/* things that might still work if they happen occasionally */ \
+DECL_LEVEL(WARNING, 2); \
+/*-1 so that vxworks macro of same name will have same effect if used*/ \
+DECL_LEVEL(ERROR, -1); /* errors */ \
+/* serious errors. the logging code will terminate the process/task */ \
+DECL_LEVEL(FATAL, 4); \
+DECL_LEVEL(LOG_UNKNOWN, 5); /* unknown logging level */
+#define DECL_LEVEL(name, value) static const log_level name = value;
+DECL_LEVELS;
+#undef DECL_LEVEL
+
+// Not static const size_t for C code.
+#define LOG_MESSAGE_LEN 400
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Actually implements the basic logging call.
+// Does not check that level is valid.
+void log_do(log_level level, const char *format, ...)
+ __attribute__((format(GOOD_PRINTF_FORMAT_TYPE, 2, 3)));
+
+void log_cork(int line, const char *function, const char *format, ...)
+ __attribute__((format(GOOD_PRINTF_FORMAT_TYPE, 3, 4)));
+// Implements the uncork logging call.
+void log_uncork(int line, const char *function, log_level level,
+ const char *file, const char *format, ...)
+ __attribute__((format(GOOD_PRINTF_FORMAT_TYPE, 5, 6)));
+
+#ifdef __cplusplus
+}
+#endif
+
+// A magical static const char[] or string literal that communicates the name
+// of the enclosing function.
+// It's currently using __PRETTY_FUNCTION__ because both GCC and Clang support
+// that and it gives nicer results in C++ than the standard __func__ (which
+// would also work).
+//#define LOG_CURRENT_FUNCTION __PRETTY_FUNCTION__
+#define LOG_CURRENT_FUNCTION __func__
+
+#define LOG_SOURCENAME __FILE__
+
+// The basic logging call.
+#define LOG(level, format, args...) \
+ do { \
+ log_do(level, LOG_SOURCENAME ": " STRINGIFY(__LINE__) ": %s: " format, \
+ LOG_CURRENT_FUNCTION, ##args); \
+ /* so that GCC knows that it won't return */ \
+ if (level == FATAL) { \
+ fprintf(stderr, "log_do(FATAL) fell through!!!!!\n"); \
+ printf("see stderr\n"); \
+ abort(); \
+ } \
+ } while (0)
+
+// Same as LOG except appends " due to %d (%s)\n" (formatted with errno and
+// aos_strerror(errno)) to the message.
+#define PLOG(level, format, args...) PELOG(level, errno, format, ##args)
+
+// Like PLOG except allows specifying an error other than errno.
+#define PELOG(level, error_in, format, args...) \
+ do { \
+ const int error = error_in; \
+ LOG(level, format " due to %d (%s)\n", ##args, error, \
+ aos_strerror(error)); \
+ } while (0);
+
+// Allows format to not be a string constant.
+#define LOG_DYNAMIC(level, format, args...) \
+ do { \
+ static char log_buf[LOG_MESSAGE_LEN]; \
+ int ret = snprintf(log_buf, sizeof(log_buf), format, ##args); \
+ if (ret < 0 || (uintmax_t)ret >= LOG_MESSAGE_LEN) { \
+ LOG(ERROR, "next message was too long so not subbing in args\n"); \
+ LOG(level, "%s", format); \
+ } else { \
+ LOG(level, "%s", log_buf); \
+ } \
+ } while (0)
+
+// Allows "bottling up" multiple log fragments which can then all be logged in
+// one message with LOG_UNCORK.
+// Calls from a given thread/task will be grouped together.
+#define LOG_CORK(format, args...) \
+ do { \
+ log_cork(__LINE__, LOG_CURRENT_FUNCTION, format, ##args); \
+ } while (0)
+// Actually logs all of the saved up log fragments (including format and args on
+// the end).
+#define LOG_UNCORK(level, format, args...) \
+ do { \
+ log_uncork(__LINE__, LOG_CURRENT_FUNCTION, level, LOG_SOURCENAME, format, \
+ ##args); \
+ } while (0)
+
+#ifdef __cplusplus
+}
+#endif
+
+#ifdef __cplusplus
+
+namespace aos {
+
+// CHECK* macros, similar to glog
+// (<http://google-glog.googlecode.com/svn/trunk/doc/glog.html>)'s, except they
+// don't support streaming in extra text. Some of the implementation is borrowed
+// from there too.
+// They all LOG(FATAL) with a helpful message when the check fails.
+// Portions copyright (c) 1999, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// CHECK dies with a fatal error if condition is not true. It is *not*
+// controlled by NDEBUG, so the check will be executed regardless of
+// compilation mode. Therefore, it is safe to do things like:
+// CHECK(fp->Write(x) == 4)
+#define CHECK(condition) \
+ if (__builtin_expect(!(condition), 0)) { \
+ LOG(FATAL, "CHECK(%s) failed\n", #condition); \
+ }
+
+// Helper functions for CHECK_OP macro.
+// The (int, int) specialization works around the issue that the compiler
+// will not instantiate the template version of the function on values of
+// unnamed enum type.
+#define DEFINE_CHECK_OP_IMPL(name, op) \
+ template <typename T1, typename T2> \
+ inline void LogImpl##name(const T1 &v1, const T2 &v2, \
+ const char *exprtext) { \
+ if (!__builtin_expect(v1 op v2, 1)) { \
+ log_do(FATAL, \
+ LOG_SOURCENAME ": " STRINGIFY(__LINE__) ": CHECK(%s) failed\n", \
+ exprtext); \
+ fprintf(stderr, "log_do(FATAL) fell through!!!!!\n"); \
+ printf("see stderr\n"); \
+ abort(); \
+ } \
+ } \
+ inline void LogImpl##name(int v1, int v2, const char *exprtext) { \
+ ::aos::LogImpl##name<int, int>(v1, v2, exprtext); \
+ }
+
+// We use the full name Check_EQ, Check_NE, etc. in case the file including
+// base/logging.h provides its own #defines for the simpler names EQ, NE, etc.
+// This happens if, for example, those are used as token names in a
+// yacc grammar.
+DEFINE_CHECK_OP_IMPL(Check_EQ, ==) // Compilation error with CHECK_EQ(NULL, x)?
+DEFINE_CHECK_OP_IMPL(Check_NE, !=) // Use CHECK(x == NULL) instead.
+DEFINE_CHECK_OP_IMPL(Check_LE, <=)
+DEFINE_CHECK_OP_IMPL(Check_LT, < )
+DEFINE_CHECK_OP_IMPL(Check_GE, >=)
+DEFINE_CHECK_OP_IMPL(Check_GT, > )
+
+#define CHECK_OP(name, op, val1, val2) \
+ ::aos::LogImplCheck##name(val1, val2, \
+ STRINGIFY(val1) STRINGIFY(op) STRINGIFY(val2))
+
+#define CHECK_EQ(val1, val2) CHECK_OP(_EQ, ==, val1, val2)
+#define CHECK_NE(val1, val2) CHECK_OP(_NE, !=, val1, val2)
+#define CHECK_LE(val1, val2) CHECK_OP(_LE, <=, val1, val2)
+#define CHECK_LT(val1, val2) CHECK_OP(_LT, < , val1, val2)
+#define CHECK_GE(val1, val2) CHECK_OP(_GE, >=, val1, val2)
+#define CHECK_GT(val1, val2) CHECK_OP(_GT, > , val1, val2)
+
+// A small helper for CHECK_NOTNULL().
+template <typename T>
+inline T* CheckNotNull(const char *value_name, T *t) {
+ if (t == NULL) {
+ LOG(FATAL, "'%s' must not be NULL\n", value_name);
+ }
+ return t;
+}
+
+// Check that the input is non NULL. This very useful in constructor
+// initializer lists.
+#define CHECK_NOTNULL(val) ::aos::CheckNotNull(STRINGIFY(val), val)
+
+inline int CheckSyscall(const char *syscall_string, int value) {
+ if (__builtin_expect(value == -1, false)) {
+ PLOG(FATAL, "%s failed", syscall_string);
+ }
+ return value;
+}
+
+inline void CheckSyscallReturn(const char *syscall_string, int value) {
+ if (__builtin_expect(value != 0, false)) {
+ PELOG(FATAL, value, "%s failed", syscall_string);
+ }
+}
+
+// Check that syscall does not return -1. If it does, PLOG(FATAL)s. This is
+// useful for quickly checking syscalls where it's not very useful to print out
+// the values of any of the arguments. Returns the result otherwise.
+//
+// Example: const int fd = PCHECK(open("/tmp/whatever", O_WRONLY))
+#define PCHECK(syscall) ::aos::CheckSyscall(STRINGIFY(syscall), syscall)
+
+// PELOG(FATAL)s with the result of syscall if it returns anything other than 0.
+// This is useful for quickly checking things like many of the pthreads
+// functions where it's not very useful to print out the values of any of the
+// arguments.
+//
+// Example: PRCHECK(munmap(address, length))
+#define PRCHECK(syscall) ::aos::CheckSyscallReturn(STRINGIFY(syscall), syscall)
+
+} // namespace aos
+
+#endif // __cplusplus
+
+#endif
diff --git a/aos/common/logging/logging_impl.cc b/aos/common/logging/logging_impl.cc
new file mode 100644
index 0000000..c776991
--- /dev/null
+++ b/aos/common/logging/logging_impl.cc
@@ -0,0 +1,330 @@
+#include "aos/common/logging/logging_impl.h"
+
+#include <stdarg.h>
+
+#include "aos/common/time.h"
+#include <inttypes.h>
+#include "aos/common/once.h"
+#include "aos/common/queue_types.h"
+#include "aos/common/logging/logging_printf_formats.h"
+
+namespace aos {
+namespace logging {
+namespace {
+
+using internal::Context;
+using internal::global_top_implementation;
+
+// The root LogImplementation. It only logs to stderr/stdout.
+// Some of the things specified in the LogImplementation documentation doesn't
+// apply here (mostly the parts about being able to use LOG) because this is the
+// root one.
+class RootLogImplementation : public LogImplementation {
+ public:
+ void have_other_implementation() { only_implementation_ = false; }
+
+ private:
+ virtual void set_next(LogImplementation *) {
+ LOG(FATAL, "can't have a next logger from here\n");
+ }
+
+ __attribute__((format(GOOD_PRINTF_FORMAT_TYPE, 3, 0)))
+ virtual void DoLog(log_level level, const char *format, va_list ap) {
+ LogMessage message;
+ internal::FillInMessage(level, format, ap, &message);
+ internal::PrintMessage(stderr, message);
+ if (!only_implementation_) {
+ fputs("root logger got used, see stderr for message\n", stdout);
+ }
+ }
+
+ bool only_implementation_ = true;
+};
+
+RootLogImplementation *root_implementation = nullptr;
+
+void SetGlobalImplementation(LogImplementation *implementation) {
+ if (root_implementation == nullptr) {
+ fputs("Somebody didn't call logging::Init()!\n", stderr);
+ abort();
+ }
+
+ Context *context = Context::Get();
+
+ context->implementation = implementation;
+ global_top_implementation.store(implementation);
+}
+
+void NewContext() {
+ Context::Delete();
+}
+
+void *DoInit() {
+ SetGlobalImplementation(root_implementation = new RootLogImplementation());
+
+ if (pthread_atfork(NULL /*prepare*/, NULL /*parent*/,
+ NewContext /*child*/) != 0) {
+ LOG(FATAL, "pthread_atfork(NULL, NULL, %p) failed\n",
+ NewContext);
+ }
+
+ return NULL;
+}
+
+} // namespace
+namespace internal {
+namespace {
+
+void FillInMessageBase(log_level level, LogMessage *message) {
+ Context *context = Context::Get();
+
+ message->level = level;
+ message->source = context->source;
+ memcpy(message->name, context->name, context->name_size);
+ message->name_length = context->name_size;
+
+ time::Time now = time::Time::Now();
+ message->seconds = now.sec();
+ message->nseconds = now.nsec();
+
+ message->sequence = context->sequence++;
+}
+
+} // namespace
+
+void FillInMessageStructure(log_level level,
+ const ::std::string &message_string, size_t size,
+ const MessageType *type,
+ const ::std::function<size_t(char *)> &serialize,
+ LogMessage *message) {
+ type_cache::AddShm(type->id);
+ message->structure.type_id = type->id;
+
+ FillInMessageBase(level, message);
+
+ if (message_string.size() + size > sizeof(message->structure.serialized)) {
+ LOG(FATAL, "serialized struct %s (size %zd) and message %s too big\n",
+ type->name.c_str(), size, message_string.c_str());
+ }
+ message->structure.string_length = message_string.size();
+ memcpy(message->structure.serialized, message_string.data(),
+ message->structure.string_length);
+
+ message->message_length = serialize(
+ &message->structure.serialized[message->structure.string_length]);
+ message->type = LogMessage::Type::kStruct;
+}
+
+void FillInMessageMatrix(log_level level,
+ const ::std::string &message_string, uint32_t type_id,
+ int rows, int cols, const void *data,
+ LogMessage *message) {
+ CHECK(MessageType::IsPrimitive(type_id));
+ message->matrix.type = type_id;
+
+ const auto element_size = MessageType::Sizeof(type_id);
+
+ FillInMessageBase(level, message);
+
+ message->message_length = rows * cols * element_size;
+ if (message_string.size() + message->message_length >
+ sizeof(message->matrix.data)) {
+ LOG(FATAL, "%dx%d matrix of type %" PRIu32
+ " (size %u) and message %s is too big\n",
+ rows, cols, type_id, element_size, message_string.c_str());
+ }
+ message->matrix.string_length = message_string.size();
+ memcpy(message->matrix.data, message_string.data(),
+ message->matrix.string_length);
+
+ message->matrix.rows = rows;
+ message->matrix.cols = cols;
+ SerializeMatrix(type_id, &message->matrix.data[message->matrix.string_length],
+ data, rows, cols);
+ message->type = LogMessage::Type::kMatrix;
+}
+
+void FillInMessage(log_level level, const char *format, va_list ap,
+ LogMessage *message) {
+ FillInMessageBase(level, message);
+
+ message->message_length =
+ ExecuteFormat(message->message, sizeof(message->message), format, ap);
+ message->type = LogMessage::Type::kString;
+}
+
+void PrintMessage(FILE *output, const LogMessage &message) {
+#define BASE_ARGS \
+ AOS_LOGGING_BASE_ARGS( \
+ message.name_length, message.name, static_cast<int32_t>(message.source), \
+ message.sequence, message.level, message.seconds, message.nseconds)
+ switch (message.type) {
+ case LogMessage::Type::kString:
+ fprintf(output, AOS_LOGGING_BASE_FORMAT "%.*s", BASE_ARGS,
+ static_cast<int>(message.message_length), message.message);
+ break;
+ case LogMessage::Type::kStruct: {
+ char buffer[2048];
+ size_t output_length = sizeof(buffer);
+ size_t input_length = message.message_length;
+ if (!PrintMessage(
+ buffer, &output_length,
+ message.structure.serialized + message.structure.string_length,
+ &input_length, type_cache::Get(message.structure.type_id))) {
+ LOG(FATAL,
+ "printing message (%.*s) of type %s into %zu-byte buffer failed\n",
+ static_cast<int>(message.message_length), message.message,
+ type_cache::Get(message.structure.type_id).name.c_str(),
+ sizeof(buffer));
+ }
+ if (input_length > 0) {
+ LOG(WARNING, "%zu extra bytes on message of type %s\n", input_length,
+ type_cache::Get(message.structure.type_id).name.c_str());
+ }
+ fprintf(output, AOS_LOGGING_BASE_FORMAT "%.*s: %.*s\n", BASE_ARGS,
+ static_cast<int>(message.structure.string_length),
+ message.structure.serialized,
+ static_cast<int>(sizeof(buffer) - output_length), buffer);
+ } break;
+ case LogMessage::Type::kMatrix: {
+ char buffer[1024];
+ size_t output_length = sizeof(buffer);
+ if (message.message_length !=
+ static_cast<size_t>(message.matrix.rows * message.matrix.cols *
+ MessageType::Sizeof(message.matrix.type))) {
+ LOG(FATAL, "expected %d bytes of matrix data but have %zu\n",
+ message.matrix.rows * message.matrix.cols *
+ MessageType::Sizeof(message.matrix.type),
+ message.message_length);
+ }
+ if (!PrintMatrix(buffer, &output_length,
+ message.matrix.data + message.matrix.string_length,
+ message.matrix.type, message.matrix.rows,
+ message.matrix.cols)) {
+ LOG(FATAL, "printing %dx%d matrix of type %" PRIu32 " failed\n",
+ message.matrix.rows, message.matrix.cols, message.matrix.type);
+ }
+ fprintf(output, AOS_LOGGING_BASE_FORMAT "%.*s: %.*s\n", BASE_ARGS,
+ static_cast<int>(message.matrix.string_length),
+ message.matrix.data,
+ static_cast<int>(sizeof(buffer) - output_length), buffer);
+ } break;
+ }
+#undef BASE_ARGS
+}
+
+} // namespace internal
+
+void LogImplementation::LogStruct(
+ log_level level, const ::std::string &message, size_t size,
+ const MessageType *type, const ::std::function<size_t(char *)> &serialize) {
+ char serialized[1024];
+ if (size > sizeof(serialized)) {
+ LOG(FATAL, "structure of type %s too big to serialize\n",
+ type->name.c_str());
+ }
+ size_t used = serialize(serialized);
+ char printed[1024];
+ size_t printed_bytes = sizeof(printed);
+ if (!PrintMessage(printed, &printed_bytes, serialized, &used, *type)) {
+ LOG(FATAL, "PrintMessage(%p, %p(=%zd), %p, %p(=%zd), %p(name=%s)) failed\n",
+ printed, &printed_bytes, printed_bytes, serialized, &used, used, type,
+ type->name.c_str());
+ }
+ DoLogVariadic(level, "%.*s: %.*s\n", static_cast<int>(message.size()),
+ message.data(),
+ static_cast<int>(sizeof(printed) - printed_bytes), printed);
+}
+
+void LogImplementation::LogMatrix(
+ log_level level, const ::std::string &message, uint32_t type_id,
+ int rows, int cols, const void *data) {
+ char serialized[1024];
+ if (static_cast<size_t>(rows * cols * MessageType::Sizeof(type_id)) >
+ sizeof(serialized)) {
+ LOG(FATAL, "matrix of size %u too big to serialize\n",
+ rows * cols * MessageType::Sizeof(type_id));
+ }
+ SerializeMatrix(type_id, serialized, data, rows, cols);
+ char printed[1024];
+ size_t printed_bytes = sizeof(printed);
+ if (!PrintMatrix(printed, &printed_bytes, serialized, type_id, rows, cols)) {
+ LOG(FATAL, "PrintMatrix(%p, %p(=%zd), %p, %" PRIu32 ", %d, %d) failed\n",
+ printed, &printed_bytes, printed_bytes, serialized, type_id, rows,
+ cols);
+ }
+ DoLogVariadic(level, "%.*s: %.*s\n", static_cast<int>(message.size()),
+ message.data(),
+ static_cast<int>(sizeof(printed) - printed_bytes), printed);
+}
+
+void HandleMessageLogImplementation::DoLog(log_level level, const char *format,
+ va_list ap) {
+ LogMessage message;
+ internal::FillInMessage(level, format, ap, &message);
+ HandleMessage(message);
+}
+
+void HandleMessageLogImplementation::LogStruct(
+ log_level level, const ::std::string &message_string, size_t size,
+ const MessageType *type, const ::std::function<size_t(char *)> &serialize) {
+ LogMessage message;
+ internal::FillInMessageStructure(level, message_string, size, type, serialize,
+ &message);
+ HandleMessage(message);
+}
+
+void HandleMessageLogImplementation::LogMatrix(
+ log_level level, const ::std::string &message_string, uint32_t type_id,
+ int rows, int cols, const void *data) {
+ LogMessage message;
+ internal::FillInMessageMatrix(level, message_string, type_id, rows, cols,
+ data, &message);
+ HandleMessage(message);
+}
+
+StreamLogImplementation::StreamLogImplementation(FILE *stream)
+ : stream_(stream) {}
+
+void StreamLogImplementation::HandleMessage(const LogMessage &message) {
+ internal::PrintMessage(stream_, message);
+}
+
+void LogNext(log_level level, const char *format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ LogImplementation::DoVLog(level, format, ap, 2);
+ va_end(ap);
+}
+
+void AddImplementation(LogImplementation *implementation) {
+ Context *context = Context::Get();
+
+ if (implementation->next() != NULL) {
+ LOG(FATAL, "%p already has a next implementation, but it's not"
+ " being used yet\n", implementation);
+ }
+
+ LogImplementation *old = context->implementation;
+ if (old != NULL) {
+ implementation->set_next(old);
+ }
+ SetGlobalImplementation(implementation);
+ root_implementation->have_other_implementation();
+}
+
+void Init() {
+ static Once<void> once(DoInit);
+ once.Get();
+}
+
+void Load() {
+ Context::Get();
+}
+
+void Cleanup() {
+ Context::Delete();
+}
+
+} // namespace logging
+} // namespace aos
diff --git a/aos/common/logging/logging_impl.h b/aos/common/logging/logging_impl.h
new file mode 100644
index 0000000..367c1a1
--- /dev/null
+++ b/aos/common/logging/logging_impl.h
@@ -0,0 +1,362 @@
+#ifndef AOS_COMMON_LOGGING_LOGGING_IMPL_H_
+#define AOS_COMMON_LOGGING_LOGGING_IMPL_H_
+
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <limits.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#include <string>
+#include <functional>
+#include <atomic>
+
+#include "aos/common/logging/logging.h"
+#include "aos/common/type_traits.h"
+#include "aos/common/mutex.h"
+#include "aos/common/macros.h"
+
+namespace aos {
+
+struct MessageType;
+
+} // namespace aos
+
+// This file has all of the logging implementation. It can't be #included by C
+// code like logging.h can.
+// It is useful for the rest of the logging implementation and other C++ code
+// that needs to do special things with logging.
+//
+// It is implemented in logging_impl.cc and logging_interface.cc. They are
+// separate so that code used by logging_impl.cc can link in
+// logging_interface.cc to use logging without creating a circular dependency.
+// However, any executables with such code still need logging_impl.cc linked in.
+
+namespace aos {
+namespace logging {
+
+// Unless explicitly stated otherwise, format must always be a string constant,
+// args are printf-style arguments for format, and ap is a va_list of args.
+// The validity of format and args together will be checked at compile time
+// using a gcc function attribute.
+
+// The struct that the code uses for making logging calls.
+struct LogMessage {
+ enum class Type : uint8_t {
+ kString, kStruct, kMatrix
+ };
+
+ int32_t seconds, nseconds;
+ // message_length is just the length of the actual data (which member depends
+ // on the type).
+ size_t message_length, name_length;
+ pid_t source;
+ static_assert(sizeof(source) == 4, "that's how they get printed");
+ // Per task/thread.
+ uint16_t sequence;
+ Type type;
+ log_level level;
+ char name[100];
+ union {
+ char message[LOG_MESSAGE_LEN];
+ struct {
+ uint32_t type_id;
+ size_t string_length;
+ // The message string and then the serialized structure.
+ char serialized[LOG_MESSAGE_LEN - sizeof(type) - sizeof(string_length)];
+ } structure;
+ struct {
+ // The type ID of the element type.
+ uint32_t type;
+ int rows, cols;
+ size_t string_length;
+ // The message string and then the serialized matrix.
+ char
+ data[LOG_MESSAGE_LEN - sizeof(type) - sizeof(rows) - sizeof(cols)];
+ } matrix;
+ };
+};
+static_assert(shm_ok<LogMessage>::value, "it's going in a queue");
+
+// Returns left > right. LOG_UNKNOWN is most important.
+static inline bool log_gt_important(log_level left, log_level right) {
+ if (left == ERROR) left = 3;
+ if (right == ERROR) right = 3;
+ return left > right;
+}
+
+// Returns a string representing level or "unknown".
+static inline const char *log_str(log_level level) {
+#define DECL_LEVEL(name, value) if (level == name) return #name;
+ DECL_LEVELS;
+#undef DECL_LEVEL
+ return "unknown";
+}
+// Returns the log level represented by str or LOG_UNKNOWN.
+static inline log_level str_log(const char *str) {
+#define DECL_LEVEL(name, value) if (!strcmp(str, #name)) return name;
+ DECL_LEVELS;
+#undef DECL_LEVEL
+ return LOG_UNKNOWN;
+}
+
+// Takes a message and logs it. It will set everything up and then call DoLog
+// for the current LogImplementation.
+void VLog(log_level level, const char *format, va_list ap)
+ __attribute__((format(GOOD_PRINTF_FORMAT_TYPE, 2, 0)));
+// Adds to the saved up message.
+void VCork(int line, const char *function, const char *format, va_list ap)
+ __attribute__((format(GOOD_PRINTF_FORMAT_TYPE, 3, 0)));
+// Actually logs the saved up message.
+void VUnCork(int line, const char *function, log_level level, const char *file,
+ const char *format, va_list ap)
+ __attribute__((format(GOOD_PRINTF_FORMAT_TYPE, 5, 0)));
+
+// Will call VLog with the given arguments for the next logger in the chain.
+void LogNext(log_level level, const char *format, ...)
+ __attribute__((format(GOOD_PRINTF_FORMAT_TYPE, 2, 3)));
+
+// Takes a structure and log it.
+template <class T>
+void DoLogStruct(log_level, const ::std::string &, const T &);
+// Takes a matrix and logs it.
+template <class T>
+void DoLogMatrix(log_level, const ::std::string &, const T &);
+
+// Represents a system that can actually take log messages and do something
+// useful with them.
+// All of the code (transitively too!) in the DoLog here can make
+// normal LOG and LOG_DYNAMIC calls but can NOT call LOG_CORK/LOG_UNCORK. These
+// calls will not result in DoLog recursing. However, implementations must be
+// safe to call from multiple threads/tasks at the same time. Also, any other
+// overriden methods may end up logging through a given implementation's DoLog.
+class LogImplementation {
+ public:
+ LogImplementation() : next_(NULL) {}
+
+ // The one that this one's implementation logs to.
+ // NULL means that there is no next one.
+ LogImplementation *next() { return next_; }
+ // Virtual in case a subclass wants to perform checks. There will be a valid
+ // logger other than this one available while this is called.
+ virtual void set_next(LogImplementation *next) { next_ = next; }
+
+ private:
+ // Actually logs the given message. Implementations should somehow create a
+ // LogMessage and then call internal::FillInMessage.
+ __attribute__((format(GOOD_PRINTF_FORMAT_TYPE, 3, 0)))
+ virtual void DoLog(log_level level, const char *format, va_list ap) = 0;
+ __attribute__((format(GOOD_PRINTF_FORMAT_TYPE, 3, 4)))
+ void DoLogVariadic(log_level level, const char *format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ DoLog(level, format, ap);
+ va_end(ap);
+ }
+
+ // Logs the contents of an auto-generated structure. The implementation here
+ // just converts it to a string with PrintMessage and then calls DoLog with
+ // that, however some implementations can be a lot more efficient than that.
+ // size and type are the result of calling Size() and Type() on the type of
+ // the message.
+ // serialize will call Serialize on the message.
+ virtual void LogStruct(log_level level, const ::std::string &message,
+ size_t size, const MessageType *type,
+ const ::std::function<size_t(char *)> &serialize);
+ // Similiar to LogStruct, except for matrixes.
+ // type_id is the type of the elements of the matrix.
+ // data points to rows*cols*type_id.Size() bytes of data in row-major order.
+ virtual void LogMatrix(log_level level, const ::std::string &message,
+ uint32_t type_id, int rows, int cols,
+ const void *data);
+
+ // These functions call similar methods on the "current" LogImplementation or
+ // Die if they can't find one.
+ // levels is how many LogImplementations to not use off the stack.
+ static void DoVLog(log_level, const char *format, va_list ap, int levels)
+ __attribute__((format(GOOD_PRINTF_FORMAT_TYPE, 2, 0)));
+ // This one is implemented in queue_logging.cc.
+ static void DoLogStruct(log_level level, const ::std::string &message,
+ size_t size, const MessageType *type,
+ const ::std::function<size_t(char *)> &serialize,
+ int levels);
+ // This one is implemented in matrix_logging.cc.
+ static void DoLogMatrix(log_level level, const ::std::string &message,
+ uint32_t type_id, int rows, int cols,
+ const void *data, int levels);
+
+ // Friends so that they can access the static Do* functions.
+ friend void VLog(log_level, const char *, va_list);
+ friend void LogNext(log_level, const char *, ...);
+ template <class T>
+ friend void DoLogStruct(log_level, const ::std::string &, const T &);
+ template <class T>
+ friend void DoLogMatrix(log_level, const ::std::string &, const T &);
+
+ LogImplementation *next_;
+};
+
+// Implements all of the DoLog* methods in terms of a (pure virtual in this
+// class) HandleMessage method that takes a pointer to the message.
+class HandleMessageLogImplementation : public LogImplementation {
+ public:
+ __attribute__((format(GOOD_PRINTF_FORMAT_TYPE, 3, 0)))
+ virtual void DoLog(log_level level, const char *format, va_list ap) override;
+ virtual void LogStruct(log_level level, const ::std::string &message_string,
+ size_t size, const MessageType *type,
+ const ::std::function<size_t(char *)> &serialize)
+ override;
+ virtual void LogMatrix(log_level level, const ::std::string &message_string,
+ uint32_t type_id, int rows, int cols, const void *data)
+ override;
+
+ virtual void HandleMessage(const LogMessage &message) = 0;
+};
+
+// A log implementation that dumps all messages to a C stdio stream.
+class StreamLogImplementation : public HandleMessageLogImplementation {
+ public:
+ StreamLogImplementation(FILE *stream);
+
+ private:
+ virtual void HandleMessage(const LogMessage &message) override;
+
+ FILE *const stream_;
+};
+
+// Adds another implementation to the stack of implementations in this
+// task/thread.
+// Any tasks/threads created after this call will also use this implementation.
+// The cutoff is when the state in a given task/thread is created (either lazily
+// when needed or by calling Load()).
+// The logging system takes ownership of implementation. It will delete it if
+// necessary, so it must be created with new.
+void AddImplementation(LogImplementation *implementation);
+
+// Must be called at least once per process/load before anything else is
+// called. This function is safe to call multiple times from multiple
+// tasks/threads.
+void Init();
+
+// Forces all of the state that is usually lazily created when first needed to
+// be created when called. Cleanup() will delete it.
+void Load();
+// Resets all information in this task/thread to its initial state.
+// NOTE: This is not the opposite of Init(). The state that this deletes is
+// lazily created when needed. It is actually the opposite of Load().
+void Cleanup();
+
+// This is where all of the code that is only used by actual LogImplementations
+// goes.
+namespace internal {
+
+extern ::std::atomic<LogImplementation *> global_top_implementation;
+
+// An separate instance of this class is accessible from each task/thread.
+// NOTE: It will get deleted in the child of a fork.
+//
+// Get() and Delete() are implemented in the platform-specific interface.cc
+// file.
+struct Context {
+ Context();
+
+ // Gets the Context object for this task/thread. Will create one the first
+ // time it is called.
+ //
+ // The implementation for each platform will lazily instantiate a new instance
+ // and then initialize name the first time.
+ // IMPORTANT: The implementation of this can not use logging.
+ static Context *Get();
+ // Deletes the Context object for this task/thread so that the next Get() is
+ // called it will create a new one.
+ // It is valid to call this when Get() has never been called.
+ // This also gets called after a fork(2) in the new process, where it should
+ // still work to clean up any state.
+ static void Delete();
+
+ // Which one to log to right now.
+ // Will be NULL if there is no logging implementation to use right now.
+ LogImplementation *implementation;
+
+ // A name representing this task/(process and thread).
+ char name[sizeof(LogMessage::name)];
+ size_t name_size;
+
+ // What to assign LogMessage::source to in this task/thread.
+ pid_t source;
+
+ // The sequence value to send out with the next message.
+ uint16_t sequence;
+
+ // Contains all of the information related to implementing LOG_CORK and
+ // LOG_UNCORK.
+ struct {
+ char message[LOG_MESSAGE_LEN];
+ int line_min, line_max;
+ // Sets the data up to record a new series of corked logs.
+ void Reset() {
+ message[0] = '\0'; // make strlen of it 0
+ line_min = INT_MAX;
+ line_max = -1;
+ function = NULL;
+ }
+ // The function that the calls are in.
+ // REMEMBER: While the compiler/linker will probably optimize all of the
+ // identical strings to point to the same data, it might not, so using == to
+ // compare this with another value is a bad idea.
+ const char *function;
+ } cork_data;
+};
+
+// Fills in all the parts of message according to the given inputs (with type
+// kStruct).
+void FillInMessageStructure(log_level level,
+ const ::std::string &message_string, size_t size,
+ const MessageType *type,
+ const ::std::function<size_t(char *)> &serialize,
+ LogMessage *message);
+
+// Fills in all the parts of the message according to the given inputs (with
+// type kMatrix).
+void FillInMessageMatrix(log_level level,
+ const ::std::string &message_string, uint32_t type_id,
+ int rows, int cols, const void *data,
+ LogMessage *message);
+
+// Fills in *message according to the given inputs (with type kString).
+// Used for implementing LogImplementation::DoLog.
+void FillInMessage(log_level level, const char *format, va_list ap,
+ LogMessage *message)
+ __attribute__((format(GOOD_PRINTF_FORMAT_TYPE, 2, 0)));
+
+__attribute__((format(GOOD_PRINTF_FORMAT_TYPE, 3, 4)))
+static inline void FillInMessageVarargs(log_level level, LogMessage *message,
+ const char *format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ FillInMessage(level, format, ap, message);
+ va_end(ap);
+}
+
+// Prints message to output.
+void PrintMessage(FILE *output, const LogMessage &message);
+
+// Prints format (with ap) into output and correctly deals with the result
+// being too long etc.
+size_t ExecuteFormat(char *output, size_t output_size, const char *format,
+ va_list ap)
+ __attribute__((format(GOOD_PRINTF_FORMAT_TYPE, 3, 0)));
+
+// Runs the given function with the current LogImplementation (handles switching
+// it out while running function etc).
+// levels is how many LogImplementations to not use off the stack.
+void RunWithCurrentImplementation(
+ int levels, ::std::function<void(LogImplementation *)> function);
+
+} // namespace internal
+} // namespace logging
+} // namespace aos
+
+#endif // AOS_COMMON_LOGGING_LOGGING_IMPL_H_
diff --git a/aos/common/logging/logging_impl_test.cc b/aos/common/logging/logging_impl_test.cc
new file mode 100644
index 0000000..dec962b
--- /dev/null
+++ b/aos/common/logging/logging_impl_test.cc
@@ -0,0 +1,239 @@
+#include <string>
+
+#include <inttypes.h>
+
+#include "gtest/gtest.h"
+
+#include "aos/common/logging/logging_impl.h"
+#include "aos/common/time.h"
+#include "aos/common/logging/logging_printf_formats.h"
+
+using ::testing::AssertionResult;
+using ::testing::AssertionSuccess;
+using ::testing::AssertionFailure;
+
+namespace aos {
+namespace logging {
+namespace testing {
+
+class TestLogImplementation : public LogImplementation {
+ __attribute__((format(GOOD_PRINTF_FORMAT_TYPE, 3, 0)))
+ virtual void DoLog(log_level level, const char *format, va_list ap) {
+ internal::FillInMessage(level, format, ap, &message_);
+
+ if (level == FATAL) {
+ internal::PrintMessage(stderr, message_);
+ abort();
+ }
+
+ used_ = true;
+ }
+
+ LogMessage message_;
+
+ public:
+ const LogMessage &message() { return message_; }
+ bool used() { return used_; }
+ void reset_used() { used_ = false; }
+
+ TestLogImplementation() : used_(false) {}
+
+ bool used_;
+};
+class LoggingTest : public ::testing::Test {
+ protected:
+ AssertionResult WasAnythingLogged() {
+ if (log_implementation->used()) {
+ return AssertionSuccess() << "read message '" <<
+ log_implementation->message().message << "'";
+ }
+ return AssertionFailure();
+ }
+ AssertionResult WasLogged(log_level level, const std::string message) {
+ if (!log_implementation->used()) {
+ return AssertionFailure() << "nothing was logged";
+ }
+ if (log_implementation->message().level != level) {
+ return AssertionFailure() << "a message with level " <<
+ log_str(log_implementation->message().level) <<
+ " was logged instead of " << log_str(level);
+ }
+ internal::Context *context = internal::Context::Get();
+ if (log_implementation->message().source != context->source) {
+ LOG(FATAL, "got a message from %" PRIu32 ", but we're %" PRIu32 "\n",
+ static_cast<uint32_t>(log_implementation->message().source),
+ static_cast<uint32_t>(context->source));
+ }
+ if (log_implementation->message().name_length != context->name_size ||
+ memcmp(log_implementation->message().name, context->name,
+ context->name_size) !=
+ 0) {
+ LOG(FATAL, "got a message from %.*s, but we're %s\n",
+ static_cast<int>(log_implementation->message().name_length),
+ log_implementation->message().name, context->name);
+ }
+ if (strstr(log_implementation->message().message, message.c_str())
+ == NULL) {
+ return AssertionFailure() << "got a message of '" <<
+ log_implementation->message().message <<
+ "' but expected it to contain '" << message << "'";
+ }
+
+ return AssertionSuccess() << log_implementation->message().message;
+ }
+
+ private:
+ virtual void SetUp() {
+ static bool first = true;
+ if (first) {
+ first = false;
+
+ Init();
+ AddImplementation(log_implementation = new TestLogImplementation());
+ }
+
+ log_implementation->reset_used();
+ }
+ virtual void TearDown() {
+ Cleanup();
+ }
+
+ static TestLogImplementation *log_implementation;
+};
+TestLogImplementation *LoggingTest::log_implementation(NULL);
+typedef LoggingTest LoggingDeathTest;
+
+// Tests both basic logging functionality and that the test setup works
+// correctly.
+TEST_F(LoggingTest, Basic) {
+ EXPECT_FALSE(WasAnythingLogged());
+ LOG(INFO, "test log 1\n");
+ EXPECT_TRUE(WasLogged(INFO, "test log 1\n"));
+ LOG(INFO, "test log 1.5\n");
+ // there's a subtle typo on purpose...
+ EXPECT_FALSE(WasLogged(INFO, "test log 15\n"));
+ LOG(ERROR, "test log 2=%d\n", 55);
+ EXPECT_TRUE(WasLogged(ERROR, "test log 2=55\n"));
+ LOG(ERROR, "test log 3\n");
+ EXPECT_FALSE(WasLogged(WARNING, "test log 3\n"));
+ LOG(WARNING, "test log 4\n");
+ EXPECT_TRUE(WasAnythingLogged());
+}
+TEST_F(LoggingTest, Cork) {
+ static const int begin_line = __LINE__;
+ LOG_CORK("first part ");
+ LOG_CORK("second part (=%d) ", 19);
+ EXPECT_FALSE(WasAnythingLogged());
+ LOG_CORK("third part ");
+ static const int end_line = __LINE__;
+ LOG_UNCORK(WARNING, "last part %d\n", 5);
+ std::stringstream expected;
+ expected << "logging_impl_test.cc: ";
+ expected << (begin_line + 1);
+ expected << "-";
+ expected << (end_line + 1);
+ expected << ": ";
+ expected << __func__;
+ expected << ": first part second part (=19) third part last part 5\n";
+ EXPECT_TRUE(WasLogged(WARNING, expected.str()));
+}
+
+TEST_F(LoggingDeathTest, Fatal) {
+ ASSERT_EXIT(LOG(FATAL, "this should crash it\n"),
+ ::testing::KilledBySignal(SIGABRT),
+ "this should crash it");
+}
+
+TEST_F(LoggingDeathTest, PCHECK) {
+ EXPECT_DEATH(PCHECK(fprintf(stdin, "nope")),
+ ".*fprintf\\(stdin, \"nope\"\\).*failed.*");
+}
+
+TEST_F(LoggingTest, PCHECK) {
+ EXPECT_EQ(7, PCHECK(printf("abc123\n")));
+}
+
+TEST_F(LoggingTest, PrintfDirectives) {
+ LOG(INFO, "test log %%1 %%d\n");
+ EXPECT_TRUE(WasLogged(INFO, "test log %1 %d\n"));
+ LOG_DYNAMIC(WARNING, "test log %%2 %%f\n");
+ EXPECT_TRUE(WasLogged(WARNING, "test log %2 %f\n"));
+ LOG_CORK("log 3 part %%1 %%d ");
+ LOG_UNCORK(DEBUG, "log 3 part %%2 %%f\n");
+ EXPECT_TRUE(WasLogged(DEBUG, "log 3 part %1 %d log 3 part %2 %f\n"));
+}
+
+TEST_F(LoggingTest, Timing) {
+ // For writing only.
+ //static const long kTimingCycles = 5000000;
+ static const long kTimingCycles = 5000;
+
+ time::Time start = time::Time::Now();
+ for (long i = 0; i < kTimingCycles; ++i) {
+ LOG(INFO, "a\n");
+ }
+ time::Time end = time::Time::Now();
+ time::Time diff = end - start;
+ printf("short message took %" PRId64 " nsec for %ld\n",
+ diff.ToNSec(), kTimingCycles);
+
+ start = time::Time::Now();
+ for (long i = 0; i < kTimingCycles; ++i) {
+ LOG(INFO, "something longer than just \"a\" to log to test timing\n");
+ }
+ end = time::Time::Now();
+ diff = end - start;
+ printf("long message took %" PRId64 " nsec for %ld\n",
+ diff.ToNSec(), kTimingCycles);
+}
+
+TEST(LoggingPrintFormatTest, Time) {
+ char buffer[1024];
+
+ // Easy ones.
+ ASSERT_EQ(18, snprintf(buffer, sizeof(buffer), AOS_TIME_FORMAT,
+ AOS_TIME_ARGS(2, 0)));
+ EXPECT_EQ("0000000002.000000s", ::std::string(buffer));
+ ASSERT_EQ(18, snprintf(buffer, sizeof(buffer), AOS_TIME_FORMAT,
+ AOS_TIME_ARGS(2, 1)));
+ EXPECT_EQ("0000000002.000000s", ::std::string(buffer));
+
+ // This one should be exact.
+ ASSERT_EQ(18, snprintf(buffer, sizeof(buffer), AOS_TIME_FORMAT,
+ AOS_TIME_ARGS(2, 1000)));
+ EXPECT_EQ("0000000002.000001s", ::std::string(buffer));
+
+ // Make sure rounding works correctly.
+ ASSERT_EQ(18, snprintf(buffer, sizeof(buffer), AOS_TIME_FORMAT,
+ AOS_TIME_ARGS(2, 999)));
+ EXPECT_EQ("0000000002.000001s", ::std::string(buffer));
+ ASSERT_EQ(18, snprintf(buffer, sizeof(buffer), AOS_TIME_FORMAT,
+ AOS_TIME_ARGS(2, 500)));
+ EXPECT_EQ("0000000002.000001s", ::std::string(buffer));
+ ASSERT_EQ(18, snprintf(buffer, sizeof(buffer), AOS_TIME_FORMAT,
+ AOS_TIME_ARGS(2, 499)));
+ EXPECT_EQ("0000000002.000000s", ::std::string(buffer));
+
+ // This used to result in "0000000001.099500s".
+ ASSERT_EQ(18, snprintf(buffer, sizeof(buffer), AOS_TIME_FORMAT,
+ AOS_TIME_ARGS(1, 995000000)));
+ EXPECT_EQ("0000000001.995000s", ::std::string(buffer));
+}
+
+TEST(LoggingPrintFormatTest, Base) {
+ char buffer[1024];
+
+ static const ::std::string kExpected1 =
+ "name(971)(01678): ERROR at 0000000001.995000s: ";
+ ASSERT_GT(sizeof(buffer), kExpected1.size());
+ ASSERT_EQ(
+ kExpected1.size(),
+ static_cast<size_t>(snprintf(
+ buffer, sizeof(buffer), AOS_LOGGING_BASE_FORMAT,
+ AOS_LOGGING_BASE_ARGS(4, "name", 971, 1678, ERROR, 1, 995000000))));
+ EXPECT_EQ(kExpected1, ::std::string(buffer));
+}
+
+} // namespace testing
+} // namespace logging
+} // namespace aos
diff --git a/aos/common/logging/logging_interface.cc b/aos/common/logging/logging_interface.cc
new file mode 100644
index 0000000..0bec52c
--- /dev/null
+++ b/aos/common/logging/logging_interface.cc
@@ -0,0 +1,141 @@
+#include "aos/common/logging/logging_impl.h"
+
+#include <stdarg.h>
+#include <string.h>
+
+#include <type_traits>
+
+#include "aos/common/die.h"
+
+// This file only contains the code necessary to link (ie no implementations).
+// See logging_impl.h for why this is necessary.
+
+namespace aos {
+namespace logging {
+namespace internal {
+
+::std::atomic<LogImplementation *> global_top_implementation(NULL);
+
+Context::Context()
+ : implementation(global_top_implementation.load()),
+ sequence(0) {
+ cork_data.Reset();
+}
+
+size_t ExecuteFormat(char *output, size_t output_size, const char *format,
+ va_list ap) {
+ static const char *const continued = "...\n";
+ const size_t size = output_size - strlen(continued);
+ const int ret = vsnprintf(output, size, format, ap);
+ typedef ::std::common_type<typeof(ret), typeof(size)>::type RetType;
+ if (ret < 0) {
+ PLOG(FATAL, "vsnprintf(%p, %zd, %s, args) failed",
+ output, size, format);
+ } else if (static_cast<RetType>(ret) >= static_cast<RetType>(size)) {
+ // Overwrite the '\0' at the end of the existing data and
+ // copy in the one on the end of continued.
+ memcpy(&output[size - 1], continued, strlen(continued) + 1);
+ }
+ return ::std::min<RetType>(ret, size);
+}
+
+void RunWithCurrentImplementation(
+ int levels, ::std::function<void(LogImplementation *)> function) {
+ Context *context = Context::Get();
+
+ LogImplementation *const top_implementation = context->implementation;
+ LogImplementation *new_implementation = top_implementation;
+ LogImplementation *implementation = NULL;
+ for (int i = 0; i < levels; ++i) {
+ implementation = new_implementation;
+ if (new_implementation == NULL) {
+ Die("no logging implementation to use\n");
+ }
+ new_implementation = new_implementation->next();
+ }
+ context->implementation = new_implementation;
+ function(implementation);
+ context->implementation = top_implementation;
+}
+
+} // namespace internal
+
+using internal::Context;
+
+void LogImplementation::DoVLog(log_level level, const char *format, va_list ap,
+ int levels) {
+ internal::RunWithCurrentImplementation(
+ levels, [&](LogImplementation * implementation) {
+ va_list ap1;
+ va_copy(ap1, ap);
+ implementation->DoLog(level, format, ap1);
+ va_end(ap1);
+
+ if (level == FATAL) {
+ VDie(format, ap);
+ }
+ });
+}
+
+void VLog(log_level level, const char *format, va_list ap) {
+ LogImplementation::DoVLog(level, format, ap, 1);
+}
+
+void VCork(int line, const char *function, const char *format, va_list ap) {
+ Context *context = Context::Get();
+
+ const size_t message_length = strlen(context->cork_data.message);
+ if (line > context->cork_data.line_max) context->cork_data.line_max = line;
+ if (line < context->cork_data.line_min) context->cork_data.line_min = line;
+
+ if (context->cork_data.function == NULL) {
+ context->cork_data.function = function;
+ } else {
+ if (strcmp(context->cork_data.function, function) != 0) {
+ LOG(FATAL, "started corking data in function %s but then moved to %s\n",
+ context->cork_data.function, function);
+ }
+ }
+
+ internal::ExecuteFormat(context->cork_data.message + message_length,
+ sizeof(context->cork_data.message) - message_length,
+ format, ap);
+}
+
+void VUnCork(int line, const char *function, log_level level, const char *file,
+ const char *format, va_list ap) {
+ Context *context = Context::Get();
+
+ VCork(line, function, format, ap);
+
+ log_do(level, "%s: %d-%d: %s: %s", file,
+ context->cork_data.line_min, context->cork_data.line_max, function,
+ context->cork_data.message);
+
+ context->cork_data.Reset();
+}
+
+} // namespace logging
+} // namespace aos
+
+void log_do(log_level level, const char *format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ aos::logging::VLog(level, format, ap);
+ va_end(ap);
+}
+
+void log_cork(int line, const char *function, const char *format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ aos::logging::VCork(line, function, format, ap);
+ va_end(ap);
+}
+
+void log_uncork(int line, const char *function, log_level level,
+ const char *file, const char *format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ aos::logging::VUnCork(line, function, level, file, format, ap);
+ va_end(ap);
+}
diff --git a/aos/common/logging/logging_printf_formats.h b/aos/common/logging/logging_printf_formats.h
new file mode 100644
index 0000000..57a83c3
--- /dev/null
+++ b/aos/common/logging/logging_printf_formats.h
@@ -0,0 +1,30 @@
+#ifndef AOS_COMMON_LOGGING_LOGGING_PRINTF_FORMATS_H_
+#define AOS_COMMON_LOGGING_LOGGING_PRINTF_FORMATS_H_
+
+#include "aos/common/macros.h"
+
+// This file has printf(3) formats and corresponding arguments for printing out
+// times and log messages.
+// They are all split out as macros because there are 2 things that want to
+// print using the same format: log_displayer and PrintMessage in
+// logging_impl.cc.
+
+#define AOS_TIME_FORMAT \
+ "%010" PRId32 ".%0" STRINGIFY(AOS_TIME_NSECONDS_DIGITS) PRId32 "s"
+#define AOS_TIME_ARGS(sec, nsec) \
+ sec, (nsec + (AOS_TIME_NSECONDS_DENOMINATOR / 2)) / \
+ AOS_TIME_NSECONDS_DENOMINATOR
+
+#define AOS_LOGGING_BASE_FORMAT \
+ "%.*s(%" PRId32 ")(%05" PRIu16 "): %-7s at " AOS_TIME_FORMAT ": "
+#define AOS_LOGGING_BASE_ARGS(name_length, name, source, sequence, level, sec, \
+ nsec) \
+ static_cast<int>(name_length), name, source, sequence, \
+ ::aos::logging::log_str(level), AOS_TIME_ARGS(sec, nsec)
+
+// These 2 define how many digits we use to print out the nseconds fields of
+// times. They have to stay matching.
+#define AOS_TIME_NSECONDS_DIGITS 6
+#define AOS_TIME_NSECONDS_DENOMINATOR 1000
+
+#endif // AOS_COMMON_LOGGING_LOGGING_PRINTF_FORMATS_H_
diff --git a/aos/common/logging/matrix_logging-tmpl.h b/aos/common/logging/matrix_logging-tmpl.h
new file mode 100644
index 0000000..bfc3ce5
--- /dev/null
+++ b/aos/common/logging/matrix_logging-tmpl.h
@@ -0,0 +1,20 @@
+#include "aos/common/logging/logging_impl.h"
+
+#include <functional>
+
+#include "aos/queue_primitives.h"
+
+namespace aos {
+namespace logging {
+
+template <class T>
+void DoLogMatrix(log_level level, const ::std::string &message,
+ const T &matrix) {
+ static_assert(!T::IsRowMajor, "we only handle column-major storage");
+ LogImplementation::DoLogMatrix(level, message, TypeID<typename T::Scalar>::id,
+ matrix.rows(), matrix.cols(), matrix.data(),
+ 1);
+}
+
+} // namespace logging
+} // namespace aos
diff --git a/aos/common/logging/matrix_logging.cc b/aos/common/logging/matrix_logging.cc
new file mode 100644
index 0000000..6173074
--- /dev/null
+++ b/aos/common/logging/matrix_logging.cc
@@ -0,0 +1,35 @@
+#include "aos/common/logging/matrix_logging.h"
+
+#include "aos/common/queue_types.h"
+
+namespace aos {
+namespace logging {
+
+void LogImplementation::DoLogMatrix(log_level level,
+ const ::std::string &message,
+ uint32_t type_id, int rows, int cols,
+ const void *data, int levels) {
+ internal::RunWithCurrentImplementation(
+ levels, [&](LogImplementation * implementation) {
+ implementation->LogMatrix(level, message, type_id, rows, cols, data);
+ });
+
+ if (level == FATAL) {
+ char serialized[1024];
+ if (static_cast<size_t>(rows * cols * MessageType::Sizeof(type_id)) >
+ sizeof(serialized)) {
+ Die("LOG(FATAL) matrix too big to serialize");
+ }
+ SerializeMatrix(type_id, serialized, data, rows, cols);
+ char printed[LOG_MESSAGE_LEN];
+ size_t printed_bytes = sizeof(printed);
+ if (!PrintMatrix(printed, &printed_bytes, serialized, type_id, rows, cols)) {
+ Die("LOG(FATAL) PrintMatrix call failed");
+ }
+ Die("%.*s: %.*s\n", static_cast<int>(message.size()), message.data(),
+ static_cast<int>(printed_bytes), printed);
+ }
+}
+
+} // namespace logging
+} // namespace aos
diff --git a/aos/common/logging/matrix_logging.h b/aos/common/logging/matrix_logging.h
new file mode 100644
index 0000000..6efaa06
--- /dev/null
+++ b/aos/common/logging/matrix_logging.h
@@ -0,0 +1,36 @@
+#ifndef AOS_COMMON_LOGGING_MATRIX_LOGGING_H_
+#define AOS_COMMON_LOGGING_MATRIX_LOGGING_H_
+
+#include <string>
+
+#include "Eigen/Dense"
+
+#include "aos/common/logging/logging.h"
+#include "aos/common/die.h"
+
+namespace aos {
+namespace logging {
+
+// Logs the contents of a matrix and a constant string.
+// matrix must be an instance of an Eigen matrix (or something similar).
+#define LOG_MATRIX(level, message, matrix) \
+ do { \
+ static const ::std::string kAosLoggingMessage( \
+ LOG_SOURCENAME ": " STRINGIFY(__LINE__) ": " message); \
+ ::aos::logging::DoLogMatrix(level, kAosLoggingMessage, matrix); \
+ /* so that GCC knows that it won't return */ \
+ if (level == FATAL) { \
+ ::aos::Die("DoLogStruct(FATAL) fell through!!!!!\n"); \
+ } \
+ } while (false)
+
+template <class T>
+void DoLogMatrix(log_level level, const ::std::string &message,
+ const T &matrix);
+
+} // namespace logging
+} // namespace aos
+
+#include "aos/common/logging/matrix_logging-tmpl.h"
+
+#endif // AOS_COMMON_LOGGING_MATRIX_LOGGING_H_
diff --git a/aos/common/logging/queue_logging-tmpl.h b/aos/common/logging/queue_logging-tmpl.h
new file mode 100644
index 0000000..e0a89e4
--- /dev/null
+++ b/aos/common/logging/queue_logging-tmpl.h
@@ -0,0 +1,19 @@
+#include "aos/common/logging/logging_impl.h"
+
+#include <functional>
+
+namespace aos {
+namespace logging {
+
+template <class T>
+void DoLogStruct(log_level level, const ::std::string &message,
+ const T &structure) {
+ LogImplementation::DoLogStruct(level, message, T::Size(), T::GetType(),
+ [&structure](char * buffer)->size_t{
+ return structure.Serialize(buffer);
+ },
+ 1);
+}
+
+} // namespace logging
+} // namespace aos
diff --git a/aos/common/logging/queue_logging.cc b/aos/common/logging/queue_logging.cc
new file mode 100644
index 0000000..ce96c29
--- /dev/null
+++ b/aos/common/logging/queue_logging.cc
@@ -0,0 +1,35 @@
+#include "aos/common/logging/queue_logging.h"
+
+#include "aos/common/logging/logging_impl.h"
+#include "aos/common/queue_types.h"
+
+namespace aos {
+namespace logging {
+
+void LogImplementation::DoLogStruct(
+ log_level level, const ::std::string &message, size_t size,
+ const MessageType *type, const ::std::function<size_t(char *)> &serialize,
+ int levels) {
+ internal::RunWithCurrentImplementation(
+ levels, [&](LogImplementation * implementation) {
+ implementation->LogStruct(level, message, size, type, serialize);
+ });
+
+ if (level == FATAL) {
+ char serialized[1024];
+ if (size > sizeof(serialized)) {
+ Die("LOG(FATAL) structure too big to serialize");
+ }
+ size_t used = serialize(serialized);
+ char printed[LOG_MESSAGE_LEN];
+ size_t printed_bytes = sizeof(printed);
+ if (!PrintMessage(printed, &printed_bytes, serialized, &used, *type)) {
+ Die("LOG(FATAL) PrintMessage call failed");
+ }
+ Die("%.*s: %.*s\n", static_cast<int>(message.size()), message.data(),
+ static_cast<int>(printed_bytes), printed);
+ }
+}
+
+} // namespace logging
+} // namespace aos
diff --git a/aos/common/logging/queue_logging.h b/aos/common/logging/queue_logging.h
new file mode 100644
index 0000000..ff0167a
--- /dev/null
+++ b/aos/common/logging/queue_logging.h
@@ -0,0 +1,37 @@
+#ifndef AOS_COMMON_LOGGING_QUEUE_LOGGING_H_
+#define AOS_COMMON_LOGGING_QUEUE_LOGGING_H_
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <string>
+
+#include "aos/common/logging/logging.h"
+#include "aos/common/die.h"
+
+namespace aos {
+namespace logging {
+
+// Logs the contents of a structure (or Queue message) and a constant string.
+// structure must be an instance of one of the generated queue types.
+#define LOG_STRUCT(level, message, structure) \
+ do { \
+ static const ::std::string kAosLoggingMessage( \
+ LOG_SOURCENAME ": " STRINGIFY(__LINE__) ": " message); \
+ ::aos::logging::DoLogStruct(level, kAosLoggingMessage, structure); \
+ /* so that GCC knows that it won't return */ \
+ if (level == FATAL) { \
+ ::aos::Die("DoLogStruct(FATAL) fell through!!!!!\n"); \
+ } \
+ } while (false)
+
+template <class T>
+void DoLogStruct(log_level level, const ::std::string &message,
+ const T &structure);
+
+} // namespace logging
+} // namespace aos
+
+#include "aos/common/logging/queue_logging-tmpl.h"
+
+#endif // AOS_COMMON_LOGGING_QUEUE_LOGGING_H_
diff --git a/aos/common/macros.h b/aos/common/macros.h
new file mode 100644
index 0000000..e8558b2
--- /dev/null
+++ b/aos/common/macros.h
@@ -0,0 +1,41 @@
+#ifndef _AOS_COMMON_MACROS_H_
+#define _AOS_COMMON_MACROS_H_
+
+// This file has some common macros etc.
+
+// A macro to disallow the copy constructor and operator= functions
+// This should be used in the private: declarations for a class
+#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
+ TypeName(const TypeName&) = delete; \
+ void operator=(const TypeName&) = delete
+
+// A macro to wrap arguments to macros that contain commas.
+// Useful for DISALLOW_COPY_AND_ASSIGNing templated types with multiple template
+// arguments.
+#define MACRO_ARG(...) __VA_ARGS__
+// Double-wraps macro arguments.
+// Necessary to use commas in gtest predicate arguments.
+#define MACRO_DARG(...) (__VA_ARGS__)
+
+#ifdef __GNUC__
+#define UNUSED_VARIABLE __attribute__ ((unused))
+#else
+#define UNUSED_VARIABLE
+#endif
+
+#define STRINGIFY(x) TO_STRING(x)
+#define TO_STRING(x) #x
+
+#ifdef __VXWORKS__
+// We're using ancient glibc, so sticking to just what the syscall can handle is
+// probably safer.
+#define GOOD_PRINTF_FORMAT_TYPE printf
+#else
+#ifdef __clang__
+#define GOOD_PRINTF_FORMAT_TYPE __printf__
+#else
+#define GOOD_PRINTF_FORMAT_TYPE gnu_printf
+#endif
+#endif
+
+#endif // _AOS_COMMON_MACROS_H_
diff --git a/aos/common/messages/messages.gyp b/aos/common/messages/messages.gyp
new file mode 100644
index 0000000..ab336b9
--- /dev/null
+++ b/aos/common/messages/messages.gyp
@@ -0,0 +1,15 @@
+{
+ 'targets': [
+ {
+ 'target_name': 'robot_state',
+ 'type': 'static_library',
+ 'sources': [
+ 'robot_state.q',
+ ],
+ 'variables': {
+ 'header_path': 'aos/common/messages',
+ },
+ 'includes': ['../../build/queues.gypi'],
+ },
+ ],
+}
diff --git a/aos/common/messages/robot_state.q b/aos/common/messages/robot_state.q
new file mode 100644
index 0000000..3ecb653
--- /dev/null
+++ b/aos/common/messages/robot_state.q
@@ -0,0 +1,67 @@
+package aos;
+
+struct Joystick {
+ // A bitmask of the button state.
+ uint16_t buttons;
+
+ // The 4 joystick axes.
+ double[4] axis;
+
+ // The POV axis.
+ int32_t pov;
+};
+
+message JoystickState {
+ Joystick[4] joysticks;
+
+ bool test_mode;
+ bool fms_attached;
+ bool enabled;
+ bool autonomous;
+ uint16_t team_id;
+
+ // If this is true, then this message isn't actually from the control
+ // system and so should not be trusted as evidence that the button inputs
+ // etc are actually real and should be acted on.
+ // However, most things should ignore this so that sending fake messages is
+ // useful for testing. The only difference in behavior should be motors not
+ // actually turning on.
+ bool fake;
+};
+
+// This queue is checked by all control loops to make sure that the
+// joystick code hasn't died.
+queue JoystickState joystick_state;
+
+message RobotState {
+ // The PID of the process reading sensors.
+ // This is here so control loops can tell when it changes.
+ int32_t reader_pid;
+
+ // True when outputs are enabled.
+ // Motor controllers keep going for a bit after this goes to false.
+ bool outputs_enabled;
+ // Indicates whether something is browned out (I think motor controller
+ // outputs). IMPORTANT: This is NOT !outputs_enabled. outputs_enabled goes to
+ // false for other reasons too (disabled, e-stopped, maybe more).
+ bool browned_out;
+
+ // Whether the two sensor rails are currently working.
+ bool is_3v3_active;
+ bool is_5v_active;
+ // The current voltages measured on the two sensor rails.
+ double voltage_3v3;
+ double voltage_5v;
+
+ // The input voltage to the roboRIO.
+ double voltage_roborio_in;
+
+ // From the DriverStation object, aka what FMS sees and what shows up on the
+ // actual driver's station.
+ double voltage_battery;
+};
+
+// Messages are sent out on this queue along with reading sensors. It contains
+// global robot state and information about whether the process reading sensors
+// has been restarted, along with all counters etc it keeps track of.
+queue RobotState robot_state;
diff --git a/aos/common/mutex.h b/aos/common/mutex.h
new file mode 100644
index 0000000..ca35b10
--- /dev/null
+++ b/aos/common/mutex.h
@@ -0,0 +1,144 @@
+#ifndef AOS_COMMON_MUTEX_H_
+#define AOS_COMMON_MUTEX_H_
+
+#include "aos/common/macros.h"
+#include "aos/linux_code/ipc_lib/aos_sync.h"
+#include "aos/common/die.h"
+
+namespace aos {
+
+class Condition;
+
+// An abstraction of a mutex that is easy to implement for environments other
+// than Linux too.
+// If there are multiple threads or processes contending for the mutex,
+// higher priority ones will succeed in locking first,
+// and tasks of equal priorities have the same chance of getting the lock.
+// To deal with priority inversion, the linux implementation does priority
+// inheritance.
+// Before destroying a mutex, it is important to make sure it isn't locked.
+// Otherwise, the destructor will LOG(FATAL).
+class Mutex {
+ public:
+ // States that signify the result of TryLock.
+ enum class State {
+ // The mutex was acquired successfully.
+ kLocked,
+ // TryLock tried to grab the mutex and failed.
+ kLockFailed
+ };
+
+ // Creates an unlocked mutex.
+ Mutex();
+ // Verifies that it isn't locked.
+ //
+ // This is important because freeing a locked mutex means there is freed
+ // memory in the middle of the robust list, which breaks things horribly.
+ ~Mutex();
+
+ // Locks the mutex. If it fails, it calls LOG(FATAL).
+ // Returns false.
+ bool Lock() __attribute__((warn_unused_result));
+ // Unlocks the mutex. Fails like Lock.
+ // Multiple unlocking is undefined.
+ void Unlock();
+ // Locks the mutex unless it is already locked.
+ // Returns the new state of the mutex.
+ // Doesn't wait for the mutex to be unlocked if it is locked.
+ State TryLock() __attribute__((warn_unused_result));
+
+ // Returns true iff the current task has this mutex locked.
+ // This is mainly for IPCRecursiveMutexLocker to use.
+ bool OwnedBySelf() const;
+
+ private:
+ aos_mutex impl_;
+
+ friend class Condition; // for access to impl_
+};
+
+// A class that locks a Mutex when constructed and unlocks it when destructed.
+// Designed to be used as a local variable so that
+// the mutex will be unlocked when the scope is exited.
+// This one immediately Dies if the previous owner died. This makes it a good
+// choice for mutexes that are only used within a single process, but NOT for
+// mutexes shared by multiple processes. For those, use IPCMutexLocker.
+class MutexLocker {
+ public:
+ explicit MutexLocker(Mutex *mutex) : mutex_(mutex) {
+ if (__builtin_expect(mutex_->Lock(), false)) {
+ ::aos::Die("previous owner of mutex %p died but it shouldn't be able to",
+ this);
+ }
+ }
+ ~MutexLocker() {
+ mutex_->Unlock();
+ }
+
+ private:
+ Mutex *const mutex_;
+
+ DISALLOW_COPY_AND_ASSIGN(MutexLocker);
+};
+
+// A version of MutexLocker which reports the previous owner dying instead of
+// immediately LOG(FATAL)ing.
+class IPCMutexLocker {
+ public:
+ explicit IPCMutexLocker(Mutex *mutex)
+ : mutex_(mutex), owner_died_(mutex_->Lock()) {}
+ ~IPCMutexLocker() {
+ if (__builtin_expect(!owner_died_checked_, false)) {
+ ::aos::Die("nobody checked if the previous owner of mutex %p died", this);
+ }
+ mutex_->Unlock();
+ }
+
+ // Whether or not the previous owner died. If this is not called at least
+ // once, the destructor will ::aos::Die.
+ __attribute__((warn_unused_result)) bool owner_died() {
+ owner_died_checked_ = true;
+ return __builtin_expect(owner_died_, false);
+ }
+
+ private:
+ Mutex *const mutex_;
+ const bool owner_died_;
+ bool owner_died_checked_ = false;
+
+ DISALLOW_COPY_AND_ASSIGN(IPCMutexLocker);
+};
+
+// A version of IPCMutexLocker which only locks (and unlocks) the mutex if the
+// current task does not already hold it.
+class IPCRecursiveMutexLocker {
+ public:
+ explicit IPCRecursiveMutexLocker(Mutex *mutex)
+ : mutex_(mutex),
+ locked_(!mutex_->OwnedBySelf()),
+ owner_died_(locked_ ? mutex_->Lock() : false) {}
+ ~IPCRecursiveMutexLocker() {
+ if (__builtin_expect(!owner_died_checked_, false)) {
+ ::aos::Die("nobody checked if the previous owner of mutex %p died", this);
+ }
+ if (locked_) mutex_->Unlock();
+ }
+
+ // Whether or not the previous owner died. If this is not called at least
+ // once, the destructor will ::aos::Die.
+ __attribute__((warn_unused_result)) bool owner_died() {
+ owner_died_checked_ = true;
+ return __builtin_expect(owner_died_, false);
+ }
+
+ private:
+ Mutex *const mutex_;
+ const bool locked_, owner_died_;
+ bool owner_died_checked_ = false;
+
+ DISALLOW_COPY_AND_ASSIGN(IPCRecursiveMutexLocker);
+};
+
+} // namespace aos
+
+#endif // AOS_COMMON_MUTEX_H_
diff --git a/aos/common/mutex_test.cc b/aos/common/mutex_test.cc
new file mode 100644
index 0000000..6614d5c
--- /dev/null
+++ b/aos/common/mutex_test.cc
@@ -0,0 +1,273 @@
+#include "aos/common/mutex.h"
+
+#include <sched.h>
+#include <math.h>
+#include <pthread.h>
+
+#include <thread>
+
+#include "gtest/gtest.h"
+
+#include "aos/linux_code/ipc_lib/aos_sync.h"
+#include "aos/common/die.h"
+#include "aos/common/util/death_test_log_implementation.h"
+#include "aos/common/util/thread.h"
+#include "aos/common/time.h"
+#include "aos/common/queue_testutils.h"
+#include "aos/linux_code/ipc_lib/core_lib.h"
+
+namespace aos {
+namespace testing {
+
+class MutexTest : public ::testing::Test {
+ public:
+ Mutex test_mutex_;
+
+ protected:
+ void SetUp() override {
+ ::aos::common::testing::EnableTestLogging();
+ SetDieTestMode(true);
+ }
+};
+
+typedef MutexTest MutexDeathTest;
+typedef MutexTest MutexLockerTest;
+typedef MutexTest MutexLockerDeathTest;
+typedef MutexTest IPCMutexLockerTest;
+typedef MutexTest IPCMutexLockerDeathTest;
+typedef MutexTest IPCRecursiveMutexLockerTest;
+
+TEST_F(MutexTest, TryLock) {
+ EXPECT_EQ(Mutex::State::kLocked, test_mutex_.TryLock());
+ EXPECT_EQ(Mutex::State::kLockFailed, test_mutex_.TryLock());
+
+ test_mutex_.Unlock();
+}
+
+TEST_F(MutexTest, Lock) {
+ ASSERT_FALSE(test_mutex_.Lock());
+ EXPECT_EQ(Mutex::State::kLockFailed, test_mutex_.TryLock());
+
+ test_mutex_.Unlock();
+}
+
+TEST_F(MutexTest, Unlock) {
+ ASSERT_FALSE(test_mutex_.Lock());
+ EXPECT_EQ(Mutex::State::kLockFailed, test_mutex_.TryLock());
+ test_mutex_.Unlock();
+ EXPECT_EQ(Mutex::State::kLocked, test_mutex_.TryLock());
+
+ test_mutex_.Unlock();
+}
+
+// Sees what happens with multiple unlocks.
+TEST_F(MutexDeathTest, RepeatUnlock) {
+ logging::Init();
+ ASSERT_FALSE(test_mutex_.Lock());
+ test_mutex_.Unlock();
+ EXPECT_DEATH(
+ {
+ logging::AddImplementation(new util::DeathTestLogImplementation());
+ test_mutex_.Unlock();
+ },
+ ".*multiple unlock.*");
+}
+
+// Sees what happens if you unlock without ever locking (or unlocking) it.
+TEST_F(MutexDeathTest, NeverLock) {
+ logging::Init();
+ EXPECT_DEATH(
+ {
+ logging::AddImplementation(new util::DeathTestLogImplementation());
+ test_mutex_.Unlock();
+ },
+ ".*multiple unlock.*");
+}
+
+// Sees what happens with multiple locks.
+TEST_F(MutexDeathTest, RepeatLock) {
+ EXPECT_DEATH(
+ {
+ logging::AddImplementation(new util::DeathTestLogImplementation());
+ ASSERT_FALSE(test_mutex_.Lock());
+ ASSERT_FALSE(test_mutex_.Lock());
+ },
+ ".*multiple lock.*");
+}
+
+TEST_F(MutexDeathTest, DestroyLocked) {
+ EXPECT_DEATH(
+ {
+ logging::AddImplementation(new util::DeathTestLogImplementation());
+ Mutex new_mutex;
+ ASSERT_FALSE(new_mutex.Lock());
+ },
+ ".*destroying locked mutex.*");
+}
+
+namespace {
+
+class AdderThread : public ::aos::util::Thread {
+ public:
+ AdderThread(int *counter, Mutex *mutex, ::aos::time::Time sleep_before_time,
+ ::aos::time::Time sleep_after_time)
+ : counter_(counter),
+ mutex_(mutex),
+ sleep_before_time_(sleep_before_time),
+ sleep_after_time_(sleep_after_time) {}
+ virtual void Run() override {
+ ::aos::time::SleepFor(sleep_before_time_);
+ MutexLocker locker(mutex_);
+ ++(*counter_);
+ ::aos::time::SleepFor(sleep_after_time_);
+ }
+
+ private:
+ int *const counter_;
+ Mutex *const mutex_;
+ const ::aos::time::Time sleep_before_time_, sleep_after_time_;
+};
+
+} // namespace
+
+// Verifies that ThreadSanitizer understands that a contended mutex establishes
+// a happens-before relationship.
+TEST_F(MutexTest, ThreadSanitizerContended) {
+ int counter = 0;
+ AdderThread threads[2]{
+ {&counter, &test_mutex_, ::aos::time::Time::InSeconds(0.2),
+ ::aos::time::Time::InSeconds(0)},
+ {&counter, &test_mutex_, ::aos::time::Time::InSeconds(0),
+ ::aos::time::Time::InSeconds(0)}, };
+ for (auto &c : threads) {
+ c.Start();
+ }
+ for (auto &c : threads) {
+ c.WaitUntilDone();
+ }
+ EXPECT_EQ(2, counter);
+}
+
+// Verifiers that ThreadSanitizer understands how a mutex works.
+// For some reason this used to fail when the other tests didn't...
+TEST_F(MutexTest, ThreadSanitizerMutexLocker) {
+ int counter = 0;
+ ::std::thread thread([&counter, this]() {
+ for (int i = 0; i < 1000; ++i) {
+ MutexLocker locker(&test_mutex_);
+ ++counter;
+ }
+ });
+ for (int i = 0; i < 1000; ++i) {
+ MutexLocker locker(&test_mutex_);
+ --counter;
+ }
+ thread.join();
+ EXPECT_EQ(0, counter);
+}
+
+// Verifies that ThreadSanitizer understands that an uncontended mutex
+// establishes a happens-before relationship.
+TEST_F(MutexTest, ThreadSanitizerUncontended) {
+ int counter = 0;
+ AdderThread threads[2]{
+ {&counter, &test_mutex_, ::aos::time::Time::InSeconds(0.2),
+ ::aos::time::Time::InSeconds(0)},
+ {&counter, &test_mutex_, ::aos::time::Time::InSeconds(0),
+ ::aos::time::Time::InSeconds(0)}, };
+ for (auto &c : threads) {
+ c.Start();
+ }
+ for (auto &c : threads) {
+ c.WaitUntilDone();
+ }
+ EXPECT_EQ(2, counter);
+}
+
+namespace {
+
+class LockerThread : public util::Thread {
+ public:
+ LockerThread(Mutex *mutex, bool lock, bool unlock)
+ : mutex_(mutex), lock_(lock), unlock_(unlock) {}
+
+ private:
+ virtual void Run() override {
+ if (lock_) ASSERT_FALSE(mutex_->Lock());
+ if (unlock_) mutex_->Unlock();
+ }
+
+ Mutex *const mutex_;
+ const bool lock_, unlock_;
+};
+
+} // namespace
+
+// Makes sure that we don't SIGSEGV or something with multiple threads.
+TEST_F(MutexTest, MultiThreadedLock) {
+ LockerThread t(&test_mutex_, true, true);
+ t.Start();
+ ASSERT_FALSE(test_mutex_.Lock());
+ test_mutex_.Unlock();
+ t.Join();
+}
+
+TEST_F(MutexLockerTest, Basic) {
+ {
+ aos::MutexLocker locker(&test_mutex_);
+ EXPECT_EQ(Mutex::State::kLockFailed, test_mutex_.TryLock());
+ }
+ EXPECT_EQ(Mutex::State::kLocked, test_mutex_.TryLock());
+
+ test_mutex_.Unlock();
+}
+
+TEST_F(IPCMutexLockerTest, Basic) {
+ {
+ aos::IPCMutexLocker locker(&test_mutex_);
+ EXPECT_EQ(Mutex::State::kLockFailed, test_mutex_.TryLock());
+ EXPECT_FALSE(locker.owner_died());
+ }
+ EXPECT_EQ(Mutex::State::kLocked, test_mutex_.TryLock());
+
+ test_mutex_.Unlock();
+}
+
+// Tests what happens when the caller doesn't check if the previous owner died
+// with an IPCMutexLocker.
+TEST_F(IPCMutexLockerDeathTest, NoCheckOwnerDied) {
+ EXPECT_DEATH({ aos::IPCMutexLocker locker(&test_mutex_); },
+ "nobody checked if the previous owner of mutex [^ ]+ died.*");
+}
+
+TEST_F(IPCRecursiveMutexLockerTest, Basic) {
+ {
+ aos::IPCRecursiveMutexLocker locker(&test_mutex_);
+ EXPECT_EQ(Mutex::State::kLockFailed, test_mutex_.TryLock());
+ EXPECT_FALSE(locker.owner_died());
+ }
+ EXPECT_EQ(Mutex::State::kLocked, test_mutex_.TryLock());
+
+ test_mutex_.Unlock();
+}
+
+// Tests actually locking a mutex recursively with IPCRecursiveMutexLocker.
+TEST_F(IPCRecursiveMutexLockerTest, RecursiveLock) {
+ {
+ aos::IPCRecursiveMutexLocker locker(&test_mutex_);
+ EXPECT_EQ(Mutex::State::kLockFailed, test_mutex_.TryLock());
+ {
+ aos::IPCRecursiveMutexLocker locker(&test_mutex_);
+ EXPECT_EQ(Mutex::State::kLockFailed, test_mutex_.TryLock());
+ EXPECT_FALSE(locker.owner_died());
+ }
+ EXPECT_EQ(Mutex::State::kLockFailed, test_mutex_.TryLock());
+ EXPECT_FALSE(locker.owner_died());
+ }
+ EXPECT_EQ(Mutex::State::kLocked, test_mutex_.TryLock());
+
+ test_mutex_.Unlock();
+}
+
+} // namespace testing
+} // namespace aos
diff --git a/aos/common/network/network.gyp b/aos/common/network/network.gyp
new file mode 100644
index 0000000..cf52f9f
--- /dev/null
+++ b/aos/common/network/network.gyp
@@ -0,0 +1,36 @@
+{
+ 'targets': [
+ {
+ 'target_name': 'team_number',
+ 'type': 'static_library',
+ 'sources': [
+ 'team_number.cc',
+ ],
+ 'dependencies': [
+ '<(AOS)/linux_code/linux_code.gyp:configuration',
+ '<(AOS)/common/common.gyp:once',
+ '<(AOS)/build/aos.gyp:logging',
+ '<(AOS)/common/util/util.gyp:string_to_num',
+ ],
+ },
+ {
+ 'target_name': 'socket',
+ 'type': 'static_library',
+ 'sources': [
+ 'receive_socket.cc',
+ 'send_socket.cc',
+ 'socket.cc',
+ ],
+ 'dependencies': [
+ '<(AOS)/build/aos.gyp:logging',
+ '<(AOS)/common/common.gyp:time',
+ '<(AOS)/common/util/util.gyp:inet_addr',
+ '<(AOS)/linux_code/linux_code.gyp:configuration',
+ ],
+ 'export_dependent_settings': [
+ '<(AOS)/build/aos.gyp:logging',
+ '<(AOS)/common/common.gyp:time',
+ ],
+ },
+ ],
+}
diff --git a/aos/common/network/receive_socket.cc b/aos/common/network/receive_socket.cc
new file mode 100644
index 0000000..c3a7710
--- /dev/null
+++ b/aos/common/network/receive_socket.cc
@@ -0,0 +1,33 @@
+#include "aos/common/network/receive_socket.h"
+
+#include <string.h>
+#include <math.h>
+#include <errno.h>
+#include <stdint.h>
+#include <stddef.h>
+#include <sys/socket.h>
+
+#include "aos/common/logging/logging.h"
+
+namespace aos {
+namespace network {
+
+static const char *localhost = "0.0.0.0";
+
+int ReceiveSocket::Connect(NetworkPort port) {
+ Reset();
+ const int ret = Socket::Connect(port, localhost);
+ if (ret != 0) {
+ return ret;
+ }
+
+ if (bind(socket_, &addr_.addr,
+ sizeof(addr_)) == -1) {
+ PLOG(ERROR, "failed to bind to address '%s'", localhost);
+ return last_ret_ = -1;
+ }
+ return last_ret_ = 0;
+}
+
+} // namespace network
+} // namespace aos
diff --git a/aos/common/network/receive_socket.h b/aos/common/network/receive_socket.h
new file mode 100644
index 0000000..afa0726
--- /dev/null
+++ b/aos/common/network/receive_socket.h
@@ -0,0 +1,18 @@
+#ifndef AOS_COMMON_NETWORK_RECEIVE_SOCKET_H_
+#define AOS_COMMON_NETWORK_RECEIVE_SOCKET_H_
+
+#include "aos/common/network/socket.h"
+
+namespace aos {
+namespace network {
+
+class ReceiveSocket : public Socket {
+ public:
+ explicit ReceiveSocket(NetworkPort port) { Connect(port); }
+ int Connect(NetworkPort port);
+};
+
+} // namespace network
+} // namespace aos
+
+#endif // AOS_COMMON_NETWORK_RECEIVE_SOCKET_H_
diff --git a/aos/common/network/send_socket.cc b/aos/common/network/send_socket.cc
new file mode 100644
index 0000000..9886893
--- /dev/null
+++ b/aos/common/network/send_socket.cc
@@ -0,0 +1,31 @@
+#include "aos/common/network/send_socket.h"
+
+#include <string.h>
+#include <errno.h>
+#include <stdint.h>
+#include <stddef.h>
+#include <math.h>
+#include <sys/socket.h>
+
+#include "aos/common/logging/logging.h"
+
+namespace aos {
+namespace network {
+
+int SendSocket::Connect(NetworkPort port, const char *robot_ip, int type) {
+ Reset();
+ const int ret = Socket::Connect(port, robot_ip, type);
+ if (ret != 0) {
+ return ret;
+ }
+
+ if (connect(socket_, &addr_.addr, sizeof(addr_)) < 0) {
+ PLOG(ERROR, "couldn't connect to ip '%s'", robot_ip);
+ return last_ret_ = 1;
+ }
+
+ return last_ret_ = 0;
+}
+
+} // namespace network
+} // namespace aos
diff --git a/aos/common/network/send_socket.h b/aos/common/network/send_socket.h
new file mode 100644
index 0000000..77db726
--- /dev/null
+++ b/aos/common/network/send_socket.h
@@ -0,0 +1,29 @@
+#ifndef AOS_COMMON_NETWORK_SEND_SOCKET_H_
+#define AOS_COMMON_NETWORK_SEND_SOCKET_H_
+
+#include "aos/common/network/socket.h"
+
+#include "aos/linux_code/configuration.h"
+#include "aos/common/network_port.h"
+#include "aos/common/util/inet_addr.h"
+
+namespace aos {
+namespace network {
+
+class SendSocket : public Socket {
+ public:
+ // Connect must be called before use.
+ SendSocket() {}
+ // Calls Connect automatically.
+ SendSocket(NetworkPort port, ::aos::NetworkAddress address) {
+ Connect(port,
+ ::aos::util::MakeIPAddress(::aos::configuration::GetOwnIPAddress(),
+ address));
+ }
+ int Connect(NetworkPort port, const char *robot_ip, int type = SOCK_DGRAM);
+};
+
+} // namespace network
+} // namespace aos
+
+#endif // AOS_COMMON_NETWORK_SEND_SOCKET_H_
diff --git a/aos/common/network/socket.cc b/aos/common/network/socket.cc
new file mode 100644
index 0000000..b674ff1
--- /dev/null
+++ b/aos/common/network/socket.cc
@@ -0,0 +1,83 @@
+#include "aos/common/network/socket.h"
+
+#include <string.h>
+#include <errno.h>
+#include <sys/socket.h>
+
+#include "aos/common/logging/logging.h"
+#include "aos/common/byteorder.h"
+
+namespace aos {
+namespace network {
+
+int Socket::Connect(NetworkPort port, const char *address, int type) {
+ last_ret_ = 0;
+ if ((socket_ = socket(AF_INET, type, 0)) < 0) {
+ PLOG(ERROR, "failed to create socket");
+ return last_ret_ = 1;
+ }
+
+ memset(&addr_, 0, sizeof(addr_));
+ addr_.in.sin_family = AF_INET;
+ addr_.in.sin_port = hton<uint16_t>(static_cast<uint16_t>(port));
+#ifndef __VXWORKS__
+ const int failure_return = 0;
+#else
+ const int failure_return = -1;
+#endif
+ if (inet_aton(address, &addr_.in.sin_addr) == failure_return) {
+ PLOG(ERROR, "invalid IP address '%s'", address);
+ return last_ret_ = -1;
+ }
+
+ return last_ret_ = 0;
+}
+
+Socket::Socket() : socket_(-1), last_ret_(2) {}
+
+Socket::~Socket() {
+ close(socket_);
+}
+
+void Socket::Reset() {
+ if (socket_ != -1) {
+ close(socket_);
+ socket_ = -1;
+ }
+ last_ret_ = 0;
+}
+
+int Socket::Receive(void *buf, int length) {
+ const int ret = recv(socket_, static_cast<char *>(buf), length, 0);
+ last_ret_ = (ret == -1) ? -1 : 0;
+ return ret;
+}
+
+int Socket::Receive(void *buf, int length, time::Time timeout) {
+ timeval timeout_timeval = timeout.ToTimeval();
+ fd_set fds;
+ FD_ZERO(&fds);
+ FD_SET(socket_, &fds);
+ switch (select(FD_SETSIZE, &fds, NULL, NULL, &timeout_timeval)) {
+ case 1:
+ return Receive(buf, length);
+ case 0:
+ return last_ret_ = 0;
+ default:
+ if (errno == EINTR) {
+ return last_ret_ = 0;
+ }
+ PLOG(FATAL, "select(FD_SETSIZE, %p, NULL, NULL, %p) failed",
+ &fds, &timeout_timeval);
+ }
+}
+
+int Socket::Send(const void *buf, int length) {
+ const int ret = write(socket_,
+ static_cast<const char *>(buf), length);
+ last_ret_ = (ret == -1) ? -1 : 0;
+ return ret;
+}
+
+} // namespace network
+} // namespace aos
diff --git a/aos/common/network/socket.h b/aos/common/network/socket.h
new file mode 100644
index 0000000..9e8eaf5
--- /dev/null
+++ b/aos/common/network/socket.h
@@ -0,0 +1,53 @@
+#ifndef AOS_COMMON_NETWORK_SOCKET_H_
+#define AOS_COMMON_NETWORK_SOCKET_H_
+
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+
+#include "aos/common/time.h"
+#include "aos/common/network_port.h"
+
+namespace aos {
+namespace network {
+
+class Socket {
+ public:
+ int LastStatus() const { return last_ret_; }
+
+ int Send(const void *buf, int length);
+
+ // buf is where to put the data and length is the maximum amount of data to
+ // put in for all overloads.
+ // All overloads return how many bytes were received or -1 for error. 0 is a
+ // valid return value for all overloads.
+ // No timeout.
+ int Receive(void *buf, int length);
+ // timeout is relative
+ int Receive(void *buf, int length, time::Time timeout);
+
+ protected:
+ int Connect(NetworkPort port, const char *address, int type = SOCK_DGRAM);
+ Socket();
+ ~Socket();
+
+ // Resets socket_ and last_ret_.
+ void Reset();
+
+ union {
+ sockaddr_in in;
+ sockaddr addr;
+ } addr_; // filled in by Connect
+
+ int socket_;
+ int last_ret_;
+};
+
+} // namespace network
+} // namespace aos
+
+#endif // AOS_COMMON_NETWORK_SOCKET_H_
diff --git a/aos/common/network/team_number.cc b/aos/common/network/team_number.cc
new file mode 100644
index 0000000..134113a
--- /dev/null
+++ b/aos/common/network/team_number.cc
@@ -0,0 +1,81 @@
+#include "aos/common/network/team_number.h"
+
+#include <netinet/in.h>
+#include <inttypes.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include <string>
+
+#include "aos/common/once.h"
+#include "aos/linux_code/configuration.h"
+#include "aos/common/logging/logging.h"
+#include "aos/common/util/string_to_num.h"
+
+namespace aos {
+namespace network {
+namespace {
+
+uint16_t override_team;
+
+::std::string GetHostname() {
+ char buf[256];
+ buf[sizeof(buf) - 1] = '\0';
+ PCHECK(gethostname(buf, sizeof(buf) - 1));
+ return buf;
+}
+
+int ParseTeamNumber(const std::string &hostname, uint16_t *teamnumber) {
+ for (size_t i = 0; i < hostname.size(); i++) {
+ if (hostname[i] == '-') {
+ const std::string num_as_s = hostname.substr(i + 1);
+
+ int num;
+ if (!::aos::util::StringToNumber(num_as_s, &num)) {
+ return -1;
+ }
+ if (hostname.substr(0, i) == "roboRIO" &&
+ std::to_string(num) == num_as_s) {
+ *teamnumber = num;
+ return 0;
+ } else {
+ return -1;
+ }
+ }
+ }
+ return -1;
+}
+
+uint16_t *DoGetTeamNumber() {
+ if (override_team != 0) return &override_team;
+
+ static uint16_t r;
+
+ const char *override_number = getenv("AOS_TEAM_NUMBER");
+ if (override_number != nullptr) {
+ if (!::aos::util::StringToNumber(override_number, &r)) {
+ LOG(FATAL, "error parsing AOS_TEAM_NUMBER '%s'\n", override_number);
+ }
+ LOG(WARNING, "team number overridden by AOS_TEAM_NUMBER to %" PRIu16 "\n",
+ r);
+ } else {
+ int error = ParseTeamNumber(GetHostname(), &r);
+ if (error) {
+ LOG(FATAL, "Invalid hostname %s\n", GetHostname().c_str());
+ }
+ LOG(INFO, "team number is %" PRIu16 "\n", r);
+ }
+ return &r;
+}
+
+} // namespace
+
+uint16_t GetTeamNumber() {
+ static Once<uint16_t> once(DoGetTeamNumber);
+ return *once.Get();
+}
+
+void OverrideTeamNumber(uint16_t team) { override_team = team; }
+
+} // namespace network
+} // namespace aos
diff --git a/aos/common/network/team_number.h b/aos/common/network/team_number.h
new file mode 100644
index 0000000..520b9ba
--- /dev/null
+++ b/aos/common/network/team_number.h
@@ -0,0 +1,24 @@
+#ifndef AOS_COMMON_NETWORK_TEAM_NUMBER_H_
+#define AOS_COMMON_NETWORK_TEAM_NUMBER_H_
+
+#include <stdint.h>
+
+namespace aos {
+namespace network {
+
+// Retrieves the current team number based off of the network address.
+// This function will only do the complicated stuff once so it is cheap to call
+// repeatedly.
+uint16_t GetTeamNumber();
+
+// Overrides the team number returned from GetTeamNumber(). Must be called
+// before GetTeamNumber() is ever called.
+// Overriding to team 0 won't work.
+// Intended only for tests.
+// Guaranteed to be safe to call during static initialization time.
+void OverrideTeamNumber(uint16_t team);
+
+} // namespace network
+} // namespace aos
+
+#endif // AOS_COMMON_NETWORK_TEAM_NUMBER_H_
diff --git a/aos/common/network_port.h b/aos/common/network_port.h
new file mode 100644
index 0000000..b523674
--- /dev/null
+++ b/aos/common/network_port.h
@@ -0,0 +1,32 @@
+#ifndef AOS_COMMON_NETWORK_PORT_H_
+#define AOS_COMMON_NETWORK_PORT_H_
+
+#include <stdint.h>
+
+namespace aos {
+
+// Constants representing the various ports used for communications and some
+// documentation about what each is used for.
+enum class NetworkPort : uint16_t {
+ // UDP socket sending motor values from the prime to the crio.
+ kMotors = 9710,
+ // UDP socket forwarding drivers station packets from the crio to the prime.
+ kDS = 9711,
+ // UDP socket sending sensor values from the crio to the prime.
+ kSensors = 9712,
+ // HTTP server that sends out camera feeds in mjpg format.
+ // Should not be changed because this number shows up elsewhere too.
+ kCameraStreamer = 9714,
+};
+
+// Constants representing the various devices that talk on the network and the
+// last segment of their IP addresses.
+enum class NetworkAddress : uint8_t {
+ // The computer that the cRIO talks to.
+ kPrime = 179,
+ kCRIO = 2,
+};
+
+} // namespace aos
+
+#endif // AOS_COMMON_NETWORK_PORT_H_
diff --git a/aos/common/once-tmpl.h b/aos/common/once-tmpl.h
new file mode 100644
index 0000000..0618395
--- /dev/null
+++ b/aos/common/once-tmpl.h
@@ -0,0 +1,43 @@
+#ifdef __VXWORKS__
+#include <taskLib.h>
+#else
+#include <sched.h>
+#endif
+
+#include "aos/common/type_traits.h"
+
+// It doesn't use pthread_once, because Brian looked at the pthreads
+// implementation for vxworks and noticed that it is completely and entirely
+// broken for doing just about anything (including its pthread_once). It has the
+// same implementation under linux for simplicity.
+
+namespace aos {
+
+// Setting function_ multiple times would be OK because it'll get set to the
+// same value each time.
+template<typename T>
+Once<T>::Once(Function function)
+ : function_(function) {
+ static_assert(shm_ok<Once<T>>::value, "Once should work in shared memory");
+}
+
+template<typename T>
+void Once<T>::Reset() {
+ __atomic_store_n(&run_, false, __ATOMIC_SEQ_CST);
+ __atomic_store_n(&done_, false, __ATOMIC_SEQ_CST);
+}
+
+template<typename T>
+T *Once<T>::Get() {
+ if (__atomic_exchange_n(&run_, true, __ATOMIC_RELAXED) == false) {
+ result_ = function_();
+ __atomic_store_n(&done_, true, __ATOMIC_RELEASE);
+ } else {
+ while (!__atomic_load_n(&done_, __ATOMIC_ACQUIRE)) {
+ sched_yield();
+ }
+ }
+ return result_;
+}
+
+} // namespace aos
diff --git a/aos/common/once.h b/aos/common/once.h
new file mode 100644
index 0000000..b7fbcb1
--- /dev/null
+++ b/aos/common/once.h
@@ -0,0 +1,69 @@
+#ifndef AOS_COMMON_ONCE_H_
+#define AOS_COMMON_ONCE_H_
+
+#include <stdint.h>
+
+#include "aos/common/gtest_prod.h"
+
+namespace aos {
+namespace testing {
+
+FORWARD_DECLARE_TEST_CASE(OnceTest, MemoryClearing);
+
+} // namespace testing
+
+// Designed for the same thing as pthread_once: to run something exactly 1 time.
+//
+// Intended use case:
+// const char *CalculateSomethingCool() {
+// static ::aos::Once<const char> once(DoCalculateSomethingCool);
+// return once.Get();
+// }
+//
+// IMPORTANT: Instances _must_ be placed in memory that gets 0-initialized
+// automatically or Reset() must be called exactly once!!
+// The expected use case is to use one of these as a static variable, and those
+// do get 0-initialized under the C++ standard. Global variables are treated the
+// same way by the C++ Standard.
+// Placing an instance in shared memory (and using Reset()) is also supported.
+// The constructor does not initialize all of the member variables!
+// This is because initializing them in the constructor creates a race condition
+// if initialization of static variables isn't thread safe.
+template<typename T>
+class Once {
+ public:
+ typedef T *(*Function)();
+ explicit Once(Function function);
+
+ // Returns the result of calling function_. The first call will actually run
+ // it and then any other ones will block (if necessary) until it's finished
+ // and then return the same thing.
+ T *Get();
+
+ // Will clear out all the member variables. If this is going to be used
+ // instead of creating an instance in 0-initialized memory, then this method
+ // must be called exactly once before Get() is called anywhere.
+ // This method can also be called to run the function again the next time
+ // Get() is called. However, calling it again is not thread safe.
+ void Reset();
+
+ private:
+ // The function to run to calculate result_.
+ Function function_;
+ // Whether or not it is running. Gets atomically swapped from false to true by
+ // the thread that actually runs function_.
+ bool run_;
+ // Whether or not it is done. Gets set to true after the thread that is
+ // running function_ finishes running it and storing the result in result_.
+ bool done_;
+ // What function_ returned when it was executed.
+ T *result_;
+
+ FRIEND_TEST_NAMESPACE(OnceTest, MemoryClearing, testing);
+};
+
+} // namespace aos
+
+#include "aos/common/once-tmpl.h"
+
+#endif // AOS_COMMON_ONCE_H_
diff --git a/aos/common/once_test.cc b/aos/common/once_test.cc
new file mode 100644
index 0000000..3773b5b
--- /dev/null
+++ b/aos/common/once_test.cc
@@ -0,0 +1,130 @@
+#include "aos/common/once.h"
+
+#include <stdlib.h>
+#include <limits.h>
+
+#include "gtest/gtest.h"
+
+namespace aos {
+namespace testing {
+
+class OnceTest : public ::testing::Test {
+ public:
+ static int *Function() {
+ ++times_run_;
+ value_ = 971 + times_run_;
+ return &value_;
+ }
+
+ protected:
+ void SetUp() {
+ value_ = 0;
+ times_run_ = 0;
+ }
+
+ static int value_;
+ static int times_run_;
+};
+int OnceTest::value_, OnceTest::times_run_;
+
+// Makes sure that it calls the function at the right time and that it correctly
+// passes the result out.
+TEST_F(OnceTest, Works) {
+ static Once<int> once(Function);
+
+ EXPECT_EQ(0, value_);
+ EXPECT_EQ(0, times_run_);
+
+ EXPECT_EQ(value_, *once.Get());
+ EXPECT_NE(0, value_);
+ EXPECT_NE(0, times_run_);
+ // Make sure it's not passing it through an assignment by value or something
+ // else weird.
+ EXPECT_EQ(&value_, once.Get());
+}
+
+// Makes sure that having a Once at namespace scope works correctly.
+namespace {
+
+Once<int> global_once(OnceTest::Function);
+
+} // namespace
+
+TEST_F(OnceTest, Global) {
+ EXPECT_EQ(value_, *global_once.Get());
+ EXPECT_NE(0, value_);
+ EXPECT_NE(0, times_run_);
+}
+
+// Makes sure that an instance keeps returning the same value without running
+// the function again.
+TEST_F(OnceTest, MultipleGets) {
+ static Once<int> once(Function);
+
+ EXPECT_EQ(value_, *once.Get());
+ EXPECT_EQ(1, times_run_);
+ EXPECT_EQ(value_, *once.Get());
+ EXPECT_EQ(1, times_run_);
+ EXPECT_EQ(value_, *once.Get());
+ EXPECT_EQ(1, times_run_);
+}
+
+// Tests to make sure that the right methods clear out the instance variables at
+// the right times.
+TEST_F(OnceTest, MemoryClearing) {
+ Once<int> once(NULL);
+
+ once.run_ = 1;
+ once.done_ = true;
+ // Run the constructor again to make sure it doesn't touch the variables set
+ // above.
+ new (&once)Once<int>(Function);
+
+ // Should return a random, (potentially) uninitialized value.
+ once.Get();
+ EXPECT_EQ(0, times_run_);
+
+ once.Reset();
+ EXPECT_EQ(0, times_run_);
+ EXPECT_EQ(value_, *once.Get());
+ EXPECT_EQ(1, times_run_);
+}
+
+namespace {
+
+int second_result = 0;
+int *SecondFunction() {
+ static int result = 254;
+ second_result = ++result;
+ return &second_result;
+}
+
+} // namespace
+
+// Makes sure that multiple instances don't interfere with each other.
+TEST_F(OnceTest, MultipleInstances) {
+ static Once<int> once1(Function);
+ static Once<int> once2(SecondFunction);
+
+ EXPECT_EQ(&value_, once1.Get());
+ EXPECT_EQ(&second_result, once2.Get());
+ EXPECT_EQ(&value_, once1.Get());
+ EXPECT_EQ(&second_result, once2.Get());
+}
+
+// Tests calling Reset() to run the function a second time.
+TEST_F(OnceTest, Recalculate) {
+ Once<int> once(Function);
+ once.Reset();
+
+ EXPECT_EQ(value_, *once.Get());
+ EXPECT_EQ(1, times_run_);
+
+ value_ = 0;
+ once.Reset();
+ EXPECT_EQ(value_, *once.Get());
+ EXPECT_EQ(2, times_run_);
+}
+
+} // namespace testing
+} // namespace aos
diff --git a/aos/common/print_field_helpers.h b/aos/common/print_field_helpers.h
new file mode 100644
index 0000000..0dd2fab
--- /dev/null
+++ b/aos/common/print_field_helpers.h
@@ -0,0 +1,41 @@
+#ifndef AOS_COMMON_PRINT_FIELD_HELPERS_H_
+#define AOS_COMMON_PRINT_FIELD_HELPERS_H_
+
+#include <stdint.h>
+
+#include <type_traits>
+
+namespace aos {
+
+template<typename T>
+inline bool PrintInteger(char *buf, T val, size_t *output) {
+ static const bool is_signed = ::std::is_signed<T>::value;
+ const bool is_negative =
+ is_signed ? (val & (static_cast<T>(1) << (sizeof(T) * 8 - 1))) : false;
+
+ size_t len = 0;
+ if (is_negative) {
+ do {
+ if (len == *output) return false;
+ buf[len++] = '0' - (val % 10);
+ val /= 10;
+ } while (val != 0);
+ if (len == *output) return false;
+ buf[len++] = '-';
+ } else {
+ do {
+ if (len == *output) return false;
+ buf[len++] = '0' + (val % 10);
+ val /= 10;
+ } while (val != 0);
+ }
+ for (size_t i = 0; i < (len >> 1); i++) {
+ std::swap(buf[len - 1 - i], buf[i]);
+ }
+ *output -= len;
+ return true;
+}
+
+} // namespace aos
+
+#endif // AOS_COMMON_PRINT_FIELD_HELPERS_H_
diff --git a/aos/common/queue.cc b/aos/common/queue.cc
new file mode 100644
index 0000000..893e62b
--- /dev/null
+++ b/aos/common/queue.cc
@@ -0,0 +1,37 @@
+#include "aos/common/queue.h"
+
+#include "aos/common/byteorder.h"
+#include <inttypes.h>
+
+namespace aos {
+
+void Message::Zero() {
+ sent_time.set_sec(0);
+ sent_time.set_nsec(0);
+}
+
+size_t Message::Deserialize(const char *buffer) {
+ int32_t sec;
+ int32_t nsec;
+ to_host(&buffer[0], &sec);
+ to_host(&buffer[4], &nsec);
+ sent_time.set_sec(sec);
+ sent_time.set_nsec(nsec);
+ return Size();
+}
+// Serializes the common fields into the buffer.
+size_t Message::Serialize(char *buffer) const {
+ // TODO(aschuh): to_network shouldn't need a pointer.
+ int32_t sec = sent_time.sec();
+ int32_t nsec = sent_time.nsec();
+ to_network(&sec, &buffer[0]);
+ to_network(&nsec, &buffer[4]);
+ return Size();
+}
+
+size_t Message::Print(char *buffer, int length) const {
+ return snprintf(buffer, length, "%" PRId32 ".%09" PRId32 "s",
+ sent_time.sec(), sent_time.nsec());
+}
+
+} // namespace aos
diff --git a/aos/common/queue.h b/aos/common/queue.h
new file mode 100644
index 0000000..5f6adeb
--- /dev/null
+++ b/aos/common/queue.h
@@ -0,0 +1,282 @@
+#ifndef AOS_COMMON_QUEUE_H_
+#define AOS_COMMON_QUEUE_H_
+
+#include <assert.h>
+
+#include "aos/common/time.h"
+#include "aos/common/macros.h"
+#include "aos/linux_code/ipc_lib/queue.h"
+#include "aos/common/time.h"
+
+namespace aos {
+
+struct MessageType;
+
+// This class is a base class for all messages sent over queues.
+// All of the methods are overloaded in (generated) subclasses to do the same
+// thing for the whole thing.
+class Message {
+ public:
+ typedef ::aos::time::Time Time;
+ // The time that the message was sent at.
+ Time sent_time;
+
+ Message() : sent_time(0, 0) {}
+
+ // Zeros out the time.
+ void Zero();
+ // Returns the size of the common fields.
+ static size_t Size() { return sizeof(Time); }
+
+ // Deserializes the common fields from the buffer.
+ size_t Deserialize(const char *buffer);
+ // Serializes the common fields into the buffer.
+ size_t Serialize(char *buffer) const;
+
+ // Populates sent_time with the current time.
+ void SetTimeToNow() { sent_time = Time::Now(); }
+
+ // Writes the contents of the message to the provided buffer.
+ size_t Print(char *buffer, int length) const;
+
+ // Compares two messages for equality, excluding their sent_time.
+ bool EqualsNoTime(const Message & /*other*/) const { return true; }
+
+ static const MessageType *GetType();
+};
+
+template <class T> class Queue;
+template <class T> class MessageBuilder;
+template <class T> class ScopedMessagePtr;
+
+// A ScopedMessagePtr<> manages a queue message pointer.
+// It frees it properly when the ScopedMessagePtr<> goes out of scope or gets
+// sent. By design, there is no way to get the ScopedMessagePtr to release the
+// message pointer to external code.
+template <class T>
+class ScopedMessagePtr {
+ public:
+ // Returns a pointer to the message.
+ // This stays valid until Send or the destructor have been called.
+ const T *get() const { return msg_; }
+ T *get() { return msg_; }
+
+ const T &operator*() const {
+ const T *msg = get();
+ assert(msg != NULL);
+ return *msg;
+ }
+
+ T &operator*() {
+ T *msg = get();
+ assert(msg != NULL);
+ return *msg;
+ }
+
+ const T *operator->() const {
+ const T *msg = get();
+ assert(msg != NULL);
+ return msg;
+ }
+
+ T *operator->() {
+ T *msg = get();
+ assert(msg != NULL);
+ return msg;
+ }
+
+ operator bool() {
+ return msg_ != NULL;
+ }
+
+ // Sends the message and removes our reference to it.
+ // If the queue is full, over-ride the oldest message in it with our new
+ // message.
+ // Returns true on success, and false otherwise.
+ // The message will be freed.
+ bool Send();
+
+ // Sends the message and removes our reference to it.
+ // If the queue is full, block until it is no longer full.
+ // Returns true on success, and false otherwise.
+ // The message will be freed.
+ bool SendBlocking();
+
+ // Frees the contained message.
+ ~ScopedMessagePtr() {
+ reset();
+ }
+
+ // Implements a move constructor. This only takes rvalue references
+ // because we want to allow someone to say
+ // ScopedMessagePtr<X> ptr = queue.MakeMessage();
+ // but we don't want to allow them to then say
+ // ScopedMessagePtr<X> new_ptr = ptr;
+ // And, if they do actually want to move the pointer, then it will correctly
+ // clear out the source so there aren't 2 pointers to the message lying
+ // around.
+ ScopedMessagePtr(ScopedMessagePtr<T> &&ptr)
+ : queue_(ptr.queue_), msg_(ptr.msg_) {
+ ptr.msg_ = NULL;
+ }
+
+ private:
+ // Provide access to set_queue and the constructor for init.
+ friend class aos::Queue<typename std::remove_const<T>::type>;
+ // Provide access to the copy constructor for MakeWithBuilder.
+ friend class aos::MessageBuilder<T>;
+
+ // Only Queue should be able to build a message.
+ ScopedMessagePtr(RawQueue *queue, T *msg)
+ : queue_(queue), msg_(msg) {}
+
+ // Sets the pointer to msg, freeing the old value if it was there.
+ // This is private because nobody should be able to get a pointer to a message
+ // that needs to be scoped without using the queue.
+ void reset(T *msg = NULL);
+
+ // Sets the queue that owns this message.
+ void set_queue(RawQueue *queue) { queue_ = queue; }
+
+ // The queue that the message is a part of.
+ RawQueue *queue_;
+ // The message or NULL.
+ T *msg_;
+
+ // Protect evil constructors.
+ DISALLOW_COPY_AND_ASSIGN(ScopedMessagePtr<T>);
+};
+
+// Specializations for the Builders will be automatically generated in the .q.h
+// header files with all of the handy builder methods.
+template <class T>
+class MessageBuilder {
+ public:
+ typedef T Message;
+ bool Send();
+};
+
+// TODO(aschuh): Base class
+// T must be a Message with the same format as the messages generated by
+// the .q files.
+template <class T>
+class Queue {
+ public:
+ typedef T Message;
+
+ Queue(const char *queue_name)
+ : queue_name_(queue_name), queue_(NULL), queue_msg_(NULL, NULL) {
+ static_assert(shm_ok<T>::value,
+ "The provided message type can't be put in shmem.");
+ }
+
+ // Initializes the queue. This may optionally be called to do any one time
+ // work before sending information, and may be be called multiple times.
+ // Init will be called when a message is sent, but this will cause sending to
+ // take a different amount of time the first cycle.
+ void Init();
+
+ // Removes all internal references to shared memory so shared memory can be
+ // restarted safely. This should only be used in testing.
+ void Clear();
+
+ // Fetches the next message from the queue.
+ // Returns true if there was a new message available and we successfully
+ // fetched it.
+ bool FetchNext();
+
+ // Fetches the next message from the queue, waiting if necessary until there
+ // is one.
+ void FetchNextBlocking();
+
+ // Fetches the last message from the queue.
+ // Returns true if there was a new message available and we successfully
+ // fetched it.
+ bool FetchLatest();
+
+ // Fetches the latest message from the queue, or blocks if we have already
+ // fetched it until another is avilable.
+ void FetchAnother();
+
+ // Returns the age of the message.
+ const time::Time Age() const {
+ return time::Time::Now() - queue_msg_->sent_time;
+ }
+
+ // Returns true if the latest value in the queue is newer than age mseconds.
+ // DEPRECATED(brians): Use IsNewerThan(const time::Time&) instead.
+ bool IsNewerThanMS(int age) const {
+ // TODO(aschuh): Log very verbosely if something is _ever_ stale.
+ return IsNewerThan(time::Time::InMS(age));
+ }
+
+ bool IsNewerThan(const time::Time &age) const {
+ return get() != nullptr && Age() < age;
+ }
+
+ // Returns a pointer to the current message.
+ // This pointer will be valid until a new message is fetched.
+ const T *get() const { return queue_msg_.get(); }
+
+ // Returns a reference to the message.
+ // The message will be valid until a new message is fetched.
+ const T &operator*() const {
+ const T *msg = get();
+ assert(msg != NULL);
+ return *msg;
+ }
+
+ // Returns a pointer to the current message.
+ // This pointer will be valid until a new message is fetched.
+ const T *operator->() const {
+ const T *msg = get();
+ assert(msg != NULL);
+ return msg;
+ }
+
+ // Returns a scoped_ptr containing a message.
+ // GCC will optimize away the copy constructor, so this is safe.
+ ScopedMessagePtr<T> MakeMessage();
+
+ // Returns a message builder that contains a pre-allocated message.
+ // This message will start out completely zeroed.
+ aos::MessageBuilder<T> MakeWithBuilder();
+
+ const char *name() const { return queue_name_; }
+
+ private:
+ const char *queue_name_;
+
+ T *MakeRawMessage();
+
+ // Pointer to the queue that this object fetches from.
+ RawQueue *queue_;
+ int index_ = 0;
+ // Scoped pointer holding the latest message or NULL.
+ ScopedMessagePtr<const T> queue_msg_;
+
+ DISALLOW_COPY_AND_ASSIGN(Queue<T>);
+};
+
+// Base class for all queue groups.
+class QueueGroup {
+ public:
+ // Constructs a queue group given its name and a unique hash of the name and
+ // type.
+ QueueGroup(const char *name, uint32_t hash) : name_(name), hash_(hash) {}
+
+ // Returns the name of the queue group.
+ const char *name() const { return name_.c_str(); }
+ // Returns a unique hash representing this instance of the queue group.
+ uint32_t hash() const { return hash_; }
+
+ private:
+ std::string name_;
+ uint32_t hash_;
+};
+
+} // namespace aos
+
+#include "aos/linux_code/queue-tmpl.h"
+
+#endif // AOS_COMMON_QUEUE_H_
diff --git a/aos/common/queue_test.cc b/aos/common/queue_test.cc
new file mode 100644
index 0000000..310301d
--- /dev/null
+++ b/aos/common/queue_test.cc
@@ -0,0 +1,324 @@
+#include <unistd.h>
+
+#include <memory>
+
+#include "gtest/gtest.h"
+#include "aos/common/test_queue.q.h"
+#include "aos/common/queue_testutils.h"
+#include "aos/common/util/thread.h"
+#include "aos/common/die.h"
+
+using ::aos::time::Time;
+
+namespace aos {
+namespace common {
+namespace testing {
+
+class QueueTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ SetDieTestMode(true);
+ }
+
+ GlobalCoreInstance my_core;
+ // Create a new instance of the test queue so that it invalidates the queue
+ // that it points to. Otherwise, we will have a pointer to shared memory that
+ // is no longer valid.
+ ::aos::Queue<TestingMessage> my_test_queue;
+
+ QueueTest() : my_test_queue(".aos.common.testing.test_queue") {}
+};
+
+class MyThread : public util::Thread {
+ public:
+ MyThread() : threaded_test_queue(".aos.common.testing.test_queue") {}
+
+ virtual void Run() {
+ threaded_test_queue.FetchNextBlocking();
+ EXPECT_TRUE(threaded_test_queue->test_bool);
+ EXPECT_EQ(0x971, threaded_test_queue->test_int);
+ }
+
+ ::aos::Queue<TestingMessage> threaded_test_queue;
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MyThread);
+};
+
+
+// Tests that we can send a message to another thread and it blocking receives
+// it at the correct time.
+TEST_F(QueueTest, FetchBlocking) {
+ MyThread t;
+ t.Start();
+ usleep(50000);
+ my_test_queue.MakeWithBuilder().test_bool(true).test_int(0x971).Send();
+ t.Join();
+ EXPECT_LE(t.threaded_test_queue.Age(), time::Time::InMS(57));
+}
+
+// Tests that we can send a message with the message pointer and get it back.
+TEST_F(QueueTest, SendMessage) {
+ ScopedMessagePtr<TestingMessage> msg = my_test_queue.MakeMessage();
+ msg->test_bool = true;
+ msg->test_int = 0x971;
+ msg.Send();
+
+ ASSERT_TRUE(my_test_queue.FetchLatest());
+ EXPECT_TRUE(my_test_queue->test_bool);
+ EXPECT_EQ(0x971, my_test_queue->test_int);
+}
+
+// Tests that we can send a message with the builder and get it back.
+TEST_F(QueueTest, SendWithBuilder) {
+ my_test_queue.MakeWithBuilder().test_bool(true).test_int(0x971).Send();
+
+ ASSERT_TRUE(my_test_queue.FetchLatest());
+ EXPECT_EQ(true, my_test_queue->test_bool);
+ EXPECT_EQ(0x971, my_test_queue->test_int);
+ EXPECT_EQ(true, my_test_queue.IsNewerThanMS(10000));
+}
+
+// Tests that multiple queue instances don't break each other.
+TEST_F(QueueTest, MultipleQueues) {
+ my_test_queue.MakeWithBuilder().test_bool(true).test_int(0x971).Send();
+ ASSERT_TRUE(my_test_queue.FetchLatest());
+ EXPECT_TRUE(my_test_queue.get());
+
+ {
+ ::aos::Queue<TestingMessage> my_other_test_queue(
+ ".aos.common.testing.queue_name");
+ my_other_test_queue.MakeMessage();
+ EXPECT_FALSE(my_other_test_queue.FetchLatest());
+ EXPECT_FALSE(my_test_queue.FetchLatest());
+ }
+
+ EXPECT_TRUE(my_test_queue.get());
+}
+
+// Tests that using a queue from multiple threads works correctly.
+TEST_F(QueueTest, MultipleThreads) {
+ my_test_queue.MakeWithBuilder().test_bool(true).test_int(0x971).Send();
+ ASSERT_TRUE(my_test_queue.FetchLatest());
+ my_test_queue.MakeWithBuilder().test_bool(true).test_int(0x254).Send();
+ EXPECT_EQ(0x971, my_test_queue->test_int);
+
+ ::aos::util::FunctionThread::RunInOtherThread([this]() {
+ ASSERT_TRUE(my_test_queue.FetchLatest());
+ EXPECT_EQ(0x254, my_test_queue->test_int);
+ });
+ EXPECT_EQ(0x254, my_test_queue->test_int);
+}
+
+// Makes sure that MakeWithBuilder zeros the message initially.
+// This might randomly succeed sometimes, but it will fail with asan if it
+// doesn't.
+TEST_F(QueueTest, BuilderZero) {
+ my_test_queue.MakeWithBuilder().Send();
+
+ ASSERT_TRUE(my_test_queue.FetchLatest());
+ EXPECT_FALSE(my_test_queue->test_bool);
+ EXPECT_EQ(0, my_test_queue->test_int);
+}
+
+// Tests that various pointer deref functions at least seem to work.
+TEST_F(QueueTest, PointerDeref) {
+ my_test_queue.MakeWithBuilder().test_bool(true).test_int(0x971).Send();
+
+ ASSERT_TRUE(my_test_queue.FetchLatest());
+ const TestingMessage *msg_ptr = my_test_queue.get();
+ ASSERT_NE(static_cast<TestingMessage*>(NULL), msg_ptr);
+ EXPECT_EQ(0x971, msg_ptr->test_int);
+ EXPECT_EQ(msg_ptr, &(*my_test_queue));
+}
+
+// Tests that FetchNext doesn't miss any messages.
+TEST_F(QueueTest, FetchNext) {
+ for (int i = 0; i < 10; ++i) {
+ my_test_queue.MakeWithBuilder().test_bool(true).test_int(i).Send();
+ }
+
+ for (int i = 0; i < 10; ++i) {
+ ASSERT_TRUE(my_test_queue.FetchNext());
+ EXPECT_EQ(i, my_test_queue->test_int);
+ }
+}
+
+// Tests that FetchLatest skips a missing message.
+TEST_F(QueueTest, FetchLatest) {
+ my_test_queue.MakeWithBuilder().test_bool(true).test_int(0x254).Send();
+ my_test_queue.MakeWithBuilder().test_bool(true).test_int(0x971).Send();
+
+ ASSERT_TRUE(my_test_queue.FetchLatest());
+ EXPECT_EQ(0x971, my_test_queue->test_int);
+}
+
+// Tests that FetchLatest works with multiple readers.
+TEST_F(QueueTest, FetchLatestMultiple) {
+ ::aos::Queue<TestingMessage> my_second_test_queue(
+ ".aos.common.testing.test_queue");
+ my_test_queue.MakeWithBuilder().test_bool(true).test_int(0x254).Send();
+ my_test_queue.MakeWithBuilder().test_bool(true).test_int(0x971).Send();
+
+ ASSERT_TRUE(my_test_queue.FetchLatest());
+ EXPECT_EQ(0x971, my_test_queue->test_int);
+ ASSERT_TRUE(my_second_test_queue.FetchLatest());
+ ASSERT_TRUE(my_second_test_queue.get() != NULL);
+ EXPECT_EQ(0x971, my_second_test_queue->test_int);
+}
+
+
+// Tests that fetching without a new message returns false.
+TEST_F(QueueTest, FetchLatestWithoutMessage) {
+ my_test_queue.MakeWithBuilder().test_bool(true).test_int(0x254).Send();
+ EXPECT_TRUE(my_test_queue.FetchLatest());
+ EXPECT_FALSE(my_test_queue.FetchLatest());
+ EXPECT_FALSE(my_test_queue.FetchLatest());
+ EXPECT_EQ(0x254, my_test_queue->test_int);
+}
+
+// Tests that fetching without a message returns false.
+TEST_F(QueueTest, FetchOnFreshQueue) {
+ EXPECT_FALSE(my_test_queue.FetchLatest());
+ EXPECT_EQ(static_cast<TestingMessage*>(NULL), my_test_queue.get());
+}
+
+// Tests that fetch next without a message returns false.
+TEST_F(QueueTest, FetchNextOnFreshQueue) {
+ EXPECT_FALSE(my_test_queue.FetchNext());
+ EXPECT_EQ(static_cast<TestingMessage*>(NULL), my_test_queue.get());
+}
+
+// Tests that fetch next without a new message returns false.
+TEST_F(QueueTest, FetchNextWithoutMessage) {
+ my_test_queue.MakeWithBuilder().test_bool(true).test_int(0x254).Send();
+ EXPECT_TRUE(my_test_queue.FetchNext());
+ EXPECT_FALSE(my_test_queue.FetchNext());
+ EXPECT_NE(static_cast<TestingMessage*>(NULL), my_test_queue.get());
+}
+
+// Tests that age makes some sense.
+TEST_F(QueueTest, Age) {
+ my_test_queue.MakeWithBuilder().test_bool(true).test_int(0x971).Send();
+
+ ASSERT_TRUE(my_test_queue.FetchLatest());
+ EXPECT_TRUE(my_test_queue.IsNewerThanMS(100));
+ const Time age = my_test_queue.Age();
+ EXPECT_EQ(0, age.sec());
+ EXPECT_GE(100000000, age.nsec());
+}
+
+
+class GroupTest : public ::testing::Test {
+ protected:
+ GlobalCoreInstance my_core;
+ // Create a new instance of the test group so that it invalidates the queue
+ // that it points to. Otherwise, we will have a pointer to shared memory that
+ // is no longer valid.
+ TwoQueues my_test_queuegroup;
+
+ GroupTest()
+ : my_test_queuegroup(".aos.common.testing.test_queuegroup",
+ 0x20561114,
+ ".aos.common.testing.test_queuegroup.first",
+ ".aos.common.testing.test_queuegroup.second") {}
+};
+
+// Tests that the hash gets preserved.
+TEST_F(GroupTest, Hash) {
+ EXPECT_EQ(static_cast<uint32_t>(0x20561114), my_test_queuegroup.hash());
+}
+
+// Tests that the hash works.
+TEST_F(GroupTest, RealHash) {
+ EXPECT_EQ(static_cast<uint32_t>(0x93596b2f), test_queuegroup.hash());
+}
+
+// Tests that name works.
+TEST_F(GroupTest, Name) {
+ EXPECT_EQ(std::string(".aos.common.testing.test_queuegroup"),
+ std::string(my_test_queuegroup.name()));
+}
+
+
+class MessageTest : public ::testing::Test {
+ public:
+ TestingMessage msg;
+};
+
+TEST_F(MessageTest, Zeroing) {
+ msg.test_bool = true;
+ msg.test_int = 0x254;
+ msg.SetTimeToNow();
+
+ msg.Zero();
+
+ EXPECT_FALSE(msg.test_bool);
+ EXPECT_EQ(0, msg.test_int);
+ EXPECT_EQ(0, msg.sent_time.sec());
+ EXPECT_EQ(0, msg.sent_time.nsec());
+}
+
+TEST_F(MessageTest, Size) {
+ EXPECT_EQ(static_cast<size_t>(13), msg.Size());
+}
+
+TEST_F(MessageTest, Serialize) {
+ char serialized_data[msg.Size()];
+ msg.test_bool = true;
+ msg.test_int = 0x254;
+ msg.SetTimeToNow();
+
+ msg.Serialize(serialized_data);
+
+ TestingMessage new_msg;
+ new_msg.Deserialize(serialized_data);
+
+ EXPECT_EQ(msg.test_bool, new_msg.test_bool);
+ EXPECT_EQ(msg.test_int, new_msg.test_int);
+ EXPECT_EQ(msg.sent_time, new_msg.sent_time);
+}
+
+// Tests that Print prints out a message nicely.
+TEST_F(MessageTest, Print) {
+ char printdata[1024];
+ msg.test_bool = true;
+ msg.test_int = 2056;
+ msg.sent_time = Time(971, 254);
+
+ std::string golden("971.000000254s, T, 2056");
+ EXPECT_EQ(golden.size(), msg.Print(printdata, sizeof(printdata)));
+
+ EXPECT_EQ(golden, std::string(printdata));
+}
+
+// Tests that the hash never changes. If it changes, then someone broke the
+// hash routine or changed the message declaration. Both changes need to be
+// validated by hand.
+TEST_F(MessageTest, Hash) {
+ EXPECT_EQ(static_cast<uint32_t>(0xc33651ac),
+ static_cast<uint32_t>(TestingMessage::kHash));
+}
+
+TEST_F(MessageTest, SetNow) {
+ msg.SetTimeToNow();
+ EXPECT_LE(msg.sent_time - Time::Now(), Time::InMS(20));
+}
+
+// Tests that EqualsNoTime works.
+TEST_F(MessageTest, EqualsNoTime) {
+ msg.test_bool = true;
+ msg.test_int = 971;
+ TestingMessage other;
+ other.test_int = 971;
+ EXPECT_FALSE(other.EqualsNoTime(msg));
+ EXPECT_FALSE(msg.EqualsNoTime(other));
+ other.test_bool = true;
+ EXPECT_TRUE(other.EqualsNoTime(msg));
+ EXPECT_TRUE(msg.EqualsNoTime(other));
+ msg.SetTimeToNow();
+ EXPECT_TRUE(msg.EqualsNoTime(other));
+}
+
+} // namespace testing
+} // namespace common
+} // namespace aos
diff --git a/aos/common/queue_testutils.cc b/aos/common/queue_testutils.cc
new file mode 100644
index 0000000..a40b5e9
--- /dev/null
+++ b/aos/common/queue_testutils.cc
@@ -0,0 +1,177 @@
+#include "aos/common/queue_testutils.h"
+
+#include <string.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include "gtest/gtest.h"
+
+#include "aos/common/queue.h"
+#include "aos/common/logging/logging_impl.h"
+#include "aos/common/once.h"
+#include "aos/common/mutex.h"
+
+using ::aos::logging::LogMessage;
+
+namespace aos {
+namespace common {
+namespace testing {
+namespace {
+
+class TestLogImplementation : public logging::HandleMessageLogImplementation {
+ public:
+ const ::std::vector<LogMessage> &messages() { return messages_; }
+
+ // This class has to be a singleton so that everybody can get access to the
+ // same instance to read out the messages etc.
+ static TestLogImplementation *GetInstance() {
+ static Once<TestLogImplementation> once(CreateInstance);
+ return once.Get();
+ }
+
+ // Clears out all of the messages already recorded.
+ void ClearMessages() {
+ ::aos::MutexLocker locker(&messages_mutex_);
+ messages_.clear();
+ }
+
+ // Prints out all of the messages (like when a test fails).
+ void PrintAllMessages() {
+ ::aos::MutexLocker locker(&messages_mutex_);
+ for (auto it = messages_.begin(); it != messages_.end(); ++it) {
+ logging::internal::PrintMessage(stdout, *it);
+ }
+ }
+
+ void SetOutputFile(const char *filename) {
+ if (strcmp("-", filename) != 0) {
+ FILE *newfile = fopen(filename, "w");
+
+ if (newfile) {
+ output_file_ = newfile;
+ }
+ }
+ }
+
+ void PrintMessagesAsTheyComeIn() { print_as_messages_come_in_ = true; }
+
+ private:
+ TestLogImplementation() {}
+ ~TestLogImplementation() {
+ if (output_file_ != stdout) {
+ fclose(output_file_);
+ }
+ }
+
+ static TestLogImplementation *CreateInstance() {
+ return new TestLogImplementation();
+ }
+
+ virtual void HandleMessage(const LogMessage &message) override {
+ ::aos::MutexLocker locker(&messages_mutex_);
+ if (message.level == FATAL || print_as_messages_come_in_) {
+ logging::internal::PrintMessage(output_file_, message);
+ }
+
+ messages_.push_back(message);
+ }
+
+ ::std::vector<LogMessage> messages_;
+ bool print_as_messages_come_in_ = false;
+ FILE *output_file_ = stdout;
+ ::aos::Mutex messages_mutex_;
+};
+
+class MyTestEventListener : public ::testing::EmptyTestEventListener {
+ virtual void OnTestStart(const ::testing::TestInfo &/*test_info*/) {
+ TestLogImplementation::GetInstance()->ClearMessages();
+ }
+ virtual void OnTestEnd(const ::testing::TestInfo &test_info) {
+ if (test_info.result()->Failed()) {
+ printf("Test %s failed. Use '--print-logs' to see all log messages.\n",
+ test_info.name());
+ }
+ }
+
+ virtual void OnTestPartResult( const ::testing::TestPartResult &result) {
+ if (result.failed()) {
+ const char *failure_type = "unknown";
+ switch (result.type()) {
+ case ::testing::TestPartResult::Type::kNonFatalFailure:
+ failure_type = "EXPECT";
+ break;
+ case ::testing::TestPartResult::Type::kFatalFailure:
+ failure_type = "ASSERT";
+ break;
+ case ::testing::TestPartResult::Type::kSuccess:
+ break;
+ }
+ log_do(ERROR, "%s: %d: gtest %s failure\n%s\n",
+ result.file_name(),
+ result.line_number(),
+ failure_type,
+ result.message());
+ }
+ }
+};
+
+void *DoEnableTestLogging() {
+ logging::Init();
+ logging::AddImplementation(TestLogImplementation::GetInstance());
+
+ ::testing::UnitTest::GetInstance()->listeners().Append(
+ new MyTestEventListener());
+
+ return NULL;
+}
+
+Once<void> enable_test_logging_once(DoEnableTestLogging);
+
+const size_t kCoreSize = 0x100000;
+
+void TerminateExitHandler() {
+ _exit(EXIT_SUCCESS);
+}
+
+} // namespace
+
+GlobalCoreInstance::GlobalCoreInstance() {
+ global_core = &global_core_data_;
+ global_core->owner = true;
+ // Use mmap(2) manually instead of through malloc(3) so that we can pass
+ // MAP_SHARED which allows forked processes to communicate using the
+ // "shared" memory.
+ void *memory = mmap(NULL, kCoreSize, PROT_READ | PROT_WRITE,
+ MAP_SHARED | MAP_ANONYMOUS, -1, 0);
+ CHECK_NE(memory, MAP_FAILED);
+
+ aos_core_use_address_as_shared_mem(memory, kCoreSize);
+
+ EnableTestLogging();
+}
+
+GlobalCoreInstance::~GlobalCoreInstance() {
+ PCHECK(munmap(global_core->mem_struct, kCoreSize));
+ global_core = NULL;
+}
+
+void EnableTestLogging() {
+ enable_test_logging_once.Get();
+}
+
+void PreventExit() {
+ CHECK_EQ(atexit(TerminateExitHandler), 0);
+}
+
+void SetLogFileName(const char* filename) {
+ TestLogImplementation::GetInstance()->SetOutputFile(filename);
+}
+
+void ForcePrintLogsDuringTests() {
+ TestLogImplementation::GetInstance()->PrintMessagesAsTheyComeIn();
+}
+
+} // namespace testing
+} // namespace common
+} // namespace aos
diff --git a/aos/common/queue_testutils.h b/aos/common/queue_testutils.h
new file mode 100644
index 0000000..fb0ffcd
--- /dev/null
+++ b/aos/common/queue_testutils.h
@@ -0,0 +1,52 @@
+#ifndef AOS_COMMON_QUEUE_TESTUTILS_H_
+#define AOS_COMMON_QUEUE_TESTUTILS_H_
+
+#include "aos/linux_code/ipc_lib/shared_mem.h"
+
+// This file has some general helper functions for dealing with testing things
+// that use shared memory etc.
+
+namespace aos {
+namespace common {
+namespace testing {
+
+// Manages creating and cleaning up "shared memory" which works within this
+// process and any that it fork(2)s.
+class GlobalCoreInstance {
+ public:
+ // Calls EnableTestLogging().
+ GlobalCoreInstance();
+ ~GlobalCoreInstance();
+
+ private:
+ struct aos_core global_core_data_;
+};
+
+// Enables the logging framework for use during a gtest test.
+// It will print out all WARNING and above messages all of the time. It will
+// also print out all log messages when a test fails.
+// This function only needs to be called once in each process (after gtest is
+// initialized), however it can be called more than that.
+void EnableTestLogging();
+
+// Registers an exit handler (using atexit(3)) which will call _exit(2).
+// Intended to be called in a freshly fork(2)ed process where it will run before
+// any other exit handlers that were already registered and prevent them from
+// being run.
+void PreventExit();
+
+// Redirect the messages enabled by EnableTestLogging() function to a file.
+// By default the messages are printed to standard output.
+void SetLogFileName(const char* filename);
+
+// Force the messages to be printed as they are handled by the logging
+// framework. This can be useful for tests that hang where no messages would
+// otherwise be printed. This is also useful for tests that do pass, but where
+// we want to use graphing tools to verify what's happening.
+void ForcePrintLogsDuringTests();
+
+} // namespace testing
+} // namespace common
+} // namespace aos
+
+#endif // AOS_COMMON_QUEUE_TESTUTILS_H_
diff --git a/aos/common/queue_testutils_test.cc b/aos/common/queue_testutils_test.cc
new file mode 100644
index 0000000..2defe74
--- /dev/null
+++ b/aos/common/queue_testutils_test.cc
@@ -0,0 +1,31 @@
+#include "aos/common/queue_testutils.h"
+
+#include <thread>
+
+#include "gtest/gtest.h"
+
+#include "aos/common/logging/logging.h"
+
+namespace aos {
+namespace common {
+namespace testing {
+
+// Tests logging from multiple threads.
+// tsan used to complain about this.
+TEST(QueueTestutilsTest, MultithreadedLog) {
+ EnableTestLogging();
+
+ ::std::thread thread([]() {
+ for (int i = 0; i < 1000; ++i) {
+ LOG(INFO, "test from thread\n");
+ }
+ });
+ for (int i = 0; i < 1000; ++i) {
+ LOG(INFO, "not from thread\n");
+ }
+ thread.join();
+}
+
+} // namespace testing
+} // namespace common
+} // namespace aos
diff --git a/aos/common/queue_types.cc b/aos/common/queue_types.cc
new file mode 100644
index 0000000..9e9505d
--- /dev/null
+++ b/aos/common/queue_types.cc
@@ -0,0 +1,442 @@
+#include "aos/common/queue_types.h"
+
+#include <inttypes.h>
+
+#include <memory>
+#include <unordered_map>
+
+#include "aos/common/byteorder.h"
+#include "aos/linux_code/ipc_lib/shared_mem.h"
+#include "aos/common/logging/logging.h"
+#include "aos/linux_code/ipc_lib/core_lib.h"
+#include "aos/common/mutex.h"
+
+namespace aos {
+
+ssize_t MessageType::Serialize(char *buffer, size_t max_bytes) const {
+ char *const buffer_start = buffer;
+ uint16_t fields_size = 0;
+ for (int i = 0; i < number_fields; ++i) {
+ fields_size += sizeof(fields[i]->type);
+ fields_size += sizeof(fields[i]->length);
+ fields_size += sizeof(uint16_t);
+ fields_size += fields[i]->name.size();
+ }
+ if (max_bytes < sizeof(id) + sizeof(super_size) + sizeof(uint16_t) +
+ sizeof(number_fields) + name.size() + fields_size) {
+ return -1;
+ }
+
+ uint16_t length;
+
+ to_network(&super_size, buffer);
+ buffer += sizeof(super_size);
+ to_network(&id, buffer);
+ buffer += sizeof(id);
+ length = name.size();
+ to_network(&length, buffer);
+ buffer += sizeof(length);
+ to_network(&number_fields, buffer);
+ buffer += sizeof(number_fields);
+ memcpy(buffer, name.data(), length);
+ buffer += name.size();
+
+ for (int i = 0; i < number_fields; ++i) {
+ to_network(&fields[i]->type, buffer);
+ buffer += sizeof(fields[i]->type);
+ to_network(&fields[i]->length, buffer);
+ buffer += sizeof(fields[i]->length);
+ length = fields[i]->name.size();
+ to_network(&length, buffer);
+ buffer += sizeof(length);
+ memcpy(buffer, fields[i]->name.data(), length);
+ buffer += length;
+ }
+
+ return buffer - buffer_start;
+}
+
+MessageType *MessageType::Deserialize(const char *buffer, size_t *bytes,
+ bool deserialize_length) {
+ uint16_t name_length;
+ decltype(MessageType::super_size) super_size;
+ decltype(MessageType::id) id;
+ decltype(MessageType::number_fields) number_fields;
+ if (*bytes < sizeof(super_size) + sizeof(id) + sizeof(name_length) +
+ sizeof(number_fields)) {
+ return nullptr;
+ }
+ *bytes -= sizeof(super_size) + sizeof(id) + sizeof(name_length) +
+ sizeof(number_fields);
+
+ to_host(buffer, &super_size);
+ buffer += sizeof(super_size);
+ to_host(buffer, &id);
+ buffer += sizeof(id);
+ to_host(buffer, &name_length);
+ buffer += sizeof(name_length);
+ to_host(buffer, &number_fields);
+ buffer += sizeof(number_fields);
+
+ if (*bytes < name_length) {
+ return nullptr;
+ }
+ *bytes -= name_length;
+
+ Field **fields = new Field *[number_fields];
+ ::std::unique_ptr<MessageType> r(
+ new MessageType(super_size, id, ::std::string(buffer, name_length),
+ number_fields, fields));
+ buffer += name_length;
+
+ for (int i = 0; i < number_fields; ++i) {
+ uint16_t field_name_length;
+ if (*bytes < sizeof(fields[i]->type) + sizeof(field_name_length) +
+ (deserialize_length ? sizeof(fields[i]->length) : 0)) {
+ return nullptr;
+ }
+ *bytes -= sizeof(fields[i]->type) + sizeof(field_name_length);
+
+ to_host(buffer, &fields[i]->type);
+ buffer += sizeof(fields[i]->type);
+ if (deserialize_length) {
+ to_host(buffer, &fields[i]->length);
+ buffer += sizeof(fields[i]->length);
+ *bytes -= sizeof(fields[i]->length);
+ }
+ to_host(buffer, &field_name_length);
+ buffer += sizeof(field_name_length);
+
+ if (*bytes < field_name_length) {
+ return nullptr;
+ }
+ *bytes -= field_name_length;
+ fields[i]->name = ::std::string(buffer, field_name_length);
+ buffer += field_name_length;
+ }
+
+ return r.release();
+}
+
+bool PrintArray(char *output, size_t *output_bytes, const void *input,
+ size_t *input_bytes, uint32_t type_id, uint32_t length) {
+ if (*output_bytes < 1) return false;
+ *output_bytes -= 1;
+ *(output++) = '[';
+
+ bool first = true;
+ for (uint32_t i = 0; i < length; ++i) {
+ if (first) {
+ first = false;
+ } else {
+ if (*output_bytes < 2) return false;
+ *output_bytes -= 2;
+ *(output++) = ',';
+ *(output++) = ' ';
+ }
+
+ const size_t output_bytes_before = *output_bytes,
+ input_bytes_before = *input_bytes;
+ if (MessageType::IsPrimitive(type_id)) {
+ if (!PrintField(output, output_bytes, input, input_bytes, type_id)) {
+ return false;
+ }
+ } else {
+ if (!PrintMessage(output, output_bytes, input, input_bytes,
+ type_cache::Get(type_id))) {
+ return false;
+ }
+ // Ignore the trailing '\0' that the subcall put on.
+ *output_bytes += 1;
+ }
+
+ // Update the input and output pointers.
+ output += output_bytes_before - *output_bytes;
+ input =
+ static_cast<const char *>(input) + input_bytes_before - *input_bytes;
+ }
+ if (*output_bytes < 2) return false;
+ *output_bytes -= 2;
+ *(output++) = ']';
+ *(output++) = '\0';
+ return true;
+}
+
+bool PrintMessage(char *output, size_t *output_bytes, const void *input,
+ size_t *input_bytes, const MessageType &type) {
+ *input_bytes -= type.super_size;
+ input = static_cast<const char *>(input) + type.super_size;
+
+ if (*output_bytes < type.name.size() + 1) return false;
+ *output_bytes -= type.name.size() + 1;
+ memcpy(output, type.name.data(), type.name.size());
+ output += type.name.size();
+ *(output++) = '{';
+
+ bool first = true;
+ for (int i = 0; i < type.number_fields; ++i) {
+ if (first) {
+ first = false;
+ } else {
+ if (*output_bytes < 2) return false;
+ *output_bytes -= 2;
+ *(output++) = ',';
+ *(output++) = ' ';
+ }
+
+ if (*output_bytes < type.fields[i]->name.size() + 1) return false;
+ *output_bytes -= type.fields[i]->name.size() + 1;
+ memcpy(output, type.fields[i]->name.data(), type.fields[i]->name.size());
+ output += type.fields[i]->name.size();
+ *(output++) = ':';
+
+ const size_t output_bytes_before = *output_bytes,
+ input_bytes_before = *input_bytes;
+ const uint32_t type_id = type.fields[i]->type;
+ if (type.fields[i]->length > 0) {
+ if (!PrintArray(output, output_bytes, input, input_bytes, type_id,
+ type.fields[i]->length)) {
+ return false;
+ }
+ // Ignore the trailing '\0' that the subcall put on.
+ *output_bytes += 1;
+ } else if (MessageType::IsPrimitive(type_id)) {
+ if (!PrintField(output, output_bytes, input, input_bytes, type_id)) {
+ return false;
+ }
+ } else {
+ if (!PrintMessage(output, output_bytes, input, input_bytes,
+ type_cache::Get(type_id))) {
+ return false;
+ }
+ // Ignore the trailing '\0' that the subcall put on.
+ *output_bytes += 1;
+ }
+
+ // Update the input and output pointers.
+ output += output_bytes_before - *output_bytes;
+ input =
+ static_cast<const char *>(input) + input_bytes_before - *input_bytes;
+ }
+ if (*output_bytes < 2) return false;
+ *output_bytes -= 2;
+ *(output++) = '}';
+ *(output++) = '\0';
+ return true;
+}
+
+bool PrintMatrix(char *output, size_t *output_bytes, const void *input,
+ uint32_t type_id, int rows, int cols) {
+ CHECK(MessageType::IsPrimitive(type_id));
+ const size_t element_size = MessageType::Sizeof(type_id);
+
+ if (*output_bytes < 1) return false;
+ *output_bytes -= 1;
+ *(output++) = '[';
+
+ bool first_row = true;
+ for (int row = 0; row < rows; ++row) {
+ if (first_row) {
+ first_row = false;
+ } else {
+ if (*output_bytes < 2) return false;
+ *output_bytes -= 2;
+ *(output++) = ',';
+ *(output++) = ' ';
+ }
+
+ if (*output_bytes < 1) return false;
+ *output_bytes -= 1;
+ *(output++) = '[';
+
+ bool first_col = true;
+ for (int col = 0; col < cols; ++col) {
+ if (first_col) {
+ first_col = false;
+ } else {
+ if (*output_bytes < 2) return false;
+ *output_bytes -= 2;
+ *(output++) = ',';
+ *(output++) = ' ';
+ }
+
+ const size_t output_bytes_before = *output_bytes;
+ size_t input_bytes = element_size;
+ if (!PrintField(output, output_bytes,
+ static_cast<const char *>(input) +
+ (row + col * rows) * element_size,
+ &input_bytes, type_id)) {
+ return false;
+ }
+ CHECK_EQ(0u, input_bytes);
+ // Update the output pointer.
+ output += output_bytes_before - *output_bytes;
+ }
+
+ if (*output_bytes < 1) return false;
+ *output_bytes -= 1;
+ *(output++) = ']';
+ }
+ if (*output_bytes < 2) return false;
+ *output_bytes -= 2;
+ *(output++) = ']';
+ *(output++) = '\0';
+ return true;
+}
+
+void SerializeMatrix(int type_id, void *output_void, const void *input_void,
+ int rows, int cols) {
+ char *const output = static_cast<char *>(output_void);
+ const char *const input = static_cast<const char *>(input_void);
+
+ CHECK(MessageType::IsPrimitive(type_id));
+ const size_t element_size = MessageType::Sizeof(type_id);
+
+ for (int i = 0; i < rows * cols; ++i) {
+ switch(element_size) {
+ case 1:
+ to_network<1>(&input[i * element_size], &output[i * element_size]);
+ break;
+ case 2:
+ to_network<2>(&input[i * element_size], &output[i * element_size]);
+ break;
+ case 4:
+ to_network<4>(&input[i * element_size], &output[i * element_size]);
+ break;
+ case 8:
+ to_network<8>(&input[i * element_size], &output[i * element_size]);
+ break;
+ default:
+ LOG(FATAL, "illegal primitive type size %zu\n", element_size);
+ }
+ }
+}
+
+namespace type_cache {
+namespace {
+
+struct CacheEntry {
+ const MessageType &type;
+ bool in_shm;
+
+ CacheEntry(const MessageType &type, bool in_shm)
+ : type(type), in_shm(in_shm) {}
+};
+
+struct ShmType {
+ uint32_t id;
+ volatile ShmType *next;
+
+ size_t serialized_size;
+ char serialized[];
+};
+
+::std::unordered_map<uint32_t, CacheEntry> cache;
+::aos::Mutex cache_lock;
+
+} // namespace
+
+void Add(const MessageType &type) {
+ ::aos::MutexLocker locker(&cache_lock);
+ if (cache.count(type.id) == 0) {
+ cache.emplace(::std::piecewise_construct, ::std::forward_as_tuple(type.id),
+ ::std::forward_as_tuple(type, false));
+ }
+}
+
+const MessageType &Get(uint32_t type_id) {
+ ::aos::MutexLocker locker(&cache_lock);
+
+ {
+ const auto cached = cache.find(type_id);
+ if (cached != cache.end()) {
+ return cached->second.type;
+ }
+ }
+
+ if (aos_core_is_init()) {
+ // No need to lock because the only thing that happens is somebody adds on
+ // to the end, and they shouldn't be adding the one we're looking for.
+ const volatile ShmType *c = static_cast<volatile ShmType *>(
+ global_core->mem_struct->queue_types.pointer);
+ while (c != nullptr) {
+ if (c->id == type_id) {
+ size_t bytes = c->serialized_size;
+ MessageType *type = MessageType::Deserialize(
+ const_cast<const char *>(c->serialized), &bytes);
+ cache.emplace(::std::piecewise_construct,
+ ::std::forward_as_tuple(type_id),
+ ::std::forward_as_tuple(*type, true));
+ return *type;
+ }
+ c = c->next;
+ }
+ } else {
+ LOG(INFO, "FYI: no shm. going to LOG(FATAL) now\n");
+ }
+
+ LOG(FATAL, "MessageType for id 0x%" PRIx32 " not found\n", type_id);
+}
+
+void AddShm(uint32_t type_id) {
+ if (!aos_core_is_init()) {
+ LOG(FATAL, "can't AddShm(%" PRIu32 ") without shm!\n", type_id);
+ }
+
+ const MessageType::Field **fields;
+ int number_fields;
+ {
+ ::aos::MutexLocker locker(&cache_lock);
+ CacheEntry &cached = cache.at(type_id);
+ if (cached.in_shm) return;
+
+ fields = cached.type.fields;
+ number_fields = cached.type.number_fields;
+
+ if (mutex_lock(&global_core->mem_struct->queue_types.lock) != 0) {
+ LOG(FATAL, "locking queue_types lock failed\n");
+ }
+ volatile ShmType *current = static_cast<volatile ShmType *>(
+ global_core->mem_struct->queue_types.pointer);
+ if (current != nullptr) {
+ while (true) {
+ if (current->id == type_id) {
+ cached.in_shm = true;
+ mutex_unlock(&global_core->mem_struct->queue_types.lock);
+ return;
+ }
+ if (current->next == nullptr) break;
+ current = current->next;
+ }
+ }
+ char buffer[768];
+ ssize_t size = cached.type.Serialize(buffer, sizeof(buffer));
+ if (size == -1) {
+ LOG(FATAL, "type %s is too big to fit into %zd bytes\n",
+ cached.type.name.c_str(), sizeof(buffer));
+ }
+
+ volatile ShmType *shm =
+ static_cast<volatile ShmType *>(shm_malloc(sizeof(ShmType) + size));
+ shm->id = type_id;
+ shm->next = nullptr;
+ shm->serialized_size = size;
+ memcpy(const_cast<char *>(shm->serialized), buffer, size);
+
+ if (current == NULL) {
+ global_core->mem_struct->queue_types.pointer = const_cast<ShmType *>(shm);
+ } else {
+ current->next = shm;
+ }
+ mutex_unlock(&global_core->mem_struct->queue_types.lock);
+ }
+
+ for (int i = 0; i < number_fields; ++i) {
+ if (!MessageType::IsPrimitive(fields[i]->type)) {
+ AddShm(fields[i]->type);
+ }
+ }
+}
+
+} // namespace type_cache
+} // namespace aos
diff --git a/aos/common/queue_types.h b/aos/common/queue_types.h
new file mode 100644
index 0000000..be943c1
--- /dev/null
+++ b/aos/common/queue_types.h
@@ -0,0 +1,148 @@
+#ifndef AOS_COMMON_QUEUE_TYPES_H_
+#define AOS_COMMON_QUEUE_TYPES_H_
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <initializer_list>
+#include <string>
+
+#include "aos/common/macros.h"
+#include "aos/common/byteorder.h"
+
+namespace aos {
+
+// The type IDs this uses are 2 parts: a 16 bit size and a 16 bit hash. Sizes
+// for primitive types are stored with 8192 (0x2000) added.
+//
+// Serializing/deserializing includes all of the fields too.
+struct MessageType {
+ struct Field {
+ // The type ID for the type of this field.
+ uint32_t type;
+ // The length of the array if it is one or 0.
+ uint32_t length;
+ ::std::string name;
+ };
+
+ // Takes ownership of all the Field pointers in fields_initializer.
+ MessageType(size_t super_size, uint32_t id, const ::std::string &name,
+ ::std::initializer_list<const Field *> fields_initializer)
+ : super_size(super_size), id(id), name(name) {
+ number_fields = fields_initializer.size();
+ fields = new const Field *[number_fields];
+ int i = 0;
+ for (const Field *c : fields_initializer) {
+ fields[i++] = c;
+ }
+ }
+
+ ~MessageType() {
+ for (int i = 0; i < number_fields; ++i) {
+ delete fields[i];
+ }
+ delete[] fields;
+ }
+
+ // Returns -1 if max_bytes is too small.
+ ssize_t Serialize(char *buffer, size_t max_bytes) const;
+ // bytes should start out as the number of bytes available in buffer and gets
+ // reduced by the number actually read before returning.
+ // deserialize_length is whether to look for a length field in the serialized
+ // data.
+ // Returns a new instance allocated with new or nullptr for error.
+ static MessageType *Deserialize(const char *buffer, size_t *bytes,
+ bool deserialize_length = true);
+
+ static bool IsPrimitive(uint32_t type_id) {
+ return (type_id & 0x2000) != 0;
+ }
+
+ static unsigned int Sizeof(uint32_t type_id) {
+ if (IsPrimitive(type_id)) {
+ return (type_id & 0xFFFF) - 0x2000;
+ } else {
+ return type_id & 0xFFFF;
+ }
+ }
+
+ // How many (serialized) bytes the superclass takes up.
+ uint16_t super_size;
+ // The type ID for this.
+ uint32_t id;
+ ::std::string name;
+
+ uint16_t number_fields;
+ const Field **fields;
+
+ private:
+ // Internal constructor for Deserialize to use.
+ MessageType(uint16_t super_size, uint32_t id, ::std::string &&name,
+ uint16_t number_fields, Field **fields)
+ : super_size(super_size),
+ id(id),
+ name(name),
+ number_fields(number_fields),
+ fields(const_cast<const Field **>(fields)) {
+ for (int i = 0; i < number_fields; ++i) {
+ fields[i] = new Field();
+ }
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(MessageType);
+};
+
+// The arguments are the same for both of these.
+//
+// output is where to write the text representation ('\0'-terminated
+// human-readable string).
+// output_bytes should point to the number of bytes available to write in
+// output. It will be reduced by the number of bytes that were actually written.
+// input is where to read the data in from (in network byte order, aka from
+// Serialize).
+// input_bytes should point to the number of bytes available to read from input.
+// It will be reduced by the number of bytes that were actually read.
+// type is which type to print.
+// Returns true for success and false for not.
+//
+// Prints the value from 1 primitive message field into output.
+// The implementation of this is generated by the ruby code.
+// Does not include a trailing '\0' in what it subtracts from *output_bytes.
+bool PrintField(char *output, size_t *output_bytes, const void *input,
+ size_t *input_bytes, uint32_t type)
+ __attribute__((warn_unused_result));
+// Prints a whole message into output.
+// This calls itself recursively and PrintField to print out the whole thing.
+bool PrintMessage(char *output, size_t *output_bytes, const void *input,
+ size_t *input_bytes, const MessageType &type)
+ __attribute__((warn_unused_result));
+// Calls PrintField to print out a matrix of values.
+bool PrintMatrix(char *output, size_t *output_bytes, const void *input,
+ uint32_t type, int rows, int cols);
+
+// "Serializes" a matrix (basically just converts to network byte order). The
+// result can be passed to PrintMatrix.
+void SerializeMatrix(int type_id, void *output_void, const void *input_void,
+ int rows, int cols);
+
+// Implements a cache of types which generally works per-process but can (when
+// instructed) put a type in shared memory which other processes will
+// automatically be able to retrieve.
+// All of these functions are thread-safe.
+namespace type_cache {
+
+// Makes sure a type is in the type cache. This will store a reference to type.
+// The types of any non-primitive fields of type must already be added.
+void Add(const MessageType &type);
+// Retrieves a type from the type cache or shm. LOG(FATAL)s if it can't find it.
+const MessageType &Get(uint32_t type_id);
+// Makes sure a type is in the list in shm. Add must have already been called
+// for type.
+// Also adds the types of any non-primitive fields of type.
+void AddShm(uint32_t type_id);
+
+} // namespace type_cache
+} // namespace aos
+
+#endif // AOS_COMMON_QUEUE_TYPES_H_
diff --git a/aos/common/queue_types_test.cc b/aos/common/queue_types_test.cc
new file mode 100644
index 0000000..868820f
--- /dev/null
+++ b/aos/common/queue_types_test.cc
@@ -0,0 +1,274 @@
+#include "aos/common/queue_types.h"
+
+#include <memory>
+#include <limits>
+#include <string>
+
+#include "gtest/gtest.h"
+
+#include "aos/common/test_queue.q.h"
+#include "aos/common/byteorder.h"
+#include "aos/queue_primitives.h"
+#include "aos/common/logging/logging.h"
+#include "aos/common/queue_testutils.h"
+
+using ::aos::common::testing::Structure;
+using ::aos::common::testing::MessageWithStructure;
+using ::aos::common::testing::OtherTestingMessage;
+using ::aos::common::testing::MessageWithArrays;
+
+namespace aos {
+namespace testing {
+
+typedef MessageType::Field Field;
+
+static const MessageType kTestType1(5, 0x1234, "TestType1",
+ {new Field{0, 0, "field1"},
+ new Field{0, 0, "field2"},
+ new Field{0, 0, "field3"}});
+
+class QueueTypesTest : public ::testing::Test {
+ public:
+ QueueTypesTest() {
+ ::aos::common::testing::EnableTestLogging();
+ }
+
+ ::testing::AssertionResult Equal(const MessageType &l, const MessageType &r) {
+ using ::testing::AssertionFailure;
+ if (l.id != r.id) {
+ return AssertionFailure() << "id " << l.id << " != " << r.id;
+ }
+ if (l.name != r.name) {
+ return AssertionFailure() << "name '" << l.name << "' != '" << r.name
+ << "'";
+ }
+ if (l.number_fields != r.number_fields) {
+ return AssertionFailure() << "number_fields " << l.number_fields
+ << " != " << r.number_fields;
+ }
+ for (int i = 0; i < l.number_fields; ++i) {
+ SCOPED_TRACE("field " + ::std::to_string(i));
+ if (l.fields[i]->type != r.fields[i]->type) {
+ return AssertionFailure() << "type " << l.fields[i]->type
+ << " != " << r.fields[i]->type;
+ }
+ if (l.fields[i]->name != r.fields[i]->name) {
+ return AssertionFailure() << "name '" << l.fields[i]->name << "' != '"
+ << r.fields[i]->name << "'";
+ }
+ }
+ return ::testing::AssertionSuccess();
+ }
+};
+
+TEST_F(QueueTypesTest, Serialization) {
+ char buffer[512];
+ ssize_t size;
+ size_t out_size;
+ ::std::unique_ptr<MessageType> deserialized;
+
+ size = kTestType1.Serialize(buffer, sizeof(buffer));
+ ASSERT_GT(size, 1);
+ ASSERT_LE(static_cast<size_t>(size), sizeof(buffer));
+
+ out_size = size;
+ deserialized.reset(MessageType::Deserialize(buffer, &out_size));
+ EXPECT_EQ(0u, out_size);
+ EXPECT_TRUE(Equal(kTestType1, *deserialized));
+
+ out_size = size - 1;
+ deserialized.reset(MessageType::Deserialize(buffer, &out_size));
+ EXPECT_EQ(nullptr, deserialized.get());
+
+ out_size = size + 1;
+ ASSERT_LE(out_size, sizeof(buffer));
+ deserialized.reset(MessageType::Deserialize(buffer, &out_size));
+ EXPECT_EQ(1u, out_size);
+ EXPECT_TRUE(Equal(kTestType1, *deserialized));
+}
+
+class PrintMessageTest : public ::testing::Test {
+ public:
+ char input[128], output[256];
+ size_t input_bytes, output_bytes;
+};
+
+class PrintFieldTest : public PrintMessageTest {
+ public:
+ template <typename T>
+ void TestInteger(T value, ::std::string result) {
+ input_bytes = sizeof(value);
+ to_network(&value, input);
+ output_bytes = sizeof(output);
+ ASSERT_TRUE(
+ PrintField(output, &output_bytes, input, &input_bytes, TypeID<T>::id));
+ EXPECT_EQ(0u, input_bytes);
+ EXPECT_EQ(sizeof(output) - result.size(), output_bytes);
+ EXPECT_EQ(result, ::std::string(output, sizeof(output) - output_bytes));
+ }
+
+ template <typename T>
+ void TestAllIntegers(T multiple) {
+ for (T i = ::std::numeric_limits<T>::min();
+ i < ::std::numeric_limits<T>::max() - multiple; i += multiple) {
+ TestInteger<T>(i, ::std::to_string(i));
+ }
+ }
+};
+
+TEST_F(PrintFieldTest, Basic) {
+ TestInteger<uint16_t>(971, "971");
+ TestInteger<uint8_t>(8, "8");
+ TestInteger<uint8_t>(254, "254");
+ TestInteger<uint8_t>(0, "0");
+ TestInteger<int8_t>(254, "-2");
+ TestInteger<int8_t>(67, "67");
+ TestInteger<int8_t>(0, "0");
+
+ input_bytes = 1;
+ input[0] = 1;
+ output_bytes = sizeof(output);
+ ASSERT_TRUE(PrintField(output, &output_bytes, input, &input_bytes,
+ queue_primitive_types::bool_p));
+ EXPECT_EQ(0u, input_bytes);
+ EXPECT_EQ(sizeof(output) - 1, output_bytes);
+ EXPECT_EQ(::std::string("T"),
+ ::std::string(output, sizeof(output) - output_bytes));
+
+ input_bytes = 1;
+ input[0] = 0;
+ output_bytes = sizeof(output);
+ ASSERT_TRUE(PrintField(output, &output_bytes, input, &input_bytes,
+ queue_primitive_types::bool_p));
+ EXPECT_EQ(0u, input_bytes);
+ EXPECT_EQ(sizeof(output) - 1, output_bytes);
+ EXPECT_EQ(::std::string("f"),
+ ::std::string(output, sizeof(output) - output_bytes));
+}
+
+// Runs through lots of integers and makes sure PrintField gives the same result
+// as ::std::to_string.
+TEST_F(PrintFieldTest, Integers) {
+ TestAllIntegers<uint8_t>(1);
+ TestAllIntegers<int8_t>(1);
+ TestAllIntegers<uint16_t>(1);
+ TestAllIntegers<int16_t>(3);
+ TestAllIntegers<uint32_t>(43129);
+ TestAllIntegers<int32_t>(654321);
+ TestAllIntegers<uint64_t>(123456789101112);
+ TestAllIntegers<int64_t>(91011121249856532);
+}
+
+// Tests PrintField with trailing input bytes and only 1 extra output byte.
+TEST_F(PrintFieldTest, OtherSizes) {
+ static const float kData = 16.78;
+ static const ::std::string kString("16.780001");
+ static const size_t kExtraInputBytes = 4;
+ input_bytes = sizeof(kData) + kExtraInputBytes;
+ to_network(&kData, input);
+ output_bytes = kString.size() + 1;
+ CHECK_LE(output_bytes, sizeof(output));
+ ASSERT_TRUE(PrintField(output, &output_bytes, input, &input_bytes,
+ Structure::GetType()->fields[2]->type));
+ EXPECT_EQ(kExtraInputBytes, input_bytes);
+ EXPECT_EQ(1u, output_bytes);
+ EXPECT_EQ(kString, ::std::string(output));
+}
+
+TEST_F(PrintFieldTest, InputTooSmall) {
+ static const float kData = 0;
+ input_bytes = sizeof(kData) - 1;
+ output_bytes = sizeof(output);
+ EXPECT_FALSE(PrintField(output, &output_bytes, input, &input_bytes,
+ Structure::GetType()->fields[2]->type));
+}
+
+TEST_F(PrintFieldTest, OutputTooSmall) {
+ static const uint16_t kData = 12345;
+ input_bytes = sizeof(input);
+ to_network(&kData, input);
+ output_bytes = 4;
+ EXPECT_FALSE(PrintField(output, &output_bytes, input, &input_bytes,
+ Structure::GetType()->fields[1]->type));
+}
+
+static const OtherTestingMessage kTestMessage1(true, 8971, 3.2);
+static const ::std::string kTestMessage1String =
+ ".aos.common.testing.OtherTestingMessage{test_bool:T, test_int:8971"
+ ", test_double:3.200000}";
+static const Structure kTestStructure1(false, 973, 8.56);
+static const ::std::string kTestStructure1String =
+ ".aos.common.testing.Structure{struct_bool:f, struct_int:973"
+ ", struct_float:8.560000}";
+static const ::aos::common::testing::Structure kStructureValue(true, 973, 3.14);
+static const MessageWithArrays kTestMessageWithArrays({{971, 254, 1678}},
+ {{kStructureValue,
+ kStructureValue}});
+static const ::std::string kTestMessageWithArraysString =
+ ".aos.common.testing.MessageWithArrays{test_int:[971, 254, 1678], "
+ "test_struct:[.aos.common.testing.Structure{struct_bool:T, struct_int:973, "
+ "struct_float:3.140000}, .aos.common.testing.Structure{struct_bool:T, "
+ "struct_int:973, struct_float:3.140000}]}";
+
+TEST_F(PrintMessageTest, Basic) {
+ CHECK_GE(sizeof(input), kTestMessage1.Size());
+ input_bytes = kTestMessage1.Serialize(input);
+ output_bytes = sizeof(output);
+ ASSERT_TRUE(PrintMessage(output, &output_bytes, input, &input_bytes,
+ *kTestMessage1.GetType()));
+ EXPECT_EQ(kTestMessage1String, ::std::string(output));
+ EXPECT_EQ(kTestMessage1String.size() + 1, sizeof(output) - output_bytes);
+}
+
+TEST_F(PrintMessageTest, OutputTooSmall) {
+ CHECK_GE(sizeof(input), kTestMessage1.Size());
+ input_bytes = kTestMessage1.Serialize(input);
+ output_bytes = kTestMessage1String.size();
+ EXPECT_FALSE(PrintMessage(output, &output_bytes, input, &input_bytes,
+ *kTestMessage1.GetType()));
+}
+
+TEST_F(PrintMessageTest, InputTooSmall) {
+ input_bytes = kTestMessage1.Size() - 1;
+ output_bytes = sizeof(output);
+ EXPECT_FALSE(PrintMessage(output, &output_bytes, input, &input_bytes,
+ *kTestMessage1.GetType()));
+}
+
+TEST_F(PrintMessageTest, Structure) {
+ CHECK_GE(sizeof(input), kTestStructure1.Size());
+ input_bytes = kTestStructure1.Serialize(input);
+ output_bytes = sizeof(output);
+ ASSERT_TRUE(PrintMessage(output, &output_bytes, input, &input_bytes,
+ *kTestStructure1.GetType()));
+ EXPECT_EQ(kTestStructure1String, ::std::string(output));
+ EXPECT_EQ(kTestStructure1String.size() + 1, sizeof(output) - output_bytes);
+}
+
+TEST_F(PrintMessageTest, Matrix) {
+ static const uint16_t kTestMatrix[] = {971, 254, 1768, 8971, 9971, 973};
+ uint16_t test_matrix[sizeof(kTestMatrix) / sizeof(kTestMatrix[0])];
+ SerializeMatrix(queue_primitive_types::uint16_t_p, test_matrix, kTestMatrix,
+ 3, 2);
+ static const ::std::string kOutput =
+ "[[971, 8971], [254, 9971], [1768, 973]]";
+ output_bytes = sizeof(output);
+ ASSERT_TRUE(PrintMatrix(output, &output_bytes, test_matrix,
+ queue_primitive_types::uint16_t_p, 3, 2));
+ EXPECT_EQ(kOutput, ::std::string(output));
+ EXPECT_EQ(kOutput.size() + 1, sizeof(output) - output_bytes);
+}
+
+TEST_F(PrintMessageTest, Array) {
+ CHECK_GE(sizeof(input), kTestMessageWithArrays.Size());
+ input_bytes = kTestMessageWithArrays.Serialize(input);
+ output_bytes = sizeof(output);
+ ASSERT_TRUE(PrintMessage(output, &output_bytes, input, &input_bytes,
+ *kTestMessageWithArrays.GetType()));
+ EXPECT_EQ(kTestMessageWithArraysString, ::std::string(output));
+ EXPECT_EQ(kTestMessageWithArraysString.size() + 1,
+ sizeof(output) - output_bytes);
+}
+
+} // namespace testing
+} // namespace aos
diff --git a/aos/common/scoped_fd.h b/aos/common/scoped_fd.h
new file mode 100644
index 0000000..57ecdd8
--- /dev/null
+++ b/aos/common/scoped_fd.h
@@ -0,0 +1,40 @@
+#include <unistd.h>
+
+#include "aos/common/macros.h"
+#include "aos/common/logging/logging.h"
+
+namespace aos {
+
+// Smart "pointer" (container) for a file descriptor.
+class ScopedFD {
+ public:
+ explicit ScopedFD(int fd = -1) : fd_(fd) {}
+ ~ScopedFD() {
+ Close();
+ }
+ int get() const { return fd_; }
+ int release() {
+ const int r = fd_;
+ fd_ = -1;
+ return r;
+ }
+ void reset(int new_fd = -1) {
+ if (fd_ != new_fd) {
+ Close();
+ fd_ = new_fd;
+ }
+ }
+ operator bool() const { return fd_ != -1; }
+ private:
+ int fd_;
+ void Close() {
+ if (fd_ != -1) {
+ if (close(fd_) == -1) {
+ PLOG(WARNING, "close(%d) failed", fd_);
+ }
+ }
+ }
+ DISALLOW_COPY_AND_ASSIGN(ScopedFD);
+};
+
+} // namespace aos
diff --git a/aos/common/scoped_ptr.h b/aos/common/scoped_ptr.h
new file mode 100644
index 0000000..e4df78e
--- /dev/null
+++ b/aos/common/scoped_ptr.h
@@ -0,0 +1,43 @@
+#ifndef AOS_COMMON_SCOPED_PTR_H_
+#define AOS_COMMON_SCOPED_PTR_H_
+
+#include "aos/common/macros.h"
+
+namespace aos {
+
+// A simple scoped_ptr implementation that works under both linux and vxworks.
+template<typename T>
+class scoped_ptr {
+ public:
+ typedef T element_type;
+
+ explicit scoped_ptr(T *p = NULL) : p_(p) {}
+ ~scoped_ptr() {
+ delete p_;
+ }
+
+ T &operator*() const { return *p_; }
+ T *operator->() const { return p_; }
+ T *get() const { return p_; }
+
+ operator bool() const { return p_ != NULL; }
+
+ void swap(scoped_ptr<T> &other) {
+ T *temp = other.p_;
+ other.p_ = p_;
+ p_ = other.p_;
+ }
+ void reset(T *p = NULL) {
+ if (p_ != NULL) delete p_;
+ p_ = p;
+ }
+
+ private:
+ T *p_;
+
+ DISALLOW_COPY_AND_ASSIGN(scoped_ptr<T>);
+};
+
+} // namespace aos
+
+#endif // AOS_COMMON_SCOPED_PTR_H_
diff --git a/aos/common/stl_mutex.h b/aos/common/stl_mutex.h
new file mode 100644
index 0000000..9242312
--- /dev/null
+++ b/aos/common/stl_mutex.h
@@ -0,0 +1,144 @@
+#ifndef AOS_COMMON_STL_MUTEX_H_
+#define AOS_COMMON_STL_MUTEX_H_
+
+#include <mutex>
+
+#include "aos/linux_code/ipc_lib/aos_sync.h"
+#include "aos/common/logging/logging.h"
+#include "aos/common/type_traits.h"
+#include "aos/common/macros.h"
+
+namespace aos {
+
+// A mutex with the same API and semantics as ::std::mutex, with the addition of
+// methods for checking if the previous owner died and a constexpr default
+// constructor.
+// Definitely safe to put in SHM.
+// This uses the pthread_mutex semantics for owner-died: once somebody dies with
+// the lock held, anybody else who takes it will see true for owner_died() until
+// one of them calls consistent(). It is an error to call unlock() when
+// owner_died() returns true.
+class stl_mutex {
+ public:
+ constexpr stl_mutex() : native_handle_() {}
+
+ void lock() {
+ const int ret = mutex_grab(&native_handle_);
+ switch (ret) {
+ case 0:
+ break;
+ case 1:
+ owner_died_ = true;
+ break;
+ default:
+ LOG(FATAL, "mutex_grab(%p) failed with %d\n", &native_handle_, ret);
+ }
+ }
+
+ bool try_lock() {
+ const int ret = mutex_trylock(&native_handle_);
+ switch (ret) {
+ case 0:
+ return true;
+ case 1:
+ owner_died_ = true;
+ return true;
+ case 4:
+ return false;
+ default:
+ LOG(FATAL, "mutex_trylock(%p) failed with %d\n", &native_handle_, ret);
+ }
+ }
+
+ void unlock() {
+ CHECK(!owner_died_);
+ mutex_unlock(&native_handle_);
+ }
+
+ typedef aos_mutex *native_handle_type;
+ native_handle_type native_handle() { return &native_handle_; }
+
+ bool owner_died() const { return owner_died_; }
+ void consistent() { owner_died_ = false; }
+
+ private:
+ aos_mutex native_handle_;
+
+ bool owner_died_ = false;
+
+ DISALLOW_COPY_AND_ASSIGN(stl_mutex);
+};
+
+// A mutex with the same API and semantics as ::std::recursive_mutex, with the
+// addition of methods for checking if the previous owner died and a constexpr
+// default constructor.
+// Definitely safe to put in SHM.
+// This uses the pthread_mutex semantics for owner-died: once somebody dies with
+// the lock held, anybody else who takes it will see true for owner_died() until
+// one of them calls consistent(). It is an error to call unlock() or lock()
+// again when owner_died() returns true.
+class stl_recursive_mutex {
+ public:
+ constexpr stl_recursive_mutex() {}
+
+ void lock() {
+ if (mutex_islocked(mutex_.native_handle())) {
+ CHECK(!owner_died());
+ ++recursive_locks_;
+ } else {
+ mutex_.lock();
+ if (mutex_.owner_died()) {
+ recursive_locks_ = 0;
+ } else {
+ CHECK_EQ(0, recursive_locks_);
+ }
+ }
+ }
+ bool try_lock() {
+ if (mutex_islocked(mutex_.native_handle())) {
+ CHECK(!owner_died());
+ ++recursive_locks_;
+ return true;
+ } else {
+ if (mutex_.try_lock()) {
+ if (mutex_.owner_died()) {
+ recursive_locks_ = 0;
+ } else {
+ CHECK_EQ(0, recursive_locks_);
+ }
+ return true;
+ } else {
+ return false;
+ }
+ }
+ }
+ void unlock() {
+ if (recursive_locks_ == 0) {
+ mutex_.unlock();
+ } else {
+ --recursive_locks_;
+ }
+ }
+
+ typedef stl_mutex::native_handle_type native_handle_type;
+ native_handle_type native_handle() { return mutex_.native_handle(); }
+
+ bool owner_died() const { return mutex_.owner_died(); }
+ void consistent() { mutex_.consistent(); }
+
+ private:
+ stl_mutex mutex_;
+ int recursive_locks_ = 0;
+
+ DISALLOW_COPY_AND_ASSIGN(stl_recursive_mutex);
+};
+
+// Convenient typedefs for various types of locking objects.
+typedef ::std::lock_guard<stl_mutex> mutex_lock_guard;
+typedef ::std::lock_guard<stl_recursive_mutex> recursive_lock_guard;
+typedef ::std::unique_lock<stl_mutex> mutex_unique_lock;
+typedef ::std::unique_lock<stl_recursive_mutex> recursive_unique_lock;
+
+} // namespace aos
+
+#endif // AOS_COMMON_STL_MUTEX_H_
diff --git a/aos/common/stl_mutex_test.cc b/aos/common/stl_mutex_test.cc
new file mode 100644
index 0000000..7d84416
--- /dev/null
+++ b/aos/common/stl_mutex_test.cc
@@ -0,0 +1,74 @@
+#include "aos/common/stl_mutex.h"
+
+#include "gtest/gtest.h"
+
+#include "aos/common/queue_testutils.h"
+#include "aos/common/util/thread.h"
+#include "aos/common/die.h"
+
+namespace aos {
+namespace testing {
+
+class StlMutexDeathTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ ::aos::common::testing::EnableTestLogging();
+ SetDieTestMode(true);
+ }
+};
+
+typedef StlMutexDeathTest StlRecursiveMutexDeathTest;
+
+// Tests that locking/unlocking without any blocking works.
+TEST(StlMutexTest, Basic) {
+ stl_mutex mutex;
+
+ mutex.lock();
+ mutex.unlock();
+
+ ASSERT_TRUE(mutex.try_lock());
+ ASSERT_FALSE(mutex.try_lock());
+ mutex.unlock();
+
+ mutex.lock();
+ ASSERT_FALSE(mutex.try_lock());
+ mutex.unlock();
+}
+
+// Tests that unlocking an unlocked mutex fails.
+TEST_F(StlMutexDeathTest, MultipleUnlock) {
+ stl_mutex mutex;
+ mutex.lock();
+ mutex.unlock();
+ EXPECT_DEATH(mutex.unlock(), ".*multiple unlock.*");
+}
+
+// Tests that locking/unlocking (including recursively) without any blocking
+// works.
+TEST(StlRecursiveMutexTest, Basic) {
+ stl_recursive_mutex mutex;
+
+ mutex.lock();
+ mutex.unlock();
+
+ ASSERT_TRUE(mutex.try_lock());
+ ASSERT_TRUE(mutex.try_lock());
+ mutex.unlock();
+ mutex.unlock();
+
+ mutex.lock();
+ ASSERT_TRUE(mutex.try_lock());
+ mutex.unlock();
+ mutex.unlock();
+}
+
+// Tests that unlocking an unlocked recursive mutex fails.
+TEST_F(StlRecursiveMutexDeathTest, MultipleUnlock) {
+ stl_recursive_mutex mutex;
+ mutex.lock();
+ mutex.unlock();
+ EXPECT_DEATH(mutex.unlock(), ".*multiple unlock.*");
+}
+
+} // namespace testing
+} // namespace aos
diff --git a/aos/common/test_queue.q b/aos/common/test_queue.q
new file mode 100644
index 0000000..827b607
--- /dev/null
+++ b/aos/common/test_queue.q
@@ -0,0 +1,38 @@
+package aos.common.testing;
+
+struct Structure {
+ bool struct_bool;
+ uint16_t struct_int;
+ float struct_float;
+};
+
+message MessageWithStructure {
+ bool other_member;
+ Structure struct1;
+ Structure struct2;
+};
+
+message TestingMessage {
+ bool test_bool;
+ int32_t test_int;
+};
+
+message OtherTestingMessage {
+ bool test_bool;
+ int32_t test_int;
+ double test_double;
+};
+
+message MessageWithArrays {
+ uint16_t[3] test_int;
+ Structure[2] test_struct;
+};
+
+queue TestingMessage test_queue;
+
+queue_group TwoQueues {
+ queue TestingMessage first;
+ queue OtherTestingMessage second;
+};
+
+queue_group TwoQueues test_queuegroup;
diff --git a/aos/common/time.cc b/aos/common/time.cc
new file mode 100644
index 0000000..7318afb
--- /dev/null
+++ b/aos/common/time.cc
@@ -0,0 +1,233 @@
+#include "aos/common/time.h"
+
+#include <string.h>
+#include <inttypes.h>
+
+// We only use global_core from here, which is weak, so we don't really have a
+// dependency on it.
+#include "aos/linux_code/ipc_lib/shared_mem.h"
+
+#include "aos/common/logging/logging.h"
+#include "aos/common/mutex.h"
+
+namespace aos {
+namespace time {
+
+// State required to enable and use mock time.
+namespace {
+// True if mock time is enabled.
+// This does not need to be checked with the mutex held because setting time to
+// be enabled or disabled is atomic, and all future operations are atomic
+// anyways. If there is a race condition setting or clearing whether time is
+// enabled or not, it will still be a race condition if current_mock_time is
+// also set atomically with enabled.
+bool mock_time_enabled = false;
+// Mutex to make time reads and writes thread safe.
+Mutex time_mutex;
+// Current time when time is mocked.
+Time current_mock_time(0, 0);
+
+// TODO(aschuh): This doesn't include SleepFor and SleepUntil.
+// TODO(aschuh): Create a clock source object and change the default?
+// That would let me create a MockTime clock source.
+
+Time NowImpl(clockid_t clock) {
+ timespec temp;
+ if (clock_gettime(clock, &temp) != 0) {
+ PLOG(FATAL, "clock_gettime(%jd, %p) failed",
+ static_cast<uintmax_t>(clock), &temp);
+ }
+
+ const timespec offset = (global_core == nullptr)
+ ? timespec{0, 0}
+ : global_core->mem_struct->time_offset;
+ return Time(temp) + Time(offset);
+}
+
+} // namespace
+
+void Time::EnableMockTime(const Time &now) {
+ MutexLocker time_mutex_locker(&time_mutex);
+ mock_time_enabled = true;
+ current_mock_time = now;
+}
+
+void Time::UpdateMockTime() {
+ SetMockTime(NowImpl(kDefaultClock));
+}
+
+void Time::DisableMockTime() {
+ MutexLocker time_mutex_locker(&time_mutex);
+ mock_time_enabled = false;
+}
+
+void Time::SetMockTime(const Time &now) {
+ MutexLocker time_mutex_locker(&time_mutex);
+ if (__builtin_expect(!mock_time_enabled, 0)) {
+ LOG(FATAL, "Tried to set mock time and mock time is not enabled\n");
+ }
+ current_mock_time = now;
+}
+
+void Time::IncrementMockTime(const Time &amount) {
+ static ::aos::Mutex mutex;
+ ::aos::MutexLocker sync(&mutex);
+ SetMockTime(Now() + amount);
+}
+
+Time Time::Now(clockid_t clock) {
+ {
+ MutexLocker time_mutex_locker(&time_mutex);
+ if (mock_time_enabled) {
+ return current_mock_time;
+ }
+ }
+ return NowImpl(clock);
+}
+
+void Time::CheckImpl(int32_t nsec) {
+ static_assert(aos::shm_ok<Time>::value,
+ "it should be able to go through shared memory");
+ if (nsec >= kNSecInSec || nsec < 0) {
+ LOG(FATAL, "0 <= nsec(%" PRId32 ") < %" PRId32 " isn't true.\n",
+ nsec, kNSecInSec);
+ }
+}
+
+Time &Time::operator+=(const Time &rhs) {
+ sec_ += rhs.sec_;
+ nsec_ += rhs.nsec_;
+ if (nsec_ >= kNSecInSec) {
+ nsec_ -= kNSecInSec;
+ sec_ += 1;
+ }
+ return *this;
+}
+const Time Time::operator+(const Time &rhs) const {
+ return Time(*this) += rhs;
+}
+Time &Time::operator-=(const Time &rhs) {
+ sec_ -= rhs.sec_;
+ nsec_ -= rhs.nsec_;
+ if (nsec_ < 0) {
+ nsec_ += kNSecInSec;
+ sec_ -= 1;
+ }
+ return *this;
+}
+const Time Time::operator-(const Time &rhs) const {
+ return Time(*this) -= rhs;
+}
+Time &Time::operator*=(int32_t rhs) {
+ const int64_t temp = static_cast<int64_t>(nsec_) *
+ static_cast<int64_t>(rhs);
+ sec_ *= rhs; // better not overflow, or the result is just too big
+ nsec_ = temp % kNSecInSec;
+ sec_ += (temp - nsec_) / kNSecInSec;
+ if (nsec_ < 0) {
+ nsec_ += kNSecInSec;
+ sec_ -= 1;
+ }
+ return *this;
+}
+const Time Time::operator*(int32_t rhs) const {
+ return Time(*this) *= rhs;
+}
+Time &Time::operator/=(int32_t rhs) {
+ nsec_ = (sec_ % rhs) * (kNSecInSec / rhs) + nsec_ / rhs;
+ sec_ /= rhs;
+ if (nsec_ < 0) {
+ nsec_ += kNSecInSec;
+ sec_ -= 1;
+ }
+ return *this;
+}
+const Time Time::operator/(int32_t rhs) const {
+ return Time(*this) /= rhs;
+}
+double Time::operator/(const Time &rhs) const {
+ return ToSeconds() / rhs.ToSeconds();
+}
+Time &Time::operator%=(int32_t rhs) {
+ nsec_ = ToNSec() % rhs;
+ const int wraps = nsec_ / ((rhs / kNSecInSec + 1) * kNSecInSec);
+ sec_ = wraps + rhs / kNSecInSec;
+ nsec_ -= kNSecInSec * wraps;
+ if (nsec_ < 0) {
+ nsec_ += kNSecInSec;
+ sec_ -= 1;
+ }
+ return *this;
+}
+const Time Time::operator%(int32_t rhs) const {
+ return Time(*this) %= rhs;
+}
+
+const Time Time::operator-() const {
+ return Time(-sec_ - 1, kNSecInSec - nsec_);
+}
+
+bool Time::operator==(const Time &rhs) const {
+ return sec_ == rhs.sec_ && nsec_ == rhs.nsec_;
+}
+bool Time::operator!=(const Time &rhs) const {
+ return !(*this == rhs);
+}
+bool Time::operator<(const Time &rhs) const {
+ return sec_ < rhs.sec_ || (sec_ == rhs.sec_ && nsec_ < rhs.nsec_);
+}
+bool Time::operator>(const Time &rhs) const {
+ return sec_ > rhs.sec_ || (sec_ == rhs.sec_ && nsec_ > rhs.nsec_);
+}
+bool Time::operator<=(const Time &rhs) const {
+ return sec_ < rhs.sec_ || (sec_ == rhs.sec_ && nsec_ <= rhs.nsec_);
+}
+bool Time::operator>=(const Time &rhs) const {
+ return sec_ > rhs.sec_ || (sec_ == rhs.sec_ && nsec_ >= rhs.nsec_);
+}
+
+bool Time::IsWithin(const Time &other, int64_t amount) const {
+ const int64_t temp = ToNSec() - other.ToNSec();
+ return temp <= amount && temp >= -amount;
+}
+
+std::ostream &operator<<(std::ostream &os, const Time& time) {
+ return os << "Time{" << time.sec_ << "s, " << time.nsec_ << "ns}";
+}
+
+void SleepFor(const Time &time, clockid_t clock) {
+ timespec converted(time.ToTimespec()), remaining;
+ int failure = EINTR;
+ do {
+ // This checks whether the last time through the loop actually failed or got
+ // interrupted.
+ if (failure != EINTR) {
+ PELOG(FATAL, failure, "clock_nanosleep(%jd, 0, %p, %p) failed",
+ static_cast<intmax_t>(clock), &converted, &remaining);
+ }
+ failure = clock_nanosleep(clock, 0, &converted, &remaining);
+ memcpy(&converted, &remaining, sizeof(converted));
+ } while (failure != 0);
+}
+
+void SleepUntil(const Time &time, clockid_t clock) {
+ timespec converted(time.ToTimespec());
+ int failure;
+ while ((failure = clock_nanosleep(clock, TIMER_ABSTIME,
+ &converted, NULL)) != 0) {
+ if (failure != EINTR) {
+ PELOG(FATAL, failure, "clock_nanosleep(%jd, TIMER_ABSTIME, %p, NULL)"
+ " failed", static_cast<intmax_t>(clock), &converted);
+ }
+ }
+}
+
+void OffsetToNow(const Time &now) {
+ CHECK_NOTNULL(global_core);
+ global_core->mem_struct->time_offset.tv_nsec = 0;
+ global_core->mem_struct->time_offset.tv_sec = 0;
+ global_core->mem_struct->time_offset = (now - Time::Now()).ToTimespec();
+}
+
+} // namespace time
+} // namespace aos
diff --git a/aos/common/time.h b/aos/common/time.h
new file mode 100644
index 0000000..9afe9a0
--- /dev/null
+++ b/aos/common/time.h
@@ -0,0 +1,271 @@
+#ifndef AOS_COMMON_TIME_H_
+#define AOS_COMMON_TIME_H_
+
+#include <stdint.h>
+#include <time.h>
+#include <sys/time.h>
+#include <stdint.h>
+
+#include <type_traits>
+#include <ostream>
+
+#include "aos/common/type_traits.h"
+#include "aos/common/macros.h"
+
+namespace aos {
+namespace time {
+
+// A nice structure for representing times.
+// 0 <= nsec_ < kNSecInSec should always be true. All functions here will make
+// sure that that is true if it was on all inputs (including *this).
+//
+// Negative times are supported so that all of the normal arithmetic identities
+// work. nsec_ is still always positive.
+//
+// The arithmetic and comparison operators are overloaded because they make
+// complete sense and are very useful. The default copy and assignment stuff is
+// left because it works fine. Multiplication of Times by Times is
+// not implemented because I can't think of any uses for them and there are
+// multiple ways to do it. Division of Times by Times is implemented as the
+// ratio of them. Multiplication, division, and modulus of Times by integers are
+// implemented as interpreting the argument as nanoseconds. Modulus takes the
+// sign from the first operand.
+struct Time {
+#ifdef SWIG
+// All of the uses of constexpr here can safely be simply removed.
+// NOTE: This means that relying on the fact that constexpr implicitly makes
+// member functions const is not valid, so they all have to be explicitly marked
+// const.
+#define constexpr
+#endif // SWIG
+ public:
+ static const int32_t kNSecInSec = 1000000000;
+ static const int32_t kNSecInMSec = 1000000;
+ static const int32_t kNSecInUSec = 1000;
+ static const int32_t kMSecInSec = 1000;
+ static const int32_t kUSecInSec = 1000000;
+ constexpr Time(int32_t sec = 0, int32_t nsec = 0)
+ : sec_(sec), nsec_(CheckConstexpr(nsec)) {
+ }
+ #ifndef SWIG
+ explicit constexpr Time(const struct timespec &value)
+ : sec_(value.tv_sec), nsec_(CheckConstexpr(value.tv_nsec)) {
+ }
+ struct timespec ToTimespec() const {
+ struct timespec ans;
+ ans.tv_sec = sec_;
+ ans.tv_nsec = nsec_;
+ return ans;
+ }
+ explicit constexpr Time(const struct timeval &value)
+ : sec_(value.tv_sec), nsec_(CheckConstexpr(value.tv_usec * kNSecInUSec)) {
+ }
+ struct timeval ToTimeval() const {
+ struct timeval ans;
+ ans.tv_sec = sec_;
+ ans.tv_usec = nsec_ / kNSecInUSec;
+ return ans;
+ }
+ #endif // SWIG
+
+ // CLOCK_MONOTONIC on linux and CLOCK_REALTIME on the cRIO because the
+ // cRIO doesn't have any others.
+ // CLOCK_REALTIME is the default realtime clock and CLOCK_MONOTONIC doesn't
+ // change when somebody changes the wall clock (like the ntp deamon or
+ // whatever). See clock_gettime(2) for details.
+ //
+ // This is the clock that code that just wants to sleep for a certain amount
+ // of time or measure how long something takes should use.
+ #ifndef __VXWORKS__
+ static const clockid_t kDefaultClock = CLOCK_MONOTONIC;
+ #else
+ static const clockid_t kDefaultClock = CLOCK_REALTIME;
+ #endif
+ // Creates a Time representing the current value of the specified clock or
+ // dies.
+ static Time Now(clockid_t clock = kDefaultClock);
+
+ // Constructs a Time representing seconds.
+ static constexpr Time InSeconds(double seconds) {
+ return (seconds < 0.0) ?
+ Time(static_cast<int32_t>(seconds) - 1,
+ (seconds - static_cast<int32_t>(seconds) + 1.0) * kNSecInSec) :
+ Time(static_cast<int32_t>(seconds),
+ (seconds - static_cast<int32_t>(seconds)) * kNSecInSec);
+ }
+
+ // Constructs a time representing microseconds.
+ static constexpr Time InNS(int64_t nseconds) {
+ return (nseconds < 0) ?
+ Time(nseconds / static_cast<int64_t>(kNSecInSec) - 1,
+ (nseconds % kNSecInSec) + kNSecInSec) :
+ Time(nseconds / static_cast<int64_t>(kNSecInSec),
+ nseconds % kNSecInSec);
+ }
+
+ // Constructs a time representing microseconds.
+ static constexpr Time InUS(int useconds) {
+ return (useconds < 0) ?
+ Time(useconds / kUSecInSec - 1,
+ (useconds % kUSecInSec) * kNSecInUSec + kNSecInSec) :
+ Time(useconds / kUSecInSec, (useconds % kUSecInSec) * kNSecInUSec);
+ }
+
+ // Constructs a time representing mseconds.
+ static constexpr Time InMS(int mseconds) {
+ return (mseconds < 0) ?
+ Time(mseconds / kMSecInSec - 1,
+ (mseconds % kMSecInSec) * kNSecInMSec + kNSecInSec) :
+ Time(mseconds / kMSecInSec, (mseconds % kMSecInSec) * kNSecInMSec);
+ }
+
+ // Checks whether or not this time is within amount nanoseconds of other.
+ bool IsWithin(const Time &other, int64_t amount) const;
+
+ // Returns the time represented all in nanoseconds.
+ int64_t constexpr ToNSec() const {
+ return static_cast<int64_t>(sec_) * static_cast<int64_t>(kNSecInSec) +
+ static_cast<int64_t>(nsec_);
+ }
+#ifdef __VXWORKS__
+ // Returns the time represented all in system clock ticks. The system clock
+ // rate is retrieved using sysClkRateGet().
+ int ToTicks() const {
+ return ToNSec() / static_cast<int64_t>(kNSecInSec / sysClkRateGet());
+ }
+ // Constructs a Time representing ticks.
+ static Time InTicks(int ticks) {
+ return Time::InSeconds(static_cast<double>(ticks) / sysClkRateGet());
+ }
+#endif
+
+ // Returns the time represented in milliseconds.
+ int64_t constexpr ToMSec() const {
+ return static_cast<int64_t>(sec_) * static_cast<int64_t>(kMSecInSec) +
+ (static_cast<int64_t>(nsec_) / static_cast<int64_t>(kNSecInMSec));
+ }
+
+ // Returns the time represent in microseconds.
+ int64_t constexpr ToUSec() const {
+ return static_cast<int64_t>(sec_) * static_cast<int64_t>(kUSecInSec) +
+ (static_cast<int64_t>(nsec_) / static_cast<int64_t>(kNSecInUSec));
+ }
+
+ // Returns the time represented in fractional seconds.
+ double constexpr ToSeconds() const {
+ return static_cast<double>(sec_) + static_cast<double>(nsec_) / kNSecInSec;
+ }
+
+ #ifndef SWIG
+ Time &operator+=(const Time &rhs);
+ Time &operator-=(const Time &rhs);
+ Time &operator*=(int32_t rhs);
+ Time &operator/=(int32_t rhs);
+ Time &operator%=(int32_t rhs);
+ Time &operator%=(double rhs) = delete;
+ Time &operator*=(double rhs) = delete;
+ Time &operator/=(double rhs) = delete;
+ const Time operator*(double rhs) const = delete;
+ const Time operator/(double rhs) const = delete;
+ const Time operator%(double rhs) const = delete;
+ #endif
+ const Time operator+(const Time &rhs) const;
+ const Time operator-(const Time &rhs) const;
+ const Time operator*(int32_t rhs) const;
+ const Time operator/(int32_t rhs) const;
+ double operator/(const Time &rhs) const;
+ const Time operator%(int32_t rhs) const;
+
+ const Time operator-() const;
+
+ bool operator==(const Time &rhs) const;
+ bool operator!=(const Time &rhs) const;
+ bool operator<(const Time &rhs) const;
+ bool operator>(const Time &rhs) const;
+ bool operator<=(const Time &rhs) const;
+ bool operator>=(const Time &rhs) const;
+
+ #ifndef SWIG
+ // For gtest etc.
+ friend std::ostream &operator<<(std::ostream &os, const Time &time);
+ #endif // SWIG
+
+ int32_t constexpr sec() const { return sec_; }
+ void set_sec(int32_t sec) { sec_ = sec; }
+ int32_t constexpr nsec() const { return nsec_; }
+ void set_nsec(int32_t nsec) {
+ nsec_ = nsec;
+ Check();
+ }
+
+ // Absolute value.
+ Time abs() const {
+ if (*this > Time(0, 0)) return *this;
+ if (nsec_ == 0) return Time(-sec_, 0);
+ return Time(-sec_ - 1, kNSecInSec - nsec_);
+ }
+
+ // Enables returning the mock time value for Now instead of checking the
+ // system clock.
+ static void EnableMockTime(const Time &now = Now());
+ // Calls SetMockTime with the current actual time.
+ static void UpdateMockTime();
+ // Sets now when time is being mocked.
+ static void SetMockTime(const Time &now);
+ // Convenience function to just increment the mock time by a certain amount in
+ // a thread safe way.
+ static void IncrementMockTime(const Time &amount);
+ // Disables mocking time.
+ static void DisableMockTime();
+
+ private:
+ int32_t sec_, nsec_;
+
+ // LOG(FATAL)s if nsec is >= kNSecInSec or negative.
+ static void CheckImpl(int32_t nsec);
+ void Check() { CheckImpl(nsec_); }
+ // A constexpr version of CheckImpl that returns the given value when it
+ // succeeds or evaluates to non-constexpr and returns 0 when it fails.
+ // This will result in the usual LOG(FATAL) if this is used where it isn't
+ // required to be constexpr or a compile error if it is.
+ static constexpr int32_t CheckConstexpr(int32_t nsec) {
+ return (nsec >= kNSecInSec || nsec < 0) ? CheckImpl(nsec), 0 : nsec;
+ }
+
+#ifdef SWIG
+#undef constexpr
+#endif // SWIG
+};
+
+// Sleeps for the amount of time represented by time counted by clock.
+void SleepFor(const Time &time, clockid_t clock = Time::kDefaultClock);
+// Sleeps until clock is at the time represented by time.
+void SleepUntil(const Time &time, clockid_t clock = Time::kDefaultClock);
+
+// Sets the global offset for all times so ::aos::time::Time::Now() will return
+// now.
+// There is no synchronization here, so this is only safe when only a single
+// task is running.
+// This is only allowed when the shared memory core infrastructure has been
+// initialized in this process.
+void OffsetToNow(const Time &now);
+
+// RAII class that freezes Time::Now() (to avoid making large numbers of
+// syscalls to find the real time).
+class TimeFreezer {
+ public:
+ TimeFreezer() {
+ Time::EnableMockTime();
+ }
+ ~TimeFreezer() {
+ Time::DisableMockTime();
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TimeFreezer);
+};
+
+} // namespace time
+} // namespace aos
+
+#endif // AOS_COMMON_TIME_H_
diff --git a/aos/common/time_test.cc b/aos/common/time_test.cc
new file mode 100644
index 0000000..c6ddd0c
--- /dev/null
+++ b/aos/common/time_test.cc
@@ -0,0 +1,233 @@
+#include "aos/common/time.h"
+
+#include "gtest/gtest.h"
+
+#include "aos/common/macros.h"
+#include "aos/common/util/death_test_log_implementation.h"
+
+namespace aos {
+namespace time {
+namespace testing {
+
+TEST(TimeTest, timespecConversions) {
+ timespec start{1234, 5678}; // NOLINT
+ Time time(start);
+ EXPECT_EQ(start.tv_sec, static_cast<time_t>(time.sec()));
+ EXPECT_EQ(start.tv_nsec, time.nsec());
+ timespec end = time.ToTimespec();
+ EXPECT_EQ(start.tv_sec, end.tv_sec);
+ EXPECT_EQ(start.tv_nsec, end.tv_nsec);
+}
+
+TEST(TimeTest, timevalConversions) {
+ timeval start{1234, 5678}; // NOLINT
+ Time time(start);
+ EXPECT_EQ(start.tv_sec, static_cast<long>(time.sec()));
+ EXPECT_EQ(start.tv_usec, time.nsec() / Time::kNSecInUSec);
+ timeval end = time.ToTimeval();
+ EXPECT_EQ(start.tv_sec, end.tv_sec);
+ EXPECT_EQ(start.tv_usec, end.tv_usec);
+}
+
+TEST(TimeDeathTest, ConstructorChecking) {
+ logging::Init();
+ EXPECT_DEATH(
+ {
+ logging::AddImplementation(new util::DeathTestLogImplementation());
+ Time(0, -1);
+ },
+ ".*0 <= nsec\\(-1\\) < 10+ .*");
+ EXPECT_DEATH(
+ {
+ logging::AddImplementation(new util::DeathTestLogImplementation());
+ Time(0, Time::kNSecInSec);
+ },
+ ".*0 <= nsec\\(10+\\) < 10+ .*");
+}
+
+// It's kind of hard not to test Now and SleepFor at the same time.
+TEST(TimeTest, NowAndSleepFor) {
+ // without this, it tends to fail the first time (ends up sleeping for way
+ // longer than it should the second time, where it actually matters)
+ SleepFor(Time(0, Time::kNSecInSec / 10));
+ Time start = Time::Now();
+ static constexpr Time kSleepTime = Time(0, Time::kNSecInSec * 2 / 10);
+ SleepFor(kSleepTime);
+ Time difference = Time::Now() - start;
+ EXPECT_GE(difference, kSleepTime);
+ EXPECT_LT(difference, kSleepTime + Time(0, Time::kNSecInSec / 100));
+}
+
+TEST(TimeTest, AbsoluteSleep) {
+ Time start = Time::Now();
+ SleepFor(Time(0, Time::kNSecInSec / 10));
+ static constexpr Time kSleepTime = Time(0, Time::kNSecInSec * 2 / 10);
+ SleepUntil(start + kSleepTime);
+ Time difference = Time::Now() - start;
+ EXPECT_GE(difference, kSleepTime);
+ EXPECT_LT(difference, kSleepTime + Time(0, Time::kNSecInSec / 100));
+}
+
+TEST(TimeTest, Addition) {
+ Time t(54, 500);
+ EXPECT_EQ(MACRO_DARG(Time(54, 5500)), t + MACRO_DARG(Time(0, 5000)));
+ EXPECT_EQ(MACRO_DARG(Time(56, 500)), t + MACRO_DARG(Time(2, 0)));
+ EXPECT_EQ(MACRO_DARG(Time(57, 6500)), t + MACRO_DARG(Time(3, 6000)));
+ EXPECT_EQ(MACRO_DARG(Time(50, 300)),
+ t + MACRO_DARG(Time(-5, Time::kNSecInSec - 200)));
+ EXPECT_EQ(Time(-46, 500), t + Time(-100, 0));
+ EXPECT_EQ(Time(-47, Time::kNSecInSec - 500),
+ Time(-101, Time::kNSecInSec - 1000) + t);
+}
+TEST(TimeTest, Subtraction) {
+ Time t(54, 500);
+ EXPECT_EQ(MACRO_DARG(Time(54, 300)), t - MACRO_DARG(Time(0, 200)));
+ EXPECT_EQ(MACRO_DARG(Time(42, 500)), t - MACRO_DARG(Time(12, 0)));
+ EXPECT_EQ(MACRO_DARG(Time(50, 100)), t - MACRO_DARG(Time(4, 400)));
+ EXPECT_EQ(MACRO_DARG(Time(53, 600)),
+ t - MACRO_DARG(Time(0, Time::kNSecInSec - 100)));
+ EXPECT_EQ(MACRO_DARG(Time(55, 800)),
+ t - MACRO_DARG(Time(-2, Time::kNSecInSec - 300)));
+ EXPECT_EQ(Time(54, 5500), t - Time(-1, Time::kNSecInSec - 5000));
+ EXPECT_EQ(Time(-50, Time::kNSecInSec - 300),
+ Time(5, 200) - t);
+}
+
+TEST(TimeTest, Multiplication) {
+ Time t(54, Time::kNSecInSec / 3);
+ EXPECT_EQ(MACRO_DARG(Time(108, Time::kNSecInSec / 3 * 2)), t * 2);
+ EXPECT_EQ(MACRO_DARG(Time(271, Time::kNSecInSec / 3 * 2 - 1)), t * 5);
+ EXPECT_EQ(Time(-109, Time::kNSecInSec / 3 + 1), t * -2);
+ EXPECT_EQ(Time(-55, Time::kNSecInSec / 3 * 2 + 1), t * -1);
+ EXPECT_EQ(Time(-218, Time::kNSecInSec / 3 * 2 + 2), (t * -1) * 4);
+}
+TEST(TimeTest, DivisionByInt) {
+ EXPECT_EQ(Time(5, Time::kNSecInSec / 10 * 4 + 50), Time(54, 500) / 10);
+ EXPECT_EQ(Time(2, Time::kNSecInSec / 4 * 3),
+ Time(5, Time::kNSecInSec / 2) / 2);
+ EXPECT_EQ(Time(-3, Time::kNSecInSec / 4 * 3),
+ Time(-5, Time::kNSecInSec / 2) / 2);
+}
+TEST(TimeTest, DivisionByTime) {
+ EXPECT_DOUBLE_EQ(2, Time(10, 0) / Time(5, 0));
+ EXPECT_DOUBLE_EQ(9, Time(27, 0) / Time(3, 0));
+ EXPECT_DOUBLE_EQ(9.25, Time(37, 0) / Time(4, 0));
+ EXPECT_DOUBLE_EQ(5.25, Time(36, Time::kNSecInSec / 4 * 3) / Time(7, 0));
+ EXPECT_DOUBLE_EQ(-5.25, Time(-37, Time::kNSecInSec / 4) / Time(7, 0));
+ EXPECT_DOUBLE_EQ(-5.25, Time(36, Time::kNSecInSec / 4 * 3) / Time(-7, 0));
+}
+
+TEST(TimeTest, Negation) {
+ EXPECT_EQ(Time(-5, 1234), -Time(4, Time::kNSecInSec - 1234));
+ EXPECT_EQ(Time(5, Time::kNSecInSec * 2 / 3 + 1),
+ -Time(-6, Time::kNSecInSec / 3));
+}
+
+TEST(TimeTest, Comparisons) {
+ EXPECT_TRUE(Time(971, 254) > Time(971, 253));
+ EXPECT_TRUE(Time(971, 254) >= Time(971, 253));
+ EXPECT_TRUE(Time(971, 254) < Time(971, 255));
+ EXPECT_TRUE(Time(971, 254) <= Time(971, 255));
+ EXPECT_TRUE(Time(971, 254) >= Time(971, 253));
+ EXPECT_TRUE(Time(971, 254) <= Time(971, 254));
+ EXPECT_TRUE(Time(971, 254) >= Time(971, 254));
+ EXPECT_TRUE(Time(972, 254) > Time(971, 254));
+ EXPECT_TRUE(Time(971, 254) < Time(972, 254));
+
+ EXPECT_TRUE(Time(-971, 254) > Time(-971, 253));
+ EXPECT_TRUE(Time(-971, 254) >= Time(-971, 253));
+ EXPECT_TRUE(Time(-971, 254) < Time(-971, 255));
+ EXPECT_TRUE(Time(-971, 254) <= Time(-971, 255));
+ EXPECT_TRUE(Time(-971, 254) >= Time(-971, 253));
+ EXPECT_TRUE(Time(-971, 254) <= Time(-971, 254));
+ EXPECT_TRUE(Time(-971, 254) >= Time(-971, 254));
+ EXPECT_TRUE(Time(-972, 254) < Time(-971, 254));
+ EXPECT_TRUE(Time(-971, 254) > Time(-972, 254));
+}
+
+TEST(TimeTest, Within) {
+ EXPECT_TRUE(MACRO_DARG(Time(55, 5000).IsWithin(Time(55, 4900), 100)));
+ EXPECT_FALSE(MACRO_DARG(Time(55, 5000).IsWithin(Time(55, 4900), 99)));
+ EXPECT_TRUE(MACRO_DARG(Time(5, 0).IsWithin(Time(4, Time::kNSecInSec - 200),
+ 250)));
+ EXPECT_TRUE(Time(-5, Time::kNSecInSec - 200).IsWithin(Time(-4, 0), 250));
+ EXPECT_TRUE(Time(-5, 200).IsWithin(Time(-5, 0), 250));
+}
+
+TEST(TimeTest, Modulus) {
+ EXPECT_EQ(MACRO_DARG(Time(0, Time::kNSecInSec / 10 * 2)),
+ MACRO_DARG(Time(50, 0) % (Time::kNSecInSec / 10 * 3)));
+ EXPECT_EQ(Time(-1, Time::kNSecInSec / 10 * 8),
+ Time(-50, 0) % (Time::kNSecInSec / 10 * 3));
+ EXPECT_EQ(Time(-1, Time::kNSecInSec / 10 * 8),
+ Time(-50, 0) % (-Time::kNSecInSec / 10 * 3));
+ EXPECT_EQ(Time(0, Time::kNSecInSec / 10 * 2),
+ Time(50, 0) % (-Time::kNSecInSec / 10 * 3));
+ EXPECT_EQ(Time(1, Time::kNSecInSec / 10),
+ Time(60, Time::kNSecInSec / 10) % (Time::kNSecInSec / 10 * 12));
+}
+
+TEST(TimeTest, InSeconds) {
+ EXPECT_EQ(MACRO_DARG(Time(2, Time::kNSecInSec / 100 * 55 - 1)),
+ Time::InSeconds(2.55));
+ EXPECT_EQ(MACRO_DARG(Time(-3, Time::kNSecInSec / 100 * 45)),
+ Time::InSeconds(-2.55));
+}
+
+TEST(TimeTest, ToSeconds) {
+ EXPECT_DOUBLE_EQ(13.23, Time::InSeconds(13.23).ToSeconds());
+ EXPECT_NEAR(-13.23, Time::InSeconds(-13.23).ToSeconds(),
+ 1.0 / Time::kNSecInSec * 2);
+}
+
+TEST(TimeTest, InMS) {
+ Time t = Time::InMS(254971);
+ EXPECT_EQ(254, t.sec());
+ EXPECT_EQ(971000000, t.nsec());
+
+ Time t2 = Time::InMS(-254971);
+ EXPECT_EQ(-255, t2.sec());
+ EXPECT_EQ(Time::kNSecInSec - 971000000, t2.nsec());
+}
+
+TEST(TimeTest, ToMSec) {
+ EXPECT_EQ(254971, Time(254, 971000000).ToMSec());
+ EXPECT_EQ(-254971, Time(-255, Time::kNSecInSec - 971000000).ToMSec());
+}
+
+TEST(TimeTest, InNS) {
+ Time t = Time::InNS(static_cast<int64_t>(973254111971ll));
+ EXPECT_EQ(973, t.sec());
+ EXPECT_EQ(254111971, t.nsec());
+
+ Time t2 = Time::InNS(static_cast<int64_t>(-973254111971ll));
+ EXPECT_EQ(-974, t2.sec());
+ EXPECT_EQ(Time::kNSecInSec - 254111971, t2.nsec());
+}
+
+TEST(TimeTest, InUS) {
+ Time t = Time::InUS(254111971);
+ EXPECT_EQ(254, t.sec());
+ EXPECT_EQ(111971000, t.nsec());
+
+ Time t2 = Time::InUS(-254111971);
+ EXPECT_EQ(-255, t2.sec());
+ EXPECT_EQ(Time::kNSecInSec - 111971000, t2.nsec());
+}
+
+TEST(TimeTest, ToUSec) {
+ EXPECT_EQ(254000971, Time(254, 971000).ToUSec());
+ EXPECT_EQ(-254000971, Time(-255, Time::kNSecInSec - 971000).ToUSec());
+}
+
+TEST(TimeTest, Abs) {
+ EXPECT_EQ(MACRO_DARG(Time(971, 1114)), MACRO_DARG(Time(971, 1114).abs()));
+ EXPECT_EQ(MACRO_DARG(Time(253, Time::kNSecInSec * 0.3)),
+ MACRO_DARG(Time(-254, Time::kNSecInSec * 0.7).abs()));
+ EXPECT_EQ(MACRO_DARG(-Time(-971, 973).ToNSec()),
+ MACRO_DARG(Time(970, Time::kNSecInSec - 973).ToNSec()));
+}
+
+} // namespace testing
+} // namespace time
+} // namespace aos
diff --git a/aos/common/type_traits.h b/aos/common/type_traits.h
new file mode 100644
index 0000000..4b5fa8c
--- /dev/null
+++ b/aos/common/type_traits.h
@@ -0,0 +1,38 @@
+#ifndef AOS_COMMON_TYPE_TRAITS_
+#define AOS_COMMON_TYPE_TRAITS_
+
+#include <features.h>
+
+#include <type_traits>
+
+namespace aos {
+namespace {
+
+template<typename Tp>
+struct has_trivial_copy_assign : public std::integral_constant<bool,
+// This changed between 4.4.5 and 4.6.3. Unless somebody discovers otherwise,
+// 4.6 seems like a reasonable place to switch.
+#if ((__GNUC__ < 4) || (__GNUC_MINOR__ < 6)) && !defined(__clang__)
+ ::std::has_trivial_assign<Tp>::value> {};
+#else
+ ::std::has_trivial_copy_assign<Tp>::value> {};
+#endif
+
+} // namespace
+
+// A class template that determines whether or not it is safe to pass a type
+// through the shared memory system (aka whether or not you can memcpy it).
+// Useful in combination with static_assert.
+//
+// Doesn't need a trivial constructor because it's bytes only need to get
+// copied. If it has a non-trivial destructor, somebody has to make sure to call
+// it when appropriate.
+// See also (3.9) [basic.types] in the C++11 standard.
+template<typename Tp>
+struct shm_ok : public std::integral_constant<bool,
+ (std::has_trivial_copy_constructor<Tp>::value &&
+ aos::has_trivial_copy_assign<Tp>::value)> {};
+
+} // namespace aos
+
+#endif
diff --git a/aos/common/type_traits_test.cpp b/aos/common/type_traits_test.cpp
new file mode 100644
index 0000000..aa104ee
--- /dev/null
+++ b/aos/common/type_traits_test.cpp
@@ -0,0 +1,98 @@
+#include "aos/common/type_traits.h"
+
+#include "gtest/gtest.h"
+
+namespace aos {
+namespace testing {
+
+class BadVirtualFunction {
+ virtual void Test() {}
+};
+TEST(TypeTraitsTest, VirtualFunction) {
+ EXPECT_FALSE(shm_ok<BadVirtualFunction>::value);
+}
+class BadPureVirtualFunction {
+ virtual void Test() = 0;
+};
+TEST(TypeTraitsTest, PureVirtualFunction) {
+ EXPECT_FALSE(shm_ok<BadPureVirtualFunction>::value);
+}
+class BadInheritedVirtual : public BadVirtualFunction {};
+TEST(TypeTraitsTest, InheritedVirtualFunction) {
+ EXPECT_FALSE(shm_ok<BadInheritedVirtual>::value);
+}
+class BadVirtualDestructor {
+ virtual ~BadVirtualDestructor();
+};
+TEST(TypeTraitsTest, VirtualDestructor) {
+ EXPECT_FALSE(shm_ok<BadVirtualDestructor>::value);
+}
+class Boring {};
+class BadVirtualBase : public virtual Boring {};
+TEST(TypeTraitsTest, VirtualBase) {
+ EXPECT_FALSE(shm_ok<BadVirtualBase>::value);
+}
+
+class GoodSimple {
+ public:
+ int test1, test2;
+ double test3;
+};
+// Make sure that it lets simple classes through and that the compiler isn't
+// completely nuts.
+TEST(TypeTraitsTest, Basic) {
+ EXPECT_TRUE(shm_ok<GoodSimple>::value);
+ GoodSimple test{5, 4, 34.2};
+ EXPECT_EQ(5, test.test1);
+ EXPECT_EQ(4, test.test2);
+ EXPECT_EQ(34.2, test.test3);
+ memset(&test, 0, sizeof(test));
+ EXPECT_EQ(0, test.test1);
+ EXPECT_EQ(0, test.test2);
+ EXPECT_EQ(0, test.test3);
+}
+
+class GoodWithConstructor {
+ public:
+ int val_;
+ GoodWithConstructor(int val) : val_(val) {}
+};
+// Make sure that it lets classes with constructors through.
+TEST(TypeTraitsTest, GoodWithConstructor) {
+ EXPECT_TRUE(shm_ok<GoodWithConstructor>::value);
+ GoodWithConstructor test(971);
+ EXPECT_EQ(971, test.val_);
+}
+
+class GoodPublicPrivateFunction {
+ public:
+ int32_t a_;
+ void set_a(int32_t a) { a_ = a; }
+ int32_t b() { return b_; }
+ void set_b(int32_t b) { b_ = b; }
+ private:
+ int32_t b_;
+};
+// Make sure that member functions still work.
+TEST(TypeTraitsTest, Function) {
+ EXPECT_TRUE(shm_ok<GoodPublicPrivateFunction>::value);
+ EXPECT_EQ(static_cast<unsigned>(8), sizeof(GoodPublicPrivateFunction)) <<
+ "The compiler did something weird, but it might not be a problem.";
+ GoodPublicPrivateFunction test;
+ test.a_ = 5;
+ test.set_b(971);
+ EXPECT_EQ(5, test.a_);
+ EXPECT_EQ(971, test.b());
+ test.set_a(74);
+ EXPECT_EQ(74, test.a_);
+ memset(&test, 0, sizeof(test));
+ EXPECT_EQ(0, test.a_);
+ EXPECT_EQ(0, test.b());
+ test.set_a(123);
+ test.set_b(254);
+ EXPECT_EQ(123, test.a_);
+ EXPECT_EQ(254, test.b());
+}
+
+} // namespace testing
+} // namespace aos
diff --git a/aos/common/unique_malloc_ptr.h b/aos/common/unique_malloc_ptr.h
new file mode 100644
index 0000000..bd2c37c
--- /dev/null
+++ b/aos/common/unique_malloc_ptr.h
@@ -0,0 +1,44 @@
+#include <memory>
+
+namespace aos {
+
+namespace {
+
+// Written as a functor so that it doesn't have to get passed to
+// std::unique_ptr's constructor as an argument.
+template<typename T, void(*function)(T *)>
+class const_wrap {
+ public:
+ void operator()(const T *ptr) {
+ function(const_cast<T *>(ptr));
+ }
+};
+
+// Wrapper function to deal with the differences between C and C++ (C++ doesn't
+// automatically convert T* to void* like C).
+template<typename T>
+void free_type(T *ptr) { ::free(reinterpret_cast<void *>(ptr)); }
+
+} // namespace
+
+// A std::unique_ptr that should get freed with a C-style free function
+// (free(2) by default).
+template<typename T, void(*function)(T *) = free_type<T>>
+class unique_c_ptr : public std::unique_ptr<T, const_wrap<T, function>> {
+ public:
+ unique_c_ptr(T *value) : std::unique_ptr<T, const_wrap<T, function>>(value) {}
+
+ // perfect forwarding of these 2 to make unique_ptr work
+ template<typename... Args>
+ unique_c_ptr(Args&&... args)
+ : std::unique_ptr<T, const_wrap<T, function>>(std::forward<Args>(args)...) {
+ }
+ template<typename... Args>
+ unique_c_ptr<T, function> &operator=(Args&&... args) {
+ std::unique_ptr<T, const_wrap<T, function>>::operator=(
+ std::forward<Args>(args)...);
+ return *this;
+ }
+};
+
+} // namespace aos
diff --git a/aos/common/util/death_test_log_implementation.h b/aos/common/util/death_test_log_implementation.h
new file mode 100644
index 0000000..c1b12c0
--- /dev/null
+++ b/aos/common/util/death_test_log_implementation.h
@@ -0,0 +1,27 @@
+#ifndef AOS_COMMON_UTIL_DEATH_TEST_LOG_IMPLEMENTATION_H_
+#define AOS_COMMON_UTIL_DEATH_TEST_LOG_IMPLEMENTATION_H_
+
+#include <stdlib.h>
+
+#include "aos/common/logging/logging_impl.h"
+
+namespace aos {
+namespace util {
+
+// Prints all FATAL messages to stderr and then abort(3)s before the regular
+// stuff can print out anything else. Ignores all other messages.
+// This is useful in death tests that expect a LOG(FATAL) to cause the death.
+class DeathTestLogImplementation : public logging::HandleMessageLogImplementation {
+ public:
+ virtual void HandleMessage(const logging::LogMessage &message) override {
+ if (message.level == FATAL) {
+ logging::internal::PrintMessage(stderr, message);
+ abort();
+ }
+ }
+};
+
+} // namespace util
+} // namespace aos
+
+#endif // AOS_COMMON_UTIL_DEATH_TEST_LOG_IMPLEMENTATION_H_
diff --git a/aos/common/util/inet_addr.cc b/aos/common/util/inet_addr.cc
new file mode 100644
index 0000000..48bde1d
--- /dev/null
+++ b/aos/common/util/inet_addr.cc
@@ -0,0 +1,41 @@
+#include "aos/common/util/inet_addr.h"
+
+#include <stdlib.h>
+#ifndef __VXWORKS__
+#include <string.h>
+
+#include "aos/common/byteorder.h"
+#else
+
+template<typename T>
+T hton(T);
+
+template<uint32_t>
+uint32_t hton(uint32_t v) { return v; }
+
+#endif
+
+namespace aos {
+namespace util {
+
+const char *MakeIPAddress(const in_addr &base_address,
+ ::aos::NetworkAddress last_segment) {
+ in_addr address = base_address;
+ SetLastSegment(&address, last_segment);
+
+#ifdef __VXWORKS__
+ char *r = static_cast<char *>(malloc(INET_ADDR_LEN));
+ inet_ntoa_b(address, r);
+ return r;
+#else
+ return strdup(inet_ntoa(address));
+#endif
+}
+
+void SetLastSegment(in_addr *address, ::aos::NetworkAddress last_segment) {
+ address->s_addr &= ~(hton<uint32_t>(0xFF));
+ address->s_addr |= hton<uint32_t>(static_cast<uint8_t>(last_segment));
+}
+
+} // namespace util
+} // namespace aos
diff --git a/aos/common/util/inet_addr.h b/aos/common/util/inet_addr.h
new file mode 100644
index 0000000..35368f1
--- /dev/null
+++ b/aos/common/util/inet_addr.h
@@ -0,0 +1,29 @@
+#ifndef AOS_COMMON_UTIL_INET_ADDR_H_
+#define AOS_COMMON_UTIL_INET_ADDR_H_
+
+#ifdef __VXWORKS__
+#include <inetLib.h>
+#else
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#endif
+
+#include "aos/common/network_port.h"
+
+namespace aos {
+namespace util {
+
+// Makes an IP address string from base_address with the last byte set to
+// last_segment.
+// Returns a malloc(3)ed string.
+const char *MakeIPAddress(const in_addr &base_address,
+ ::aos::NetworkAddress last_segment);
+
+// Sets the last byte of *address to last_segment.
+void SetLastSegment(in_addr *address, ::aos::NetworkAddress last_segment);
+
+} // namespace util
+} // namespace aos
+
+#endif // AOS_COMMON_UTIL_INET_ADDR_H_
diff --git a/aos/common/util/log_interval.h b/aos/common/util/log_interval.h
new file mode 100644
index 0000000..7c8a329
--- /dev/null
+++ b/aos/common/util/log_interval.h
@@ -0,0 +1,93 @@
+#ifndef AOS_COMMON_UTIL_LOG_INTERVAL_H_
+#define AOS_COMMON_UTIL_LOG_INTERVAL_H_
+
+#include "aos/common/time.h"
+#include "aos/common/logging/logging.h"
+
+#include <string>
+
+namespace aos {
+namespace util {
+
+// A class to help with logging things that happen a lot only occasionally.
+//
+// Intended use {
+// static LogInterval interval(::aos::time::Time::InSeconds(0.2));
+//
+// if (WantToLog()) {
+// interval.WantToLog();
+// }
+// if (interval.ShouldLog()) {
+// LOG(DEBUG, "thingie happened! (%d times)\n", interval.Count());
+// }
+// }
+class LogInterval {
+ public:
+ constexpr LogInterval(const ::aos::time::Time &interval)
+ : count_(0), interval_(interval), last_done_(0, 0) {}
+
+ void WantToLog() {
+ if (count_ == 0) {
+ last_done_ = ::aos::time::Time::Now();
+ }
+ ++count_;
+ }
+ bool ShouldLog() {
+ const ::aos::time::Time now = ::aos::time::Time::Now();
+ const bool r = (now - last_done_) >= interval_ && count_ > 0;
+ if (r) {
+ last_done_ = now;
+ }
+ return r;
+ }
+ int Count() {
+ const int r = count_;
+ count_ = 0;
+ return r;
+ }
+
+ const ::aos::time::Time &interval() const { return interval_; }
+
+ private:
+ int count_;
+ const ::aos::time::Time interval_;
+ ::aos::time::Time last_done_;
+};
+
+// This one is even easier to use. It always logs with a message "%s %d
+// times\n". Call LOG_INTERVAL wherever it should log and make sure Print gets
+// called often (ie not after a conditional return)
+class SimpleLogInterval {
+ public:
+ SimpleLogInterval(const ::aos::time::Time &interval, log_level level,
+ const ::std::string &message)
+ : interval_(interval), level_(level), message_(message) {}
+
+#define LOG_INTERVAL(simple_log) \
+ simple_log.WantToLog(LOG_SOURCENAME ": " STRINGIFY(__LINE__))
+ void WantToLog(const char *context) {
+ context_ = context;
+ interval_.WantToLog();
+ }
+
+ void Print() {
+ if (interval_.ShouldLog()) {
+ CHECK_NOTNULL(context_);
+ log_do(level_, "%s: %.*s %d times over %f sec\n", context_,
+ static_cast<int>(message_.size()), message_.data(),
+ interval_.Count(), interval_.interval().ToSeconds());
+ context_ = NULL;
+ }
+ }
+
+ private:
+ LogInterval interval_;
+ const log_level level_;
+ const ::std::string message_;
+ const char *context_ = NULL;
+};
+
+} // namespace util
+} // namespace aos
+
+#endif // AOS_COMMON_UTIL_LOG_INTERVAL_H_
diff --git a/aos/common/util/options.h b/aos/common/util/options.h
new file mode 100644
index 0000000..48c4fd8
--- /dev/null
+++ b/aos/common/util/options.h
@@ -0,0 +1,92 @@
+#ifndef AOS_COMMON_UTIL_OPTIONS_H_
+#define AOS_COMMON_UTIL_OPTIONS_H_
+
+#include <sys/types.h>
+
+namespace aos {
+
+template <class Owner>
+class Options;
+
+// An "option" that can be combined with other options and passed as one
+// argument. This class is designed to emulate integral constants (except be
+// type-safe), so its instances can be combined with operator| (creating an
+// Options), and an Options can be tested for membership with operator&.
+// Owner is the only class that can construct Option instances and is used to
+// differentiate between options for different classes. It is safe to only have
+// a forward declaration for Owner in scope when instantiating either of these
+// template classes.
+template <class Owner>
+class Options {
+ public:
+ // Represents a single options. Instances of this should be created as
+ // constants by Owner.
+ class Option {
+ public:
+ constexpr Options operator|(Option option) const {
+ return Options(bit_ | option.bit_);
+ }
+
+ constexpr bool operator==(Option other) const { return bit_ == other.bit_; }
+
+ constexpr unsigned int printable() const { return bit_; }
+
+ private:
+ // Bit must have exactly 1 bit set and that must be a different one for each
+ // instance.
+ explicit constexpr Option(unsigned int bit) : bit_(bit) {}
+
+ unsigned int bit_;
+
+ friend class Options;
+ friend Owner;
+ };
+
+ constexpr Options(Option option) : bits_(option.bit_) {}
+
+ constexpr bool operator&(Option option) const {
+ return (bits_ & option.bit_) != 0;
+ }
+
+ constexpr Options operator|(Option option) const {
+ return Options(bits_ | option.bit_);
+ }
+ constexpr Options operator|(Options options) const {
+ return Options(bits_ | options.bits_);
+ }
+
+ constexpr bool operator==(Options other) const {
+ return bits_ == other.bits_;
+ }
+
+ constexpr unsigned int printable() const { return bits_; }
+
+ // Returns true if no Options other than the ones in options are set.
+ // Useful for validating that no illegal options are passed.
+ constexpr bool NoOthersSet(Options options) const {
+ return (bits_ & ~options.bits_) == 0;
+ }
+
+ // Returns true if exactly 1 of the Options in options is set.
+ // Useful for validating that one of a group of mutually exclusive options has
+ // been passed.
+ constexpr bool ExactlyOneSet(Options options) const {
+ return __builtin_popcount(bits_ & options.bits_) == 1;
+ }
+
+ // Returns true if all of the Options in options are set.
+ constexpr bool AllSet(Options options) const {
+ return (bits_ & options.bits_) == options.bits_;
+ }
+
+ private:
+ explicit constexpr Options(unsigned int bits) : bits_(bits) {}
+
+ unsigned int bits_;
+
+ friend class Option;
+};
+
+} // namespace options
+
+#endif // AOS_COMMON_UTIL_OPTIONS_H_
diff --git a/aos/common/util/options_test.cc b/aos/common/util/options_test.cc
new file mode 100644
index 0000000..197340c
--- /dev/null
+++ b/aos/common/util/options_test.cc
@@ -0,0 +1,56 @@
+#include "aos/common/util/options.h"
+
+#include "gtest/gtest.h"
+
+namespace aos {
+namespace testing {
+
+class OptionsTest : public ::testing::Test {
+ public:
+ static constexpr Options<OptionsTest>::Option kOne{1}, kTwo{2}, kThree{4},
+ kFour{8};
+};
+
+constexpr Options<OptionsTest>::Option OptionsTest::kOne, OptionsTest::kTwo,
+ OptionsTest::kThree, OptionsTest::kFour;
+
+TEST_F(OptionsTest, Basic) {
+ const Options<OptionsTest> one_three = kOne | kThree;
+ EXPECT_TRUE(one_three & kOne);
+ EXPECT_FALSE(one_three & kTwo);
+ EXPECT_TRUE(one_three & kThree);
+}
+
+TEST_F(OptionsTest, NoOthersSet) {
+ const Options<OptionsTest> one_three = kOne | kThree;
+ EXPECT_TRUE(one_three.NoOthersSet(one_three));
+ EXPECT_TRUE(one_three.NoOthersSet(kOne | kTwo | kThree));
+ EXPECT_TRUE(one_three.NoOthersSet(kOne | kThree | kFour));
+ EXPECT_TRUE(one_three.NoOthersSet(kOne | kTwo | kThree | kFour));
+ EXPECT_FALSE(one_three.NoOthersSet(kOne));
+ EXPECT_FALSE(one_three.NoOthersSet(kThree));
+ EXPECT_FALSE(one_three.NoOthersSet(kTwo | kFour));
+}
+
+TEST_F(OptionsTest, ExactlyOneSet) {
+ const Options<OptionsTest> one_three = kOne | kThree;
+ EXPECT_TRUE(one_three.ExactlyOneSet(kOne | kTwo));
+ EXPECT_FALSE(one_three.ExactlyOneSet(one_three));
+ EXPECT_TRUE(one_three.ExactlyOneSet(kTwo | kThree | kFour));
+ EXPECT_FALSE(one_three.ExactlyOneSet(kOne | kTwo | kThree | kFour));
+}
+
+TEST_F(OptionsTest, AllSet) {
+ const Options<OptionsTest> one_three = kOne | kThree;
+ EXPECT_TRUE(one_three.AllSet(one_three));
+ EXPECT_TRUE(one_three.AllSet(kOne));
+ EXPECT_FALSE(one_three.AllSet(kTwo));
+ EXPECT_TRUE(one_three.AllSet(kThree));
+ EXPECT_FALSE(one_three.AllSet(kFour));
+ EXPECT_FALSE(one_three.AllSet(kOne | kTwo | kFour));
+ EXPECT_FALSE(one_three.AllSet(kTwo | kThree | kFour));
+ EXPECT_FALSE(one_three.AllSet(kOne | kTwo | kThree | kFour));
+}
+
+} // namespace testing
+} // namespace aos
diff --git a/aos/common/util/phased_loop.cc b/aos/common/util/phased_loop.cc
new file mode 100644
index 0000000..4b9dcdb
--- /dev/null
+++ b/aos/common/util/phased_loop.cc
@@ -0,0 +1,20 @@
+#include "aos/common/util/phased_loop.h"
+
+#include <string.h>
+
+#include "aos/common/logging/logging.h"
+#include "aos/common/time.h"
+
+namespace aos {
+namespace time {
+
+void PhasedLoopXMS(int ms, int offset) {
+ // TODO(brians): Tests!
+ const Time frequency = Time::InMS(ms);
+ SleepUntil((Time::Now() / static_cast<int32_t>(frequency.ToNSec())) *
+ static_cast<int32_t>(frequency.ToNSec()) +
+ frequency + Time::InUS(offset));
+}
+
+} // namespace timing
+} // namespace aos
diff --git a/aos/common/util/phased_loop.h b/aos/common/util/phased_loop.h
new file mode 100644
index 0000000..f3c417a
--- /dev/null
+++ b/aos/common/util/phased_loop.h
@@ -0,0 +1,17 @@
+#ifndef AOS_COMMON_UTIL_PHASED_LOOP_H_
+#define AOS_COMMON_UTIL_PHASED_LOOP_H_
+
+#include <time.h>
+#include <string>
+
+namespace aos {
+namespace time {
+
+// Will not be accurate if ms isn't a factor of 1000.
+// offset is in us.
+void PhasedLoopXMS(int ms, int offset);
+
+} // namespace time
+} // namespace aos
+
+#endif // AOS_COMMON_UTIL_PHASED_LOOP_H_
diff --git a/aos/common/util/run_command.cc b/aos/common/util/run_command.cc
new file mode 100644
index 0000000..a4a19d8
--- /dev/null
+++ b/aos/common/util/run_command.cc
@@ -0,0 +1,73 @@
+#include "aos/common/util/run_command.h"
+
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+
+#include "aos/common/logging/logging.h"
+
+namespace aos {
+namespace util {
+namespace {
+
+// RAII class to block SIGCHLD and then restore it on destruction.
+class BlockSIGCHLD {
+ public:
+ BlockSIGCHLD() {
+ sigset_t to_block;
+ sigemptyset(&to_block);
+ sigaddset(&to_block, SIGCHLD);
+ if (sigprocmask(SIG_BLOCK, &to_block, &original_blocked_) == -1) {
+ PLOG(FATAL, "sigprocmask(SIG_BLOCK, %p, %p) failed",
+ &to_block, &original_blocked_);
+ }
+ }
+ ~BlockSIGCHLD() {
+ if (sigprocmask(SIG_SETMASK, &original_blocked_, nullptr) == -1) {
+ PLOG(FATAL, "sigprocmask(SIG_SETMASK, %p, nullptr) failed",
+ &original_blocked_);
+ }
+ }
+
+ private:
+ sigset_t original_blocked_;
+};
+
+} // namespace
+
+int RunCommand(const char *command) {
+ BlockSIGCHLD blocker;
+ const pid_t pid = fork();
+ switch (pid) {
+ case 0: // in child
+ {
+ int new_stdin = open("/dev/null", O_RDONLY);
+ if (new_stdin == -1) _exit(127);
+ int new_stdout = open("/dev/null", O_WRONLY);
+ if (new_stdout == -1) _exit(127);
+ int new_stderr = open("/dev/null", O_WRONLY);
+ if (new_stderr == -1) _exit(127);
+ if (dup2(new_stdin, 0) != 0) _exit(127);
+ if (dup2(new_stdout, 1) != 1) _exit(127);
+ if (dup2(new_stderr, 2) != 2) _exit(127);
+ execl("/bin/sh", "sh", "-c", command, nullptr);
+ _exit(127);
+ }
+ case -1:
+ return -1;
+ default:
+ int stat;
+ while (waitpid(pid, &stat, 0) == -1) {
+ if (errno != EINTR) {
+ return -1;
+ }
+ }
+ return stat;
+ }
+}
+
+} // namespace util
+} // namespace aos
diff --git a/aos/common/util/run_command.h b/aos/common/util/run_command.h
new file mode 100644
index 0000000..d116481
--- /dev/null
+++ b/aos/common/util/run_command.h
@@ -0,0 +1,17 @@
+#ifndef AOS_COMMON_UTIL_RUN_COMMAND_H_
+#define AOS_COMMON_UTIL_RUN_COMMAND_H_
+
+namespace aos {
+namespace util {
+
+// Improved replacement for system(3). Doesn't block signals like system(3) and
+// is thread-safe. Also makes sure all 3 standard streams are /dev/null.
+//
+// This means that it passes command to `/bin/sh -c` and returns -1 or a status
+// like from wait(2).
+int RunCommand(const char *command);
+
+} // namespace util
+} // namespace aos
+
+#endif // AOS_COMMON_UTIL_RUN_COMMAND_H_
diff --git a/aos/common/util/run_command_test.cc b/aos/common/util/run_command_test.cc
new file mode 100644
index 0000000..daff3c5
--- /dev/null
+++ b/aos/common/util/run_command_test.cc
@@ -0,0 +1,61 @@
+#include "aos/common/util/run_command.h"
+
+#include "gtest/gtest.h"
+
+#include "aos/common/util/thread.h"
+
+namespace aos {
+namespace util {
+namespace testing {
+
+TEST(RunCommandTest, True) {
+ int result = RunCommand("true");
+ ASSERT_NE(-1, result);
+ ASSERT_TRUE(WIFEXITED(result));
+ EXPECT_EQ(0, WEXITSTATUS(result));
+}
+
+TEST(RunCommandTest, False) {
+ int result = RunCommand("false");
+ ASSERT_NE(-1, result);
+ ASSERT_TRUE(WIFEXITED(result));
+ EXPECT_EQ(1, WEXITSTATUS(result));
+}
+
+TEST(RunCommandTest, CommandNotFound) {
+ int result = RunCommand("ajflkjasdlfa");
+ ASSERT_NE(-1, result);
+ ASSERT_TRUE(WIFEXITED(result));
+ EXPECT_EQ(127, WEXITSTATUS(result));
+}
+
+TEST(RunCommandTest, KilledBySignal) {
+ int result = RunCommand("kill -QUIT $$");
+ ASSERT_NE(-1, result);
+ ASSERT_TRUE(WIFSIGNALED(result));
+ EXPECT_EQ(SIGQUIT, WTERMSIG(result));
+}
+
+TEST(RunCommandTest, MultipleThreads) {
+ int result1, result2;
+ util::FunctionThread t1([&result1](util::Thread *) {
+ result1 = RunCommand("true");
+ });
+ util::FunctionThread t2([&result2](util::Thread *) {
+ result2 = RunCommand("true");
+ });
+ t1.Start();
+ t2.Start();
+ t1.WaitUntilDone();
+ t2.WaitUntilDone();
+ ASSERT_NE(-1, result1);
+ ASSERT_NE(-1, result2);
+ ASSERT_TRUE(WIFEXITED(result1));
+ ASSERT_TRUE(WIFEXITED(result2));
+ EXPECT_EQ(0, WEXITSTATUS(result1));
+ EXPECT_EQ(0, WEXITSTATUS(result2));
+}
+
+} // namespace testing
+} // namespace util
+} // namespace aos
diff --git a/aos/common/util/string_to_num.h b/aos/common/util/string_to_num.h
new file mode 100644
index 0000000..d99ed91
--- /dev/null
+++ b/aos/common/util/string_to_num.h
@@ -0,0 +1,29 @@
+#ifndef AOS_COMMON_UTIL_STRING_TO_NUM_H_
+#define AOS_COMMON_UTIL_STRING_TO_NUM_H_
+
+#include <sstream>
+#include <string>
+
+namespace aos {
+namespace util {
+
+// Converts a string into a specified numeric type. If it can't be converted
+// completely or at all, or if the converted number would overflow the
+// specified type, it returns false.
+template<typename T>
+inline bool StringToNumber(const ::std::string &input, T *out_num) {
+ ::std::istringstream stream(input);
+ stream >> *out_num;
+
+ if (stream.fail() || !stream.eof()) {
+ return false;
+ }
+
+ return true;
+}
+
+
+} // util
+} // aos
+
+#endif
diff --git a/aos/common/util/string_to_num_test.cc b/aos/common/util/string_to_num_test.cc
new file mode 100644
index 0000000..705de42
--- /dev/null
+++ b/aos/common/util/string_to_num_test.cc
@@ -0,0 +1,49 @@
+#include <stdint.h>
+
+#include <string>
+
+#include "gtest/gtest.h"
+
+#include "aos/common/util/string_to_num.h"
+
+namespace aos {
+namespace util {
+namespace testing {
+
+TEST(StringToNumTest, CorrectNumber) {
+ int result;
+ ASSERT_TRUE(StringToNumber<int>(::std::string("42"), &result));
+ EXPECT_EQ(result, 42);
+}
+
+TEST(StringToNumTest, NegativeTest) {
+ int result;
+ ASSERT_TRUE(StringToNumber<int>(::std::string("-42"), &result));
+ EXPECT_EQ(result, -42);
+}
+
+TEST(StringToNumTest, NonNumber) {
+ int result;
+ ASSERT_FALSE(StringToNumber<int>(::std::string("Daniel"), &result));
+}
+
+TEST(StringToNumTest, NumberWithText) {
+ int result;
+ ASSERT_FALSE(StringToNumber<int>(::std::string("42Daniel"), &result));
+}
+
+TEST(StringToNumTest, OverflowTest) {
+ uint32_t result;
+ // 2 << 32 should overflow.
+ ASSERT_FALSE(StringToNumber<uint32_t>(::std::string("4294967296"), &result));
+}
+
+TEST(StringToNumTest, FloatingPointTest) {
+ double result;
+ ASSERT_TRUE(StringToNumber<double>(::std::string("3.1415927")));
+ EXPECT_EQ(result, 3.1415927);
+}
+
+} // testing
+} // util
+} // aos
diff --git a/aos/common/util/thread.cc b/aos/common/util/thread.cc
new file mode 100644
index 0000000..54a8ef2
--- /dev/null
+++ b/aos/common/util/thread.cc
@@ -0,0 +1,76 @@
+#include "aos/common/util/thread.h"
+
+#include <pthread.h>
+#include <signal.h>
+
+#include "aos/common/logging/logging.h"
+
+namespace aos {
+namespace util {
+
+Thread::Thread() : started_(false), joined_(false), should_terminate_(false) {}
+
+Thread::~Thread() {
+ CHECK(!(started_ && !joined_));
+}
+
+void Thread::Start() {
+ CHECK(!started_);
+ started_ = true;
+ CHECK(pthread_create(&thread_, NULL, &Thread::StaticRun, this) == 0);
+}
+
+void Thread::Join() {
+ CHECK(!joined_ && started_);
+ joined_ = true;
+ should_terminate_.store(true);
+ CHECK(pthread_join(thread_, NULL) == 0);
+}
+
+bool Thread::TryJoin() {
+ CHECK(!joined_ && started_);
+#ifdef AOS_SANITIZER_thread
+ // ThreadSanitizer misses the tryjoin and then complains about leaking the
+ // thread. Instead, we'll just check if the thread is still around and then
+ // do a regular Join() iff it isn't.
+ // TODO(brians): Remove this once tsan learns about pthread_tryjoin_np.
+ const int kill_ret = pthread_kill(thread_, 0);
+ // If it's still around.
+ if (kill_ret == 0) return false;
+ // If it died, we'll get ESRCH. Otherwise, something went wrong.
+ if (kill_ret != ESRCH) {
+ PELOG(FATAL, kill_ret, "pthread_kill(thread_, 0) failed");
+ }
+ Join();
+ return true;
+#else
+ const int ret = pthread_tryjoin_np(thread_, nullptr);
+ if (ret == 0) {
+ joined_ = true;
+ return true;
+ } else if (ret == EBUSY) {
+ return false;
+ } else {
+ PELOG(FATAL, ret, "pthread_tryjoin_np(thread_, nullptr) failed");
+ }
+#endif
+}
+
+void Thread::RequestStop() {
+ CHECK(!joined_ && started_);
+ should_terminate_.store(true);
+}
+
+void Thread::WaitUntilDone() {
+ CHECK(!joined_ && started_);
+ joined_ = true;
+ CHECK(pthread_join(thread_, NULL) == 0);
+}
+
+void *Thread::StaticRun(void *self) {
+ static_cast<Thread *>(self)->Run();
+ return NULL;
+}
+
+} // namespace util
+} // namespace aos
diff --git a/aos/common/util/thread.h b/aos/common/util/thread.h
new file mode 100644
index 0000000..993e343
--- /dev/null
+++ b/aos/common/util/thread.h
@@ -0,0 +1,90 @@
+#ifndef AOS_COMMON_UTIL_THREAD_H_
+#define AOS_COMMON_UTIL_THREAD_H_
+
+#include <functional>
+#include <atomic>
+
+#include <pthread.h>
+
+#include "aos/common/macros.h"
+
+namespace aos {
+namespace util {
+
+// A nice wrapper around a pthreads thread.
+//
+// TODO(aschuh): Test this.
+class Thread {
+ public:
+ Thread();
+ virtual ~Thread();
+
+ // Actually creates the thread.
+ void Start();
+
+ // Asks the code to stop and then waits until it has done so.
+ // This or TryJoin() (returning true) must be called exactly once for every
+ // instance.
+ void Join();
+
+ // If the code has already finished, returns true. Does not block waiting if
+ // it isn't.
+ // Join() must not be called on this instance if this returns true.
+ // This must return true or Join() must be called exactly once for every
+ // instance.
+ bool TryJoin();
+
+ // Asks the code to stop (in preparation for a Join()).
+ void RequestStop();
+
+ // Waits until the code has stopped. Does not ask it to do so.
+ void WaitUntilDone();
+
+ protected:
+ // Subclasses need to call this periodically if they are going to loop to
+ // check whether they have been asked to stop.
+ bool should_continue() {
+ return !should_terminate_.load();
+ }
+
+ private:
+ // Where subclasses actually do something.
+ //
+ // They should not block for long periods of time without checking
+ // should_continue().
+ virtual void Run() = 0;
+
+ static void *StaticRun(void *self);
+
+ pthread_t thread_;
+ bool started_;
+ bool joined_;
+ ::std::atomic_bool should_terminate_;
+
+ DISALLOW_COPY_AND_ASSIGN(Thread);
+};
+
+class FunctionThread : public Thread {
+ public:
+ FunctionThread(::std::function<void(FunctionThread *)> function)
+ : function_(function) {}
+
+ // Runs function in a new thread and waits for it to return.
+ static void RunInOtherThread(::std::function<void()> function) {
+ FunctionThread t([&function](FunctionThread *) { function(); });
+ t.Start();
+ t.Join();
+ }
+
+ private:
+ virtual void Run() override {
+ function_(this);
+ }
+
+ const ::std::function<void(FunctionThread *)> function_;
+};
+
+} // namespace util
+} // namespace aos
+
+#endif // AOS_COMMON_UTIL_THREAD_H_
diff --git a/aos/common/util/trapezoid_profile.cc b/aos/common/util/trapezoid_profile.cc
new file mode 100644
index 0000000..cd8d02c
--- /dev/null
+++ b/aos/common/util/trapezoid_profile.cc
@@ -0,0 +1,130 @@
+#include "aos/common/util/trapezoid_profile.h"
+
+#include "aos/common/logging/logging.h"
+
+using ::Eigen::Matrix;
+
+namespace aos {
+namespace util {
+
+TrapezoidProfile::TrapezoidProfile(const time::Time &delta_time)
+ : maximum_acceleration_(0),
+ maximum_velocity_(0),
+ timestep_(delta_time) {
+ output_.setZero();
+}
+
+void TrapezoidProfile::UpdateVals(double acceleration,
+ double delta_time) {
+ output_(0) += output_(1) * delta_time +
+ 0.5 * acceleration * delta_time * delta_time;
+ output_(1) += acceleration * delta_time;
+}
+
+const Matrix<double, 2, 1> &TrapezoidProfile::Update(
+ double goal_position,
+ double goal_velocity) {
+ CalculateTimes(goal_position - output_(0), goal_velocity);
+
+ double next_timestep = timestep_.ToSeconds();
+
+ if (acceleration_time_ > next_timestep) {
+ UpdateVals(acceleration_, next_timestep);
+ } else {
+ UpdateVals(acceleration_, acceleration_time_);
+
+ next_timestep -= acceleration_time_;
+ if (constant_time_ > next_timestep) {
+ UpdateVals(0, next_timestep);
+ } else {
+ UpdateVals(0, constant_time_);
+ next_timestep -= constant_time_;
+ if (deceleration_time_ > next_timestep) {
+ UpdateVals(deceleration_, next_timestep);
+ } else {
+ UpdateVals(deceleration_, deceleration_time_);
+ next_timestep -= deceleration_time_;
+ UpdateVals(0, next_timestep);
+ }
+ }
+ }
+
+ return output_;
+}
+
+void TrapezoidProfile::CalculateTimes(double distance_to_target,
+ double goal_velocity) {
+ if (distance_to_target == 0) {
+ // We're there. Stop everything.
+ // TODO(aschuh): Deal with velocity not right.
+ acceleration_time_ = 0;
+ acceleration_ = 0;
+ constant_time_ = 0;
+ deceleration_time_ = 0;
+ deceleration_ = 0;
+ return;
+ } else if (distance_to_target < 0) {
+ // Recurse with everything inverted.
+ output_(1) *= -1;
+ CalculateTimes(-distance_to_target,
+ -goal_velocity);
+ output_(1) *= -1;
+ acceleration_ *= -1;
+ deceleration_ *= -1;
+ return;
+ }
+
+ constant_time_ = 0;
+ acceleration_ = maximum_acceleration_;
+ double maximum_acceleration_velocity = distance_to_target * 2 *
+ std::abs(acceleration_) + output_(1) * output_(1);
+ if (maximum_acceleration_velocity > 0) {
+ maximum_acceleration_velocity = sqrt(maximum_acceleration_velocity);
+ } else {
+ maximum_acceleration_velocity = -sqrt(-maximum_acceleration_velocity);
+ }
+
+ // Since we know what we'd have to do if we kept after it to decelerate, we
+ // know the sign of the acceleration.
+ if (maximum_acceleration_velocity > goal_velocity) {
+ deceleration_ = -maximum_acceleration_;
+ } else {
+ deceleration_ = maximum_acceleration_;
+ }
+
+ // We now know the top velocity we can get to.
+ double top_velocity = sqrt((distance_to_target +
+ (output_(1) * output_(1)) /
+ (2.0 * acceleration_) +
+ (goal_velocity * goal_velocity) /
+ (2.0 * deceleration_)) /
+ (-1.0 / (2.0 * deceleration_) +
+ 1.0 / (2.0 * acceleration_)));
+
+ // If it can go too fast, we now know how long we get to accelerate for and
+ // how long to go at constant velocity.
+ if (top_velocity > maximum_velocity_) {
+ acceleration_time_ = (maximum_velocity_ - output_(1)) /
+ maximum_acceleration_;
+ constant_time_ = (distance_to_target +
+ (goal_velocity * goal_velocity -
+ maximum_velocity_ * maximum_velocity_) /
+ (2.0 * maximum_acceleration_)) / maximum_velocity_;
+ } else {
+ acceleration_time_ = (top_velocity - output_(1)) /
+ acceleration_;
+ }
+
+ CHECK_GT(top_velocity, -maximum_velocity_);
+
+ if (output_(1) > maximum_velocity_) {
+ constant_time_ = 0;
+ acceleration_time_ = 0;
+ }
+
+ deceleration_time_ = (goal_velocity - top_velocity) /
+ deceleration_;
+}
+
+} // namespace util
+} // namespace aos
diff --git a/aos/common/util/trapezoid_profile.h b/aos/common/util/trapezoid_profile.h
new file mode 100644
index 0000000..fe63352
--- /dev/null
+++ b/aos/common/util/trapezoid_profile.h
@@ -0,0 +1,69 @@
+#ifndef AOS_COMMON_UTIL_TRAPEZOID_PROFILE_H_
+#define AOS_COMMON_UTIL_TRAPEZOID_PROFILE_H_
+
+#include "Eigen/Dense"
+
+#include "aos/common/macros.h"
+#include "aos/common/time.h"
+
+namespace aos {
+namespace util {
+
+// Calculates a trapezoidal motion profile (like for a control loop's goals).
+// Supports having the end speed and position changed in the middle.
+//
+// The only units assumption that this class makes is that the unit of time is
+// seconds.
+class TrapezoidProfile {
+ public:
+ // delta_time is how long between each call to Update.
+ TrapezoidProfile(const time::Time &delta_time);
+
+ // Updates the state.
+ const Eigen::Matrix<double, 2, 1> &Update(double goal_position,
+ double goal_velocity);
+ // Useful for preventing windup etc.
+ void MoveCurrentState(const Eigen::Matrix<double, 2, 1> ¤t) {
+ output_ = current;
+ }
+
+ // Useful for preventing windup etc.
+ void MoveGoal(double dx) { output_(0, 0) += dx; }
+
+ void SetGoal(double x) { output_(0, 0) = x; }
+
+ void set_maximum_acceleration(double maximum_acceleration) {
+ maximum_acceleration_ = maximum_acceleration;
+ }
+ void set_maximum_velocity(double maximum_velocity) {
+ maximum_velocity_ = maximum_velocity;
+ }
+
+ private:
+ // Basic kinematics to update output_, given that we are going to accelerate
+ // by acceleration over delta_time.
+ void UpdateVals(double acceleration, double delta_time);
+ // Calculates how long to go for each segment.
+ void CalculateTimes(double distance_to_target, double goal_velocity);
+ // output_ is where it should go at time_.
+ Eigen::Matrix<double, 2, 1> output_;
+
+ double acceleration_time_;
+ double acceleration_;
+ double constant_time_;
+ double deceleration_time_;
+ double deceleration_;
+
+ double maximum_acceleration_;
+ double maximum_velocity_;
+
+ // How long between calls to Update.
+ const time::Time timestep_;
+
+ DISALLOW_COPY_AND_ASSIGN(TrapezoidProfile);
+};
+
+} // namespace util
+} // namespace aos
+
+#endif // AOS_COMMON_UTIL_TRAPEZOID_PROFILE_H_
diff --git a/aos/common/util/trapezoid_profile_test.cc b/aos/common/util/trapezoid_profile_test.cc
new file mode 100644
index 0000000..1dfeff3
--- /dev/null
+++ b/aos/common/util/trapezoid_profile_test.cc
@@ -0,0 +1,116 @@
+#include "gtest/gtest.h"
+
+#include "Eigen/Dense"
+
+#include "aos/common/util/trapezoid_profile.h"
+
+namespace aos {
+namespace util {
+namespace testing {
+
+class TrapezoidProfileTest : public ::testing::Test {
+ public:
+ EIGEN_MAKE_ALIGNED_OPERATOR_NEW;
+
+ protected:
+ TrapezoidProfileTest() : profile_(delta_time) {
+ position_.setZero();
+ profile_.set_maximum_acceleration(0.75);
+ profile_.set_maximum_velocity(1.75);
+ }
+
+ // Runs an iteration.
+ void RunIteration(double goal_position,
+ double goal_velocity) {
+ position_ = profile_.Update(goal_position,
+ goal_velocity);
+ }
+
+ const Eigen::Matrix<double, 2, 1> &position() { return position_; }
+
+ TrapezoidProfile profile_;
+
+ ::testing::AssertionResult At(double position, double velocity) {
+ static const double kDoubleNear = 0.00001;
+ if (::std::abs(velocity - position_(1)) > kDoubleNear) {
+ return ::testing::AssertionFailure() << "velocity is " << position_(1) <<
+ " not " << velocity;
+ }
+ if (::std::abs(position - position_(0)) > kDoubleNear) {
+ return ::testing::AssertionFailure() << "position is " << position_(0) <<
+ " not " << position;
+ }
+ return ::testing::AssertionSuccess() << "at " << position <<
+ " moving at " << velocity;
+ }
+
+ private:
+ static const time::Time delta_time;
+
+ Eigen::Matrix<double, 2, 1> position_;
+};
+const time::Time TrapezoidProfileTest::delta_time = time::Time::InSeconds(0.01);
+
+TEST_F(TrapezoidProfileTest, ReachesGoal) {
+ for (int i = 0; i < 450; ++i) {
+ RunIteration(3, 0);
+ }
+ EXPECT_TRUE(At(3, 0));
+}
+
+// Tests that decresing the maximum velocity in the middle when it is already
+// moving faster than the new max is handled correctly.
+TEST_F(TrapezoidProfileTest, ContinousUnderVelChange) {
+ profile_.set_maximum_velocity(1.75);
+ RunIteration(12.0, 0);
+ double last_pos = position()(0);
+ double last_vel = 1.75;
+ for (int i = 0; i < 1600; ++i) {
+ if (i == 400) {
+ profile_.set_maximum_velocity(0.75);
+ }
+ RunIteration(12.0, 0);
+ if (i >= 400) {
+ EXPECT_TRUE(::std::abs(last_pos - position()(0)) <= 1.75 * 0.01);
+ EXPECT_NEAR(last_vel, ::std::abs(last_pos - position()(0)), 0.0001);
+ }
+ last_vel = ::std::abs(last_pos - position()(0));
+ last_pos = position()(0);
+ }
+ EXPECT_TRUE(At(12.0, 0));
+}
+
+// There is some somewhat tricky code for dealing with going backwards.
+TEST_F(TrapezoidProfileTest, Backwards) {
+ for (int i = 0; i < 400; ++i) {
+ RunIteration(-2, 0);
+ }
+ EXPECT_TRUE(At(-2, 0));
+}
+
+TEST_F(TrapezoidProfileTest, SwitchGoalInMiddle) {
+ for (int i = 0; i < 200; ++i) {
+ RunIteration(-2, 0);
+ }
+ EXPECT_FALSE(At(-2, 0));
+ for (int i = 0; i < 550; ++i) {
+ RunIteration(0, 0);
+ }
+ EXPECT_TRUE(At(0, 0));
+}
+
+// Checks to make sure that it hits top speed.
+TEST_F(TrapezoidProfileTest, TopSpeed) {
+ for (int i = 0; i < 200; ++i) {
+ RunIteration(4, 0);
+ }
+ EXPECT_NEAR(1.5, position()(1), 10e-5);
+ for (int i = 0; i < 2000; ++i) {
+ RunIteration(4, 0);
+ }
+ EXPECT_TRUE(At(4, 0));
+}
+
+} // namespace testing
+} // namespace util
+} // namespace aos
diff --git a/aos/common/util/util.gyp b/aos/common/util/util.gyp
new file mode 100644
index 0000000..e9a37c9
--- /dev/null
+++ b/aos/common/util/util.gyp
@@ -0,0 +1,153 @@
+{
+ 'targets': [
+ {
+ 'target_name': 'run_command',
+ 'type': 'static_library',
+ 'sources': [
+ 'run_command.cc',
+ ],
+ 'dependencies': [
+ '<(AOS)/build/aos.gyp:logging_interface',
+ ],
+ },
+ {
+ 'target_name': 'run_command_test',
+ 'type': 'executable',
+ 'sources': [
+ 'run_command_test.cc',
+ ],
+ 'dependencies': [
+ 'run_command',
+ '<(EXTERNALS):gtest',
+ '<(AOS)/build/aos.gyp:logging',
+ 'thread',
+ ],
+ },
+ {
+ 'target_name': 'death_test_log_implementation',
+ 'type': 'static_library',
+ 'sources': [
+ #'death_test_log_implementation',
+ ],
+ 'dependencies': [
+ '<(AOS)/build/aos.gyp:logging',
+ ],
+ 'export_dependent_settings': [
+ '<(AOS)/build/aos.gyp:logging',
+ ],
+ },
+ {
+ 'target_name': 'inet_addr',
+ 'type': 'static_library',
+ 'sources': [
+ 'inet_addr.cc',
+ ],
+ },
+ {
+ 'target_name': 'phased_loop',
+ 'type': 'static_library',
+ 'sources': [
+ 'phased_loop.cc',
+ ],
+ 'dependencies': [
+ '<(AOS)/build/aos.gyp:logging',
+ '<(AOS)/common/common.gyp:time',
+ ],
+ },
+ {
+ 'target_name': 'log_interval',
+ 'type': 'static_library',
+ 'sources': [
+ #'log_interval.h',
+ ],
+ 'dependencies': [
+ '<(AOS)/common/common.gyp:time',
+ '<(AOS)/build/aos.gyp:logging',
+ ],
+ 'export_dependent_settings': [
+ '<(AOS)/common/common.gyp:time',
+ '<(AOS)/build/aos.gyp:logging',
+ ],
+ },
+ {
+ 'target_name': 'string_to_num',
+ 'type': 'static_library',
+ 'sources': [
+ #'string_to_num.h',
+ ],
+ },
+ {
+ 'target_name': 'string_to_num_test',
+ 'type': 'executable',
+ 'sources': [
+ 'string_to_num_test.cc',
+ ],
+ 'dependencies': [
+ ':string_to_num',
+ '<(EXTERNALS):gtest',
+ ],
+ },
+ {
+ 'target_name': 'thread',
+ 'type': 'static_library',
+ 'sources': [
+ 'thread.cc',
+ ],
+ },
+ {
+ 'target_name': 'trapezoid_profile',
+ 'type': 'static_library',
+ 'sources': [
+ 'trapezoid_profile.cc',
+ ],
+ 'dependencies': [
+ '<(EXTERNALS):eigen',
+ '<(AOS)/common/common.gyp:time',
+ '<(AOS)/build/aos.gyp:logging',
+ ],
+ 'export_dependent_settings': [
+ '<(EXTERNALS):eigen',
+ '<(AOS)/common/common.gyp:time',
+ ],
+ },
+ {
+ 'target_name': 'trapezoid_profile_test',
+ 'type': 'executable',
+ 'sources': [
+ 'trapezoid_profile_test.cc',
+ ],
+ 'dependencies': [
+ ':trapezoid_profile',
+ '<(EXTERNALS):gtest',
+ ],
+ },
+ {
+ 'target_name': 'wrapping_counter',
+ 'type': 'static_library',
+ 'sources': [
+ 'wrapping_counter.cc',
+ ],
+ },
+ {
+ 'target_name': 'wrapping_counter_test',
+ 'type': 'executable',
+ 'sources': [
+ 'wrapping_counter_test.cc',
+ ],
+ 'dependencies': [
+ 'wrapping_counter',
+ '<(EXTERNALS):gtest',
+ ],
+ },
+ {
+ 'target_name': 'options_test',
+ 'type': 'executable',
+ 'sources': [
+ 'options_test.cc',
+ ],
+ 'dependencies': [
+ '<(EXTERNALS):gtest',
+ ],
+ },
+ ],
+}
diff --git a/aos/common/util/wrapping_counter.cc b/aos/common/util/wrapping_counter.cc
new file mode 100644
index 0000000..61f5047
--- /dev/null
+++ b/aos/common/util/wrapping_counter.cc
@@ -0,0 +1,19 @@
+#include "aos/common/util/wrapping_counter.h"
+
+namespace aos {
+namespace util {
+
+WrappingCounter::WrappingCounter(int32_t initial_count)
+ : count_(initial_count), last_count_(0) {}
+
+int32_t WrappingCounter::Update(uint8_t current) {
+ if (last_count_ > current) {
+ count_ += 0x100;
+ }
+ count_ = (count_ & 0xffffff00) | current;
+ last_count_ = current;
+ return count_;
+}
+
+} // namespace util
+} // namespace aos
diff --git a/aos/common/util/wrapping_counter.h b/aos/common/util/wrapping_counter.h
new file mode 100644
index 0000000..fbf3611
--- /dev/null
+++ b/aos/common/util/wrapping_counter.h
@@ -0,0 +1,34 @@
+#ifndef AOS_COMMON_UTIL_WRAPPING_COUNTER_H_
+#define AOS_COMMON_UTIL_WRAPPING_COUNTER_H_
+
+#include <stdint.h>
+
+namespace aos {
+namespace util {
+
+// Deals correctly with 1-byte counters which wrap.
+// This is only possible if the counter never wraps twice between Update calls.
+// It will also fail if the counter ever goes down (that will be interpreted as
+// +255 instead of -1, for example).
+class WrappingCounter {
+ public:
+ WrappingCounter(int32_t initial_count = 0);
+
+ // Updates the internal counter with a new raw value.
+ // Returns count() for convenience.
+ int32_t Update(uint8_t current);
+
+ // Resets the actual count to value.
+ void Reset(int32_t value = 0) { count_ = value; }
+
+ int32_t count() const { return count_; }
+
+ private:
+ int32_t count_;
+ uint8_t last_count_;
+};
+
+} // namespace util
+} // namespace aos
+
+#endif // AOS_COMMON_UTIL_WRAPPING_COUNTER_H_
diff --git a/aos/common/util/wrapping_counter_test.cc b/aos/common/util/wrapping_counter_test.cc
new file mode 100644
index 0000000..e257fb0
--- /dev/null
+++ b/aos/common/util/wrapping_counter_test.cc
@@ -0,0 +1,58 @@
+#include "aos/common/util/wrapping_counter.h"
+
+#include <limits.h>
+
+#include "gtest/gtest.h"
+
+namespace aos {
+namespace util {
+namespace testing {
+
+TEST(WrappingCounterTest, Basic) {
+ WrappingCounter test_counter;
+ EXPECT_EQ(0, test_counter.count());
+ EXPECT_EQ(1, test_counter.Update(1));
+ EXPECT_EQ(1, test_counter.Update(1));
+ EXPECT_EQ(2, test_counter.Update(2));
+ EXPECT_EQ(7, test_counter.Update(7));
+ EXPECT_EQ(7, test_counter.count());
+ EXPECT_EQ(123, test_counter.Update(123));
+ EXPECT_EQ(123, test_counter.count());
+}
+
+TEST(WrappingCounterTest, Reset) {
+ WrappingCounter test_counter;
+ test_counter.Update(5);
+ test_counter.Reset();
+ EXPECT_EQ(0, test_counter.count());
+ test_counter.Reset(56);
+ EXPECT_EQ(56, test_counter.count());
+}
+
+namespace {
+void test_wrapping(int16_t start, int16_t step) {
+ WrappingCounter test_counter;
+ for (int16_t i = start; i < INT16_MAX - step; i += step) {
+ EXPECT_EQ(i, test_counter.Update(i & 0xFF));
+ }
+}
+}
+
+// This tests the basic wrapping functionality.
+TEST(WrappingCounterTest, ReasonableWrapping) {
+ test_wrapping(0, 13);
+ test_wrapping(0, 53);
+ test_wrapping(0, 64);
+ test_wrapping(0, 73);
+}
+
+// It would be reasonable for these to fail if the implementation changes.
+TEST(WrappingCounterTest, UnreasonableWrapping) {
+ test_wrapping(0, 128);
+ test_wrapping(0, 213);
+ test_wrapping(0, 255);
+}
+
+} // namespace testing
+} // namespace util
+} // namespace aos
diff --git a/aos/config/10-net-eth0.rules b/aos/config/10-net-eth0.rules
new file mode 100644
index 0000000..0d8d807
--- /dev/null
+++ b/aos/config/10-net-eth0.rules
@@ -0,0 +1,6 @@
+# This is a file that will make any NIC eth0.
+# It prevents the persistent net rules generator from running because that ends
+# up naming the 1 NIC eth1 instead of when you move a disk between boxes.
+# Put it in /etc/udev/rules.d/ to use it.
+
+SUBSYSTEM=="net", ACTION=="add", ATTR{type}=="1", KERNEL=="eth*", NAME="eth0"
diff --git a/aos/config/aos.conf b/aos/config/aos.conf
new file mode 100644
index 0000000..bda250f
--- /dev/null
+++ b/aos/config/aos.conf
@@ -0,0 +1,6 @@
+# put this file in /etc/security/limits.d/ to make it work
+# you have to create a group named aos (groupadd aos) and then add anybody you want to it (vim /etc/group)
+# See limits.conf(5) for details.
+
+@aos hard memlock unlimited
+@aos hard rtprio 40
diff --git a/aos/config/configure-prime.txt b/aos/config/configure-prime.txt
new file mode 100644
index 0000000..1adaccd
--- /dev/null
+++ b/aos/config/configure-prime.txt
@@ -0,0 +1,79 @@
+This file contains notes on setting up a new BBB image.
+Doing it right requires knowing what you're doing as a Debian sysadmin, so the
+main goal of this file is to avoid forgetting anything.
+
+Daniel last updated this on 2014-01-20 with Debian Wheezy.
+
+1. Install Debian stable.
+ Follow the instructions here:
+ http://blogs.bu.edu/mhirsch/2013/11/install-debian-7-to-emmc-internal-flash-drive-of-beaglebone-black/
+ Create a "driver" user.
+2. Install aos.conf and starter.
+ I just changed aos.conf to give driver permissions instead of the group.
+3. Install and configure exim4.
+ TODO (daniel): We might not need this.
+ `apt-get install exim4-config`
+ `dpkg-reconfigure exim4-config` and select the option for making minimal
+ DNS queries (it also says some junk about modems).
+4. Configure the network.
+ Edit /etc/network/interfaces to give it the right static IP address.
+ Set up eth1 like the default eth0 in case the NIC gets assigned that (see
+ #8 below). That shouldn't be a problem any more, but it's probably good
+ to be on the safe side because it can be a pain to find a screen to fix
+ it otherwise.
+5. Install stuff.
+ This includes the realtime kernel.
+ Note that at the time of this writing, you can't apt-get install the
+ realtime kernel packages directly on the beaglebone.
+ You must download them on your computer, copy them to the beaglebone,
+ and install them using dpkg -i.
+ <http://robotics.mvla.net/files/frc971/packages/>.
+ After you do this, you will still need to modify the zImage
+ symlink to point to the right kernel.
+ `rm /boot/zImage`
+ `ln -s /boot/vmlinuz-3.8.13.9-rt20+ /boot/zImage`
+ After you reboot, you should be running the rt kernel.
+ (Check it with `uname -r`.)
+ Besides the realtime kernel packages, you'll figure everything else out
+ as you start trying to run stuff.
+6. Make SSH logins faster.
+ Add the line "UseDNS no" to /etc/ssh/sshd_config.
+7. Make it so that the programming team can log in without passwords.
+ Everybody will have to run `ssh-copy-id -i ~/.ssh/id_rsa.pub BBB` (see
+ <http://www.debian-administration.org/articles/152> for details).
+8. Make udev stop being annoying and naming NICs eth1.
+ udev wants to remember the ethernet NIC from each device and name the one
+ in a new BBB eth1, which breaks stuff. If that happens, removing
+ /etc/udev/rules.d/70-persistent-net.rules will make it autogenerate a
+ new one and fix it.
+ To prevent this problem from happening in the first place, follow the
+ directions in 10-net-eth0.rules.
+9. Set up /etc/fstab sanely.
+ Open /etc/fstab on the bbb and remove the last two lines, which the
+ comments indicate mount both partitions on the uSD card.
+ Because the uSD card shows up as /dev/mmcblk0 and pushes the internal emmc
+ to /dev/mmcblk1 when it's plugged in, using those names for the two of them
+ doesn't work very well. Instead, we use /dev/disk/by-path.
+ Add "/dev/disk/by-path/platform-mmc.14-part1 /boot/uboot msdos defaults 0 2"
+ to mount the uboot partition.
+ Also add
+ "/dev/disk/by-path/platform-mmc.5-part1 /home/driver/tmp/robot_logs ext4 defaults,noatime,data=writeback 0 0"
+ to mount the uSD card in the right place for the logs to go to it.
+10. Set up logging.
+ Fairly straightforward here. We want it to log to the uSD card, so:
+ `mkdir ~/tmp`
+ `mkdir /media/driver/sdcard/robot_logs`
+ `ln -s /media/driver/sdcard/robot_logs ~/tmp/robot_logs`
+11. Set the correct date.
+ `date` to check if date is correct.
+ `date -s <date string>` to set it if it isn't.
+12. Fix the locale setup for SSHing in.
+ `dpkg-reconfigure locales`, leave it with "en_US.UTF-8" only being
+ enabled, and then select "None" instead of that for the default in
+ the second screen.
+30. Download the code!
+50. Clone the image to the rest of the BBBs.
+ Boot up from a uSD card.
+ `dd if=/dev/mmcblk1 | gzip -c > BBB.img.gz`
+ You can then copy this image to your computer.
+ Use this image to flash other uSD cards and/or BBBs.
diff --git a/aos/config/robotCommand b/aos/config/robotCommand
new file mode 100755
index 0000000..7b726a7
--- /dev/null
+++ b/aos/config/robotCommand
@@ -0,0 +1 @@
+/home/admin/robot_code/starter.sh
diff --git a/aos/config/setup_rt_caps.sh b/aos/config/setup_rt_caps.sh
new file mode 100755
index 0000000..1ba4f65
--- /dev/null
+++ b/aos/config/setup_rt_caps.sh
@@ -0,0 +1,8 @@
+#!/bin/bash
+
+# Sets capabilities on a binary so it can run as realtime code as a user who
+# doesn't have those rlimits or some other reason they can.
+# Has to be run as root.
+
+setcap 'CAP_IPC_LOCK+pie CAP_SYS_NICE+pie' $0
+
diff --git a/aos/externals/allwpilib/README b/aos/externals/allwpilib/README
new file mode 100644
index 0000000..90595b6
--- /dev/null
+++ b/aos/externals/allwpilib/README
@@ -0,0 +1,3 @@
+This is our version of WPILib. We copy the wpilibc/ and hal/ folders from
+our customized version periodically. The commit messages all include the commit
+which is being copied in and the corresponding FPGA image version.
diff --git a/aos/externals/allwpilib/hal/CMakeLists.txt b/aos/externals/allwpilib/hal/CMakeLists.txt
new file mode 100644
index 0000000..5ad2e64
--- /dev/null
+++ b/aos/externals/allwpilib/hal/CMakeLists.txt
@@ -0,0 +1,17 @@
+cmake_minimum_required(VERSION 2.8)
+project(HAL)
+
+file(GLOB_RECURSE SRC_FILES lib/Athena/*.cpp)
+include_directories(lib/Athena lib/Athena/FRC_FPGA_ChipObject include)
+add_library(HALAthena STATIC ${SRC_FILES})
+target_link_libraries(HALAthena ${NI_LIBS})
+INSTALL(TARGETS HALAthena ARCHIVE DESTINATION lib COMPONENT lib)
+INSTALL(FILES ${NI_LIBS} ${WPI_LD_LIBS} DESTINATION lib COMPONENT ni_lib)
+INSTALL(DIRECTORY include DESTINATION ${CMAKE_INSTALL_PREFIX} COMPONENT headers)
+
+add_library(HALAthena_shared SHARED ${SRC_FILES})
+target_link_libraries(HALAthena_shared ${NI_LIBS})
+INSTALL(TARGETS HALAthena_shared LIBRARY DESTINATION lib COMPONENT lib)
+# lib/ c m gcc_s ld-linux
+# usr/lib
+# FRC_NetworkCommunication FRC_FPGA_ChipObject RoboRIO_FRC_ChipObject
diff --git a/aos/externals/allwpilib/hal/README.org b/aos/externals/allwpilib/hal/README.org
new file mode 100644
index 0000000..9eb132a
--- /dev/null
+++ b/aos/externals/allwpilib/hal/README.org
@@ -0,0 +1,94 @@
+
+* Purpose
+The HAL is a hardware abstraction layer that provides a uniform
+interface that can be used to access a number of primarily I/O
+features in the underlying platform. The features include:
+- Analog input, accumulation and triggers
+- PWM, Relay and Solenoid output
+- Digital input and output
+- I2C and SPI communication
+- Encoders and counters
+- Interrupts and Notifiers
+
+The initial goal is to allow a higher level like WPILib to support
+both the CRIO and the upcoming Athena platform only by changing which
+version of the HAL it's running on.
+
+* Editing
+You can always use any text editor and then build with Maven. There
+are also eclipse project files so that it can be edited in the same
+eclipse environment that teams develop with. For the AthenaXX, this
+can be found in the =root= directory of this project. It imports as an
+FRC Robot C++ Eclipse project. The Windriver project can be imported
+from the =src= directory.
+
+* Building with Maven
+There are multiple build targets that the HAL supports. Instructions
+for setting up the environment and building each of these is described
+below. Current targets are listed below:
+- All: All of the following targets.
+- include: The header files for the HAL.
+- Azalea: CRIO C++ build.
+- AthenaXX: Athena Dos Equis C++ build.
+- AthenaXXJava: Athena Dos Equis Java build with auto-generated JNA
+ wrappers.
+
+Output from each build target is placed in the directory
+=target/<target-name>=. So, the Azalea output is placed in
+=target/Azalea=.
+
+** All
+Note: Windows only due to the Windriver requirement.
+1. Ensure that =C:\WindRiver\gnu\3.4.4-vxworks-6.3\x86-win32\bin= is
+ on the system path so that =ccppc= and =arppc= can be accessed.
+2. Set the environment variable =WIND_BASE= to =C:\WindRiver\vxworks-6.3=.
+3. Ensure that
+ =$HOME/wpilib/toolchains/arm-none-linux-gnueabi-4.4.1/bin/= is on
+ the system path so that =arm-none-linux-gnueabi-g++= and
+ =arm-none-linux-gnueabi-ar= can be accessed.
+4. Checkout and install the NI-Libraries from Github:
+ [[https://github.com/first/NI-Libraries]].
+5. Run the following maven command:
+ =mvn clean install=
+6. Success
+
+** include
+1. =cd= into the include directory: =cd include=
+2. Run the following maven command:
+ =mvn clean install=
+3. Success
+
+** Azalea
+Note: Windows only.
+1. Ensure that =C:\WindRiver\gnu\3.4.4-vxworks-6.3\x86-win32\bin= is
+ on the system path so that =ccppc= and =arppc= can be accessed.
+2. Set the environment variable =WIND_BASE= to =C:\WindRiver\vxworks-6.3=.
+3. =cd= into the AthenaXX directory: =cd AthenaXX=
+4. =cd= into the Azalea directory: =cd Azalea=
+5. Run the following maven command:
+ =mvn clean install=
+6. Success
+
+** AthenaXX
+1. Ensure that
+ =$HOME/wpilib/toolchains/arm-none-linux-gnueabi-4.4.1/bin/= is on
+ the system path so that =arm-none-linux-gnueabi-g++= and
+ =arm-none-linux-gnueabi-ar= can be accessed.
+2. Install the include target.
+3. =cd= into the AthenaXX directory: =cd AthenaXX=
+4. Run the following maven command:
+ =mvn clean install=
+5. Success
+
+** AthenaXXJava
+1. Ensure that
+ =$HOME/wpilib/toolchains/arm-none-linux-gnueabi-4.4.1/bin/= is on
+ the system path so that =arm-none-linux-gnueabi-g++= and
+ =arm-none-linux-gnueabi-ar= can be accessed.
+2. Checkout and install the NI-Libraries from Github:
+ [[https://github.com/first/NI-Libraries]].
+3. Install the include target.
+4. =cd= into the AthenaXXJava directory: =cd AthenaXXJava=
+5. Run the following maven command:
+ =mvn clean install=
+6. Success
diff --git a/aos/externals/allwpilib/hal/include/HAL/Accelerometer.hpp b/aos/externals/allwpilib/hal/include/HAL/Accelerometer.hpp
new file mode 100644
index 0000000..103fb2a
--- /dev/null
+++ b/aos/externals/allwpilib/hal/include/HAL/Accelerometer.hpp
@@ -0,0 +1,15 @@
+#pragma once
+
+enum AccelerometerRange {
+ kRange_2G = 0,
+ kRange_4G = 1,
+ kRange_8G = 2,
+};
+
+extern "C" {
+ void setAccelerometerActive(bool);
+ void setAccelerometerRange(AccelerometerRange);
+ double getAccelerometerX();
+ double getAccelerometerY();
+ double getAccelerometerZ();
+}
diff --git a/aos/externals/allwpilib/hal/include/HAL/Analog.hpp b/aos/externals/allwpilib/hal/include/HAL/Analog.hpp
new file mode 100644
index 0000000..981aa9e
--- /dev/null
+++ b/aos/externals/allwpilib/hal/include/HAL/Analog.hpp
@@ -0,0 +1,80 @@
+#pragma once
+
+#ifdef __vxworks
+#include <vxWorks.h>
+#else
+#include <stdint.h>
+#endif
+
+enum AnalogTriggerType
+{
+ kInWindow = 0,
+ kState = 1,
+ kRisingPulse = 2,
+ kFallingPulse = 3
+};
+
+extern "C"
+{
+ // Analog output functions
+ void* initializeAnalogOutputPort(void* port_pointer, int32_t *status);
+ void setAnalogOutput(void* analog_port_pointer, double voltage, int32_t *status);
+ double getAnalogOutput(void* analog_port_pointer, int32_t *status);
+ bool checkAnalogOutputChannel(uint32_t pin);
+
+ // Analog input functions
+ void* initializeAnalogInputPort(void* port_pointer, int32_t *status);
+ bool checkAnalogModule(uint8_t module);
+ bool checkAnalogInputChannel(uint32_t pin);
+
+ void setAnalogSampleRate(double samplesPerSecond, int32_t *status);
+ float getAnalogSampleRate(int32_t *status);
+ void setAnalogAverageBits(void* analog_port_pointer, uint32_t bits, int32_t *status);
+ uint32_t getAnalogAverageBits(void* analog_port_pointer, int32_t *status);
+ void setAnalogOversampleBits(void* analog_port_pointer, uint32_t bits, int32_t *status);
+ uint32_t getAnalogOversampleBits(void* analog_port_pointer, int32_t *status);
+ int16_t getAnalogValue(void* analog_port_pointer, int32_t *status);
+ int32_t getAnalogAverageValue(void* analog_port_pointer, int32_t *status);
+ int32_t getAnalogVoltsToValue(void* analog_port_pointer, double voltage, int32_t *status);
+ float getAnalogVoltage(void* analog_port_pointer, int32_t *status);
+ float getAnalogAverageVoltage(void* analog_port_pointer, int32_t *status);
+ uint32_t getAnalogLSBWeight(void* analog_port_pointer, int32_t *status);
+ int32_t getAnalogOffset(void* analog_port_pointer, int32_t *status);
+
+ bool isAccumulatorChannel(void* analog_port_pointer, int32_t *status);
+ void initAccumulator(void* analog_port_pointer, int32_t *status);
+ void resetAccumulator(void* analog_port_pointer, int32_t *status);
+ void setAccumulatorCenter(void* analog_port_pointer, int32_t center, int32_t *status);
+ void setAccumulatorDeadband(void* analog_port_pointer, int32_t deadband, int32_t *status);
+ int64_t getAccumulatorValue(void* analog_port_pointer, int32_t *status);
+ uint32_t getAccumulatorCount(void* analog_port_pointer, int32_t *status);
+ void getAccumulatorOutput(void* analog_port_pointer, int64_t *value, uint32_t *count,
+ int32_t *status);
+
+ void* initializeAnalogTrigger(void* port_pointer, uint32_t *index, int32_t *status);
+ void cleanAnalogTrigger(void* analog_trigger_pointer, int32_t *status);
+ void setAnalogTriggerLimitsRaw(void* analog_trigger_pointer, int32_t lower, int32_t upper,
+ int32_t *status);
+ void setAnalogTriggerLimitsVoltage(void* analog_trigger_pointer, double lower, double upper,
+ int32_t *status);
+ void setAnalogTriggerAveraged(void* analog_trigger_pointer, bool useAveragedValue,
+ int32_t *status);
+ void setAnalogTriggerFiltered(void* analog_trigger_pointer, bool useFilteredValue,
+ int32_t *status);
+ bool getAnalogTriggerInWindow(void* analog_trigger_pointer, int32_t *status);
+ bool getAnalogTriggerTriggerState(void* analog_trigger_pointer, int32_t *status);
+ bool getAnalogTriggerOutput(void* analog_trigger_pointer, AnalogTriggerType type,
+ int32_t *status);
+
+ //// Float JNA Hack
+ // Float
+ int getAnalogSampleRateIntHack(int32_t *status);
+ int getAnalogVoltageIntHack(void* analog_port_pointer, int32_t *status);
+ int getAnalogAverageVoltageIntHack(void* analog_port_pointer, int32_t *status);
+
+ // Doubles
+ void setAnalogSampleRateIntHack(int samplesPerSecond, int32_t *status);
+ int32_t getAnalogVoltsToValueIntHack(void* analog_port_pointer, int voltage, int32_t *status);
+ void setAnalogTriggerLimitsVoltageIntHack(void* analog_trigger_pointer, int lower, int upper,
+ int32_t *status);
+}
diff --git a/aos/externals/allwpilib/hal/include/HAL/CAN.hpp b/aos/externals/allwpilib/hal/include/HAL/CAN.hpp
new file mode 100644
index 0000000..80b589c
--- /dev/null
+++ b/aos/externals/allwpilib/hal/include/HAL/CAN.hpp
@@ -0,0 +1,26 @@
+#pragma once
+
+#include <stdint.h>
+#include "NetworkCommunication/CANSessionMux.h"
+
+void canTxSend(uint32_t arbID, uint8_t length, int32_t period = CAN_SEND_PERIOD_NO_REPEAT);
+
+void canTxPackInt8 (uint32_t arbID, uint8_t offset, uint8_t value);
+void canTxPackInt16(uint32_t arbID, uint8_t offset, uint16_t value);
+void canTxPackInt32(uint32_t arbID, uint8_t offset, uint32_t value);
+void canTxPackFXP16(uint32_t arbID, uint8_t offset, double value);
+void canTxPackFXP32(uint32_t arbID, uint8_t offset, double value);
+
+uint8_t canTxUnpackInt8 (uint32_t arbID, uint8_t offset);
+uint32_t canTxUnpackInt32(uint32_t arbID, uint8_t offset);
+uint16_t canTxUnpackInt16(uint32_t arbID, uint8_t offset);
+double canTxUnpackFXP16(uint32_t arbID, uint8_t offset);
+double canTxUnpackFXP32(uint32_t arbID, uint8_t offset);
+
+bool canRxReceive(uint32_t arbID);
+
+uint8_t canRxUnpackInt8 (uint32_t arbID, uint8_t offset);
+uint16_t canRxUnpackInt16(uint32_t arbID, uint8_t offset);
+uint32_t canRxUnpackInt32(uint32_t arbID, uint8_t offset);
+double canRxUnpackFXP16(uint32_t arbID, uint8_t offset);
+double canRxUnpackFXP32(uint32_t arbID, uint8_t offset);
diff --git a/aos/externals/allwpilib/hal/include/HAL/Compressor.hpp b/aos/externals/allwpilib/hal/include/HAL/Compressor.hpp
new file mode 100644
index 0000000..5c222eb
--- /dev/null
+++ b/aos/externals/allwpilib/hal/include/HAL/Compressor.hpp
@@ -0,0 +1,37 @@
+/**
+ * Compressor.h
+ * Methods for interacting with a compressor with the CAN PCM device
+ */
+
+#ifdef __vxworks
+#include <vxWorks.h>
+#else
+#include <stdint.h>
+#endif
+
+#ifndef __HAL_COMPRESSOR_H__
+#define __HAL_COMPRESSOR_H__
+
+extern "C" {
+ void *initializeCompressor(uint8_t module);
+ bool checkCompressorModule(uint8_t module);
+
+ bool getCompressor(void *pcm_pointer, int32_t *status);
+
+ void setClosedLoopControl(void *pcm_pointer, bool value, int32_t *status);
+ bool getClosedLoopControl(void *pcm_pointer, int32_t *status);
+
+ bool getPressureSwitch(void *pcm_pointer, int32_t *status);
+ float getCompressorCurrent(void *pcm_pointer, int32_t *status);
+
+ bool getCompressorCurrentTooHighFault(void *pcm_pointer, int32_t *status);
+ bool getCompressorCurrentTooHighStickyFault(void *pcm_pointer, int32_t *status);
+ bool getCompressorShortedStickyFault(void *pcm_pointer, int32_t *status);
+ bool getCompressorShortedFault(void *pcm_pointer, int32_t *status);
+ bool getCompressorNotConnectedStickyFault(void *pcm_pointer, int32_t *status);
+ bool getCompressorNotConnectedFault(void *pcm_pointer, int32_t *status);
+ void clearAllPCMStickyFaults(void *pcm_pointer, int32_t *status);
+}
+
+#endif
+
diff --git a/aos/externals/allwpilib/hal/include/HAL/Digital.hpp b/aos/externals/allwpilib/hal/include/HAL/Digital.hpp
new file mode 100644
index 0000000..4d77976
--- /dev/null
+++ b/aos/externals/allwpilib/hal/include/HAL/Digital.hpp
@@ -0,0 +1,124 @@
+#pragma once
+#ifdef __vxworks
+#include <vxWorks.h>
+#else
+#include <stdint.h>
+#endif
+#include "HAL/cpp/Synchronized.hpp"
+
+enum Mode
+{
+ kTwoPulse = 0,
+ kSemiperiod = 1,
+ kPulseLength = 2,
+ kExternalDirection = 3
+};
+
+extern "C"
+{
+ void* initializeDigitalPort(void* port_pointer, int32_t *status);
+ bool checkPWMChannel(void* digital_port_pointer);
+ bool checkRelayChannel(void* digital_port_pointer);
+
+ void setPWM(void* digital_port_pointer, unsigned short value, int32_t *status);
+ bool allocatePWMChannel(void* digital_port_pointer, int32_t *status);
+ void freePWMChannel(void* digital_port_pointer, int32_t *status);
+ unsigned short getPWM(void* digital_port_pointer, int32_t *status);
+ void latchPWMZero(void* digital_port_pointer, int32_t *status);
+ void setPWMPeriodScale(void* digital_port_pointer, uint32_t squelchMask, int32_t *status);
+ void* allocatePWM(int32_t *status);
+ void freePWM(void* pwmGenerator, int32_t *status);
+ void setPWMRate(double rate, int32_t *status);
+ void setPWMDutyCycle(void* pwmGenerator, double dutyCycle, int32_t *status);
+ void setPWMOutputChannel(void* pwmGenerator, uint32_t pin, int32_t *status);
+
+ void setRelayForward(void* digital_port_pointer, bool on, int32_t *status);
+ void setRelayReverse(void* digital_port_pointer, bool on, int32_t *status);
+ bool getRelayForward(void* digital_port_pointer, int32_t *status);
+ bool getRelayReverse(void* digital_port_pointer, int32_t *status);
+
+ bool allocateDIO(void* digital_port_pointer, bool input, int32_t *status);
+ void freeDIO(void* digital_port_pointer, int32_t *status);
+ void setDIO(void* digital_port_pointer, short value, int32_t *status);
+ bool getDIO(void* digital_port_pointer, int32_t *status);
+ bool getDIODirection(void* digital_port_pointer, int32_t *status);
+ void pulse(void* digital_port_pointer, double pulseLength, int32_t *status);
+ bool isPulsing(void* digital_port_pointer, int32_t *status);
+ bool isAnyPulsing(int32_t *status);
+
+ void setFilterSelect(void* digital_port_pointer, int filter_index,
+ int32_t* status);
+ int getFilterSelect(void* digital_port_pointer, int32_t* status);
+
+ void setFilterPeriod(int filter_index, uint32_t value, int32_t *status);
+ uint32_t getFilterPeriod(int filter_index, int32_t *status);
+
+ void* initializeCounter(Mode mode, uint32_t *index, int32_t *status);
+ void freeCounter(void* counter_pointer, int32_t *status);
+ void setCounterAverageSize(void* counter_pointer, int32_t size, int32_t *status);
+ void setCounterUpSource(void* counter_pointer, uint32_t pin, bool analogTrigger, int32_t *status);
+ void setCounterUpSourceEdge(void* counter_pointer, bool risingEdge, bool fallingEdge,
+ int32_t *status);
+ void clearCounterUpSource(void* counter_pointer, int32_t *status);
+ void setCounterDownSource(void* counter_pointer, uint32_t pin, bool analogTrigger, int32_t *status);
+ void setCounterDownSourceEdge(void* counter_pointer, bool risingEdge, bool fallingEdge,
+ int32_t *status);
+ void clearCounterDownSource(void* counter_pointer, int32_t *status);
+ void setCounterUpDownMode(void* counter_pointer, int32_t *status);
+ void setCounterExternalDirectionMode(void* counter_pointer, int32_t *status);
+ void setCounterSemiPeriodMode(void* counter_pointer, bool highSemiPeriod, int32_t *status);
+ void setCounterPulseLengthMode(void* counter_pointer, double threshold, int32_t *status);
+ int32_t getCounterSamplesToAverage(void* counter_pointer, int32_t *status);
+ void setCounterSamplesToAverage(void* counter_pointer, int samplesToAverage, int32_t *status);
+ void resetCounter(void* counter_pointer, int32_t *status);
+ int32_t getCounter(void* counter_pointer, int32_t *status);
+ double getCounterPeriod(void* counter_pointer, int32_t *status);
+ void setCounterMaxPeriod(void* counter_pointer, double maxPeriod, int32_t *status);
+ void setCounterUpdateWhenEmpty(void* counter_pointer, bool enabled, int32_t *status);
+ bool getCounterStopped(void* counter_pointer, int32_t *status);
+ bool getCounterDirection(void* counter_pointer, int32_t *status);
+ void setCounterReverseDirection(void* counter_pointer, bool reverseDirection, int32_t *status);
+
+ void* initializeEncoder(uint8_t port_a_module, uint32_t port_a_pin, bool port_a_analog_trigger,
+ uint8_t port_b_module, uint32_t port_b_pin, bool port_b_analog_trigger,
+ bool reverseDirection, int32_t *index, int32_t *status); // TODO: fix routing
+ void freeEncoder(void* encoder_pointer, int32_t *status);
+ void resetEncoder(void* encoder_pointer, int32_t *status);
+ int32_t getEncoder(void* encoder_pointer, int32_t *status); // Raw value
+ double getEncoderPeriod(void* encoder_pointer, int32_t *status);
+ void setEncoderMaxPeriod(void* encoder_pointer, double maxPeriod, int32_t *status);
+ bool getEncoderStopped(void* encoder_pointer, int32_t *status);
+ bool getEncoderDirection(void* encoder_pointer, int32_t *status);
+ void setEncoderReverseDirection(void* encoder_pointer, bool reverseDirection, int32_t *status);
+ void setEncoderSamplesToAverage(void* encoder_pointer, uint32_t samplesToAverage,
+ int32_t *status);
+ uint32_t getEncoderSamplesToAverage(void* encoder_pointer, int32_t *status);
+ void setEncoderIndexSource(void *encoder_pointer, uint32_t pin, bool analogTrigger, bool activeHigh,
+ bool edgeSensitive, int32_t *status);
+
+ uint16_t getLoopTiming(int32_t *status);
+
+ void spiInitialize(uint8_t port, int32_t *status);
+ int32_t spiTransaction(uint8_t port, uint8_t *dataToSend, uint8_t *dataReceived, uint8_t size);
+ int32_t spiWrite(uint8_t port, uint8_t* dataToSend, uint8_t sendSize);
+ int32_t spiRead(uint8_t port, uint8_t *buffer, uint8_t count);
+ void spiClose(uint8_t port);
+ void spiSetSpeed(uint8_t port, uint32_t speed);
+ void spiSetBitsPerWord(uint8_t port, uint8_t bpw);
+ void spiSetOpts(uint8_t port, int msb_first, int sample_on_trailing, int clk_idle_high);
+ void spiSetChipSelectActiveHigh(uint8_t port, int32_t *status);
+ void spiSetChipSelectActiveLow(uint8_t port, int32_t *status);
+ int32_t spiGetHandle(uint8_t port);
+ void spiSetHandle(uint8_t port, int32_t handle);
+
+ void i2CInitialize(uint8_t port, int32_t *status);
+ int32_t i2CTransaction(uint8_t port, uint8_t deviceAddress, uint8_t *dataToSend, uint8_t sendSize, uint8_t *dataReceived, uint8_t receiveSize);
+ int32_t i2CWrite(uint8_t port, uint8_t deviceAddress, uint8_t *dataToSend, uint8_t sendSize);
+ int32_t i2CRead(uint8_t port, uint8_t deviceAddress, uint8_t *buffer, uint8_t count);
+ void i2CClose(uint8_t port);
+
+ //// Float JNA Hack
+ // double
+ void setPWMRateIntHack(int rate, int32_t *status);
+ void setPWMDutyCycleIntHack(void* pwmGenerator, int32_t dutyCycle, int32_t *status);
+}
diff --git a/aos/externals/allwpilib/hal/include/HAL/Errors.hpp b/aos/externals/allwpilib/hal/include/HAL/Errors.hpp
new file mode 100644
index 0000000..a64c3c4
--- /dev/null
+++ b/aos/externals/allwpilib/hal/include/HAL/Errors.hpp
@@ -0,0 +1,65 @@
+#pragma once
+
+#define CTR_RxTimeout_MESSAGE "CTRE CAN Recieve Timeout"
+#define CTR_TxTimeout_MESSAGE "CTRE CAN Transmit Timeout"
+#define CTR_InvalidParamValue_MESSAGE "CTRE CAN Invalid Parameter"
+#define CTR_UnexpectedArbId_MESSAGE "CTRE Unexpected Arbitration ID (CAN Node ID)"
+#define CTR_TxFailed_MESSAGE "CTRE CAN Transmit Error"
+#define CTR_SigNotUpdated_MESSAGE "CTRE CAN Signal Not Updated"
+
+#define NiFpga_Status_FifoTimeout_MESSAGE "NIFPGA: FIFO timeout error"
+#define NiFpga_Status_TransferAborted_MESSAGE "NIFPGA: Transfer aborted error"
+#define NiFpga_Status_MemoryFull_MESSAGE "NIFPGA: Memory Allocation failed, memory full"
+#define NiFpga_Status_SoftwareFault_MESSAGE "NIFPGA: Unexepected software error"
+#define NiFpga_Status_InvalidParameter_MESSAGE "NIFPGA: Invalid Parameter"
+#define NiFpga_Status_ResourceNotFound_MESSAGE "NIFPGA: Resource not found"
+#define NiFpga_Status_ResourceNotInitialized_MESSAGE "NIFPGA: Resource not initialized"
+#define NiFpga_Status_HardwareFault_MESSAGE "NIFPGA: Hardware Fault"
+#define NiFpga_Status_IrqTimeout_MESSAGE "NIFPGA: Interrupt timeout"
+#define NiFpga_Status_VersionMismatch_MESSAGE "NIFPGA: Version Mismatch"
+
+#define ERR_CANSessionMux_InvalidBuffer_MESSAGE "CAN: Invalid Buffer"
+#define ERR_CANSessionMux_MessageNotFound_MESSAGE "CAN: Message not found"
+#define WARN_CANSessionMux_NoToken_MESSAGE "CAN: No token"
+#define ERR_CANSessionMux_NotAllowed_MESSAGE "CAN: Not allowed"
+#define ERR_CANSessionMux_NotInitialized_MESSAGE "CAN: Not initialized"
+
+#define SAMPLE_RATE_TOO_HIGH 1001
+#define SAMPLE_RATE_TOO_HIGH_MESSAGE "HAL: Analog module sample rate is too high"
+#define VOLTAGE_OUT_OF_RANGE 1002
+#define VOLTAGE_OUT_OF_RANGE_MESSAGE "HAL: Voltage to convert to raw value is out of range [0; 5]"
+#define LOOP_TIMING_ERROR 1004
+#define LOOP_TIMING_ERROR_MESSAGE "HAL: Digital module loop timing is not the expected value"
+#define SPI_WRITE_NO_MOSI 1012
+#define SPI_WRITE_NO_MOSI_MESSAGE "HAL: Cannot write to SPI port with no MOSI output"
+#define SPI_READ_NO_MISO 1013
+#define SPI_READ_NO_MISO_MESSAGE "HAL: Cannot read from SPI port with no MISO input"
+#define SPI_READ_NO_DATA 1014
+#define SPI_READ_NO_DATA_MESSAGE "HAL: No data available to read from SPI"
+#define INCOMPATIBLE_STATE 1015
+#define INCOMPATIBLE_STATE_MESSAGE "HAL: Incompatible State: The operation cannot be completed"
+#define NO_AVAILABLE_RESOURCES -1004
+#define NO_AVAILABLE_RESOURCES_MESSAGE "HAL: No available resources to allocate"
+#define NULL_PARAMETER -1005
+#define NULL_PARAMETER_MESSAGE "HAL: A pointer parameter to a method is NULL"
+#define ANALOG_TRIGGER_LIMIT_ORDER_ERROR -1010
+#define ANALOG_TRIGGER_LIMIT_ORDER_ERROR_MESSAGE "HAL: AnalogTrigger limits error. Lower limit > Upper Limit"
+#define ANALOG_TRIGGER_PULSE_OUTPUT_ERROR -1011
+#define ANALOG_TRIGGER_PULSE_OUTPUT_ERROR_MESSAGE "HAL: Attempted to read AnalogTrigger pulse output."
+#define PARAMETER_OUT_OF_RANGE -1028
+#define PARAMETER_OUT_OF_RANGE_MESSAGE "HAL: A parameter is out of range."
+#define RESOURCE_IS_ALLOCATED -1029
+#define RESOURCE_IS_ALLOCATED_MESSAGE "HAL: Resource already allocated"
+
+#define VI_ERROR_SYSTEM_ERROR_MESSAGE "HAL - VISA: System Error";
+#define VI_ERROR_INV_OBJECT_MESSAGE "HAL - VISA: Invalid Object"
+#define VI_ERROR_RSRC_LOCKED_MESSAGE "HAL - VISA: Resource Locked"
+#define VI_ERROR_RSRC_NFOUND_MESSAGE "HAL - VISA: Resource Not Found"
+#define VI_ERROR_INV_RSRC_NAME_MESSAGE "HAL - VISA: Invalid Resource Name"
+#define VI_ERROR_QUEUE_OVERFLOW_MESSAGE "HAL - VISA: Queue Overflow"
+#define VI_ERROR_IO_MESSAGE "HAL - VISA: General IO Error"
+#define VI_ERROR_ASRL_PARITY_MESSAGE "HAL - VISA: Parity Error"
+#define VI_ERROR_ASRL_FRAMING_MESSAGE "HAL - VISA: Framing Error"
+#define VI_ERROR_ASRL_OVERRUN_MESSAGE "HAL - VISA: Buffer Overrun Error"
+#define VI_ERROR_RSRC_BUSY_MESSAGE "HAL - VISA: Resource Busy"
+#define VI_ERROR_INV_PARAMETER_MESSAGE "HAL - VISA: Invalid Parameter"
diff --git a/aos/externals/allwpilib/hal/include/HAL/HAL.hpp b/aos/externals/allwpilib/hal/include/HAL/HAL.hpp
new file mode 100644
index 0000000..9c6acb7
--- /dev/null
+++ b/aos/externals/allwpilib/hal/include/HAL/HAL.hpp
@@ -0,0 +1,257 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2013. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+#pragma once
+
+#ifdef __vxworks
+#include <vxWorks.h>
+#else
+#include <stdint.h>
+#endif
+#include <cmath>
+
+#include "Accelerometer.hpp"
+#include "Analog.hpp"
+#include "Compressor.hpp"
+#include "Digital.hpp"
+#include "Solenoid.hpp"
+#include "Notifier.hpp"
+#include "Interrupts.hpp"
+#include "Errors.hpp"
+#include "PDP.hpp"
+#include "Power.hpp"
+
+#include "Utilities.hpp"
+#include "Semaphore.hpp"
+#include "Task.hpp"
+
+#define HAL_IO_CONFIG_DATA_SIZE 32
+#define HAL_SYS_STATUS_DATA_SIZE 44
+#define HAL_USER_STATUS_DATA_SIZE (984 - HAL_IO_CONFIG_DATA_SIZE - HAL_SYS_STATUS_DATA_SIZE)
+
+#define HALFRC_NetworkCommunication_DynamicType_DSEnhancedIO_Input 17
+#define HALFRC_NetworkCommunication_DynamicType_DSEnhancedIO_Output 18
+#define HALFRC_NetworkCommunication_DynamicType_Kinect_Header 19
+#define HALFRC_NetworkCommunication_DynamicType_Kinect_Extra1 20
+#define HALFRC_NetworkCommunication_DynamicType_Kinect_Vertices1 21
+#define HALFRC_NetworkCommunication_DynamicType_Kinect_Extra2 22
+#define HALFRC_NetworkCommunication_DynamicType_Kinect_Vertices2 23
+#define HALFRC_NetworkCommunication_DynamicType_Kinect_Joystick 24
+#define HALFRC_NetworkCommunication_DynamicType_Kinect_Custom 25
+
+namespace HALUsageReporting
+{
+ enum tResourceType
+ {
+ kResourceType_Controller,
+ kResourceType_Module,
+ kResourceType_Language,
+ kResourceType_CANPlugin,
+ kResourceType_Accelerometer,
+ kResourceType_ADXL345,
+ kResourceType_AnalogChannel,
+ kResourceType_AnalogTrigger,
+ kResourceType_AnalogTriggerOutput,
+ kResourceType_CANJaguar,
+ kResourceType_Compressor,
+ kResourceType_Counter,
+ kResourceType_Dashboard,
+ kResourceType_DigitalInput,
+ kResourceType_DigitalOutput,
+ kResourceType_DriverStationCIO,
+ kResourceType_DriverStationEIO,
+ kResourceType_DriverStationLCD,
+ kResourceType_Encoder,
+ kResourceType_GearTooth,
+ kResourceType_Gyro,
+ kResourceType_I2C,
+ kResourceType_Framework,
+ kResourceType_Jaguar,
+ kResourceType_Joystick,
+ kResourceType_Kinect,
+ kResourceType_KinectStick,
+ kResourceType_PIDController,
+ kResourceType_Preferences,
+ kResourceType_PWM,
+ kResourceType_Relay,
+ kResourceType_RobotDrive,
+ kResourceType_SerialPort,
+ kResourceType_Servo,
+ kResourceType_Solenoid,
+ kResourceType_SPI,
+ kResourceType_Task,
+ kResourceType_Ultrasonic,
+ kResourceType_Victor,
+ kResourceType_Button,
+ kResourceType_Command,
+ kResourceType_AxisCamera,
+ kResourceType_PCVideoServer,
+ kResourceType_SmartDashboard,
+ kResourceType_Talon,
+ kResourceType_HiTechnicColorSensor,
+ kResourceType_HiTechnicAccel,
+ kResourceType_HiTechnicCompass,
+ kResourceType_SRF08,
+ kResourceType_DigitalGlitchFilter,
+ };
+
+ enum tInstances
+ {
+ kLanguage_LabVIEW = 1,
+ kLanguage_CPlusPlus = 2,
+ kLanguage_Java = 3,
+ kLanguage_Python = 4,
+
+ kCANPlugin_BlackJagBridge = 1,
+ kCANPlugin_2CAN = 2,
+
+ kFramework_Iterative = 1,
+ kFramework_Simple = 2,
+
+ kRobotDrive_ArcadeStandard = 1,
+ kRobotDrive_ArcadeButtonSpin = 2,
+ kRobotDrive_ArcadeRatioCurve = 3,
+ kRobotDrive_Tank = 4,
+ kRobotDrive_MecanumPolar = 5,
+ kRobotDrive_MecanumCartesian = 6,
+
+ kDriverStationCIO_Analog = 1,
+ kDriverStationCIO_DigitalIn = 2,
+ kDriverStationCIO_DigitalOut = 3,
+
+ kDriverStationEIO_Acceleration = 1,
+ kDriverStationEIO_AnalogIn = 2,
+ kDriverStationEIO_AnalogOut = 3,
+ kDriverStationEIO_Button = 4,
+ kDriverStationEIO_LED = 5,
+ kDriverStationEIO_DigitalIn = 6,
+ kDriverStationEIO_DigitalOut = 7,
+ kDriverStationEIO_FixedDigitalOut = 8,
+ kDriverStationEIO_PWM = 9,
+ kDriverStationEIO_Encoder = 10,
+ kDriverStationEIO_TouchSlider = 11,
+
+ kADXL345_SPI = 1,
+ kADXL345_I2C = 2,
+
+ kCommand_Scheduler = 1,
+
+ kSmartDashboard_Instance = 1,
+ };
+}
+
+struct HALControlWord {
+ uint32_t enabled : 1;
+ uint32_t autonomous : 1;
+ uint32_t test :1;
+ uint32_t eStop : 1;
+ uint32_t fmsAttached:1;
+ uint32_t dsAttached:1;
+ uint32_t control_reserved : 26;
+};
+
+enum HALAllianceStationID {
+ kHALAllianceStationID_red1,
+ kHALAllianceStationID_red2,
+ kHALAllianceStationID_red3,
+ kHALAllianceStationID_blue1,
+ kHALAllianceStationID_blue2,
+ kHALAllianceStationID_blue3,
+};
+
+/* The maximum number of axes that will be stored in a single HALJoystickAxes
+ struct. This is used for allocating buffers, not bounds checking, since
+ there are usually less axes in practice. */
+static constexpr size_t kMaxJoystickAxes = 12;
+static constexpr size_t kMaxJoystickPOVs = 12;
+
+struct HALJoystickAxes {
+ uint16_t count;
+ int16_t axes[kMaxJoystickAxes];
+};
+
+struct HALJoystickPOVs {
+ uint16_t count;
+ int16_t povs[kMaxJoystickPOVs];
+};
+
+struct HALJoystickButtons {
+ uint32_t buttons;
+ uint8_t count;
+};
+
+struct HALJoystickDescriptor {
+ uint8_t isXbox;
+ uint8_t type;
+ char name[256];
+ uint8_t axisCount;
+ uint8_t axisTypes;
+ uint8_t buttonCount;
+ uint8_t povCount;
+};
+
+inline float intToFloat(int value)
+{
+ return (float)value;
+}
+
+inline int floatToInt(float value)
+{
+ return round(value);
+}
+
+extern "C"
+{
+ extern const uint32_t dio_kNumSystems;
+ extern const uint32_t solenoid_kNumDO7_0Elements;
+ extern const uint32_t interrupt_kNumSystems;
+ extern const uint32_t kSystemClockTicksPerMicrosecond;
+
+ void* getPort(uint8_t pin);
+ void* getPortWithModule(uint8_t module, uint8_t pin);
+ const char* getHALErrorMessage(int32_t code);
+
+ uint16_t getFPGAVersion(int32_t *status);
+ uint32_t getFPGARevision(int32_t *status);
+ uint32_t getFPGATime(int32_t *status);
+
+ bool getFPGAButton(int32_t *status);
+
+ int HALSetErrorData(const char *errors, int errorsLength, int wait_ms);
+
+ int HALGetControlWord(HALControlWord *data);
+ int HALGetAllianceStation(enum HALAllianceStationID *allianceStation);
+ int HALGetJoystickAxes(uint8_t joystickNum, HALJoystickAxes *axes);
+ int HALGetJoystickPOVs(uint8_t joystickNum, HALJoystickPOVs *povs);
+ int HALGetJoystickButtons(uint8_t joystickNum, HALJoystickButtons *buttons);
+ int HALGetJoystickDescriptor(uint8_t joystickNum, HALJoystickDescriptor *desc);
+ int HALSetJoystickOutputs(uint8_t joystickNum, uint32_t outputs, uint16_t leftRumble, uint16_t rightRumble);
+ int HALGetMatchTime(float *matchTime);
+
+ void HALSetNewDataSem(MULTIWAIT_ID sem);
+
+ bool HALGetSystemActive(int32_t *status);
+ bool HALGetBrownedOut(int32_t *status);
+
+ int HALInitialize(int mode = 0);
+ void HALNetworkCommunicationObserveUserProgramStarting();
+ void HALNetworkCommunicationObserveUserProgramDisabled();
+ void HALNetworkCommunicationObserveUserProgramAutonomous();
+ void HALNetworkCommunicationObserveUserProgramTeleop();
+ void HALNetworkCommunicationObserveUserProgramTest();
+
+ uint32_t HALReport(uint8_t resource, uint8_t instanceNumber, uint8_t context = 0,
+ const char *feature = NULL);
+}
+
+// TODO: HACKS for now...
+extern "C"
+{
+
+ void NumericArrayResize();
+ void RTSetCleanupProc();
+ void EDVR_CreateReference();
+ void Occur();
+}
diff --git a/aos/externals/allwpilib/hal/include/HAL/Interrupts.hpp b/aos/externals/allwpilib/hal/include/HAL/Interrupts.hpp
new file mode 100644
index 0000000..636e809
--- /dev/null
+++ b/aos/externals/allwpilib/hal/include/HAL/Interrupts.hpp
@@ -0,0 +1,30 @@
+#pragma once
+
+#ifdef __vxworks
+#include <vxWorks.h>
+#else
+#include <stdint.h>
+#endif
+
+#include <iostream>
+#include "errno.h"
+
+extern "C"
+{
+ typedef void (*InterruptHandlerFunction)(uint32_t interruptAssertedMask, void *param);
+
+ void* initializeInterrupts(uint32_t interruptIndex, bool watcher, int32_t *status);
+ void cleanInterrupts(void* interrupt_pointer, int32_t *status);
+
+ uint32_t waitForInterrupt(void* interrupt_pointer, double timeout, bool ignorePrevious, int32_t *status);
+ void enableInterrupts(void* interrupt_pointer, int32_t *status);
+ void disableInterrupts(void* interrupt_pointer, int32_t *status);
+ double readRisingTimestamp(void* interrupt_pointer, int32_t *status);
+ double readFallingTimestamp(void* interrupt_pointer, int32_t *status);
+ void requestInterrupts(void* interrupt_pointer, uint8_t routing_module, uint32_t routing_pin,
+ bool routing_analog_trigger, int32_t *status);
+ void attachInterruptHandler(void* interrupt_pointer, InterruptHandlerFunction handler,
+ void* param, int32_t *status);
+ void setInterruptUpSourceEdge(void* interrupt_pointer, bool risingEdge, bool fallingEdge,
+ int32_t *status);
+}
diff --git a/aos/externals/allwpilib/hal/include/HAL/Notifier.hpp b/aos/externals/allwpilib/hal/include/HAL/Notifier.hpp
new file mode 100644
index 0000000..987686c
--- /dev/null
+++ b/aos/externals/allwpilib/hal/include/HAL/Notifier.hpp
@@ -0,0 +1,15 @@
+#pragma once
+
+#ifdef __vxworks
+#include <vxWorks.h>
+#else
+#include <stdint.h>
+#endif
+
+extern "C"
+{
+ void* initializeNotifier(void (*ProcessQueue)(uint32_t, void*), int32_t *status);
+ void cleanNotifier(void* notifier_pointer, int32_t *status);
+
+ void updateNotifierAlarm(void* notifier_pointer, uint32_t triggerTime, int32_t *status);
+}
diff --git a/aos/externals/allwpilib/hal/include/HAL/PDP.hpp b/aos/externals/allwpilib/hal/include/HAL/PDP.hpp
new file mode 100644
index 0000000..3dde22b
--- /dev/null
+++ b/aos/externals/allwpilib/hal/include/HAL/PDP.hpp
@@ -0,0 +1,19 @@
+#pragma once
+
+#ifdef __vxworks
+#include <vxWorks.h>
+#else
+#include <stdint.h>
+#endif
+
+extern "C"
+{
+ double getPDPTemperature(int32_t *status);
+ double getPDPVoltage(int32_t *status);
+ double getPDPChannelCurrent(uint8_t channel, int32_t *status);
+ double getPDPTotalCurrent(int32_t *status);
+ double getPDPTotalPower(int32_t *status);
+ double getPDPTotalEnergy(int32_t *status);
+ void resetPDPTotalEnergy(int32_t *status);
+ void clearPDPStickyFaults(int32_t *status);
+}
diff --git a/aos/externals/allwpilib/hal/include/HAL/Power.hpp b/aos/externals/allwpilib/hal/include/HAL/Power.hpp
new file mode 100644
index 0000000..b430c1e
--- /dev/null
+++ b/aos/externals/allwpilib/hal/include/HAL/Power.hpp
@@ -0,0 +1,21 @@
+#pragma once
+
+#include <stdint.h>
+
+extern "C"
+{
+ float getVinVoltage(int32_t *status);
+ float getVinCurrent(int32_t *status);
+ float getUserVoltage6V(int32_t *status);
+ float getUserCurrent6V(int32_t *status);
+ bool getUserActive6V(int32_t *status);
+ int getUserCurrentFaults6V(int32_t *status);
+ float getUserVoltage5V(int32_t *status);
+ float getUserCurrent5V(int32_t *status);
+ bool getUserActive5V(int32_t *status);
+ int getUserCurrentFaults5V(int32_t *status);
+ float getUserVoltage3V3(int32_t *status);
+ float getUserCurrent3V3(int32_t *status);
+ bool getUserActive3V3(int32_t *status);
+ int getUserCurrentFaults3V3(int32_t *status);
+}
diff --git a/aos/externals/allwpilib/hal/include/HAL/Semaphore.hpp b/aos/externals/allwpilib/hal/include/HAL/Semaphore.hpp
new file mode 100644
index 0000000..e02011f
--- /dev/null
+++ b/aos/externals/allwpilib/hal/include/HAL/Semaphore.hpp
@@ -0,0 +1,53 @@
+#pragma once
+
+#ifdef __vxworks
+#include <vxWorks.h>
+#else
+#include <stdint.h>
+#include <pthread.h>
+#include <semaphore.h>
+#endif
+
+#ifdef __vxworks
+typedef SEM_ID MUTEX_ID;
+typedef SEM_ID SEMAPHORE_ID;
+typedef SEM_ID MULTIWAIT_ID;
+#else
+class MutexInterface;
+typedef MutexInterface* MUTEX_ID;
+typedef sem_t* SEMAPHORE_ID;
+typedef pthread_cond_t* MULTIWAIT_ID;
+#endif
+
+extern "C"
+{
+ extern const uint32_t SEMAPHORE_Q_FIFO;
+ extern const uint32_t SEMAPHORE_Q_PRIORITY;
+ extern const uint32_t SEMAPHORE_DELETE_SAFE;
+ extern const uint32_t SEMAPHORE_INVERSION_SAFE;
+
+ extern const int32_t SEMAPHORE_NO_WAIT;
+ extern const int32_t SEMAPHORE_WAIT_FOREVER;
+
+ extern const uint32_t SEMAPHORE_EMPTY;
+ extern const uint32_t SEMAPHORE_FULL;
+
+ MUTEX_ID initializeMutexRecursive();
+ MUTEX_ID initializeMutexNormal();
+ void deleteMutex(MUTEX_ID sem);
+ int8_t lockMutex(MUTEX_ID sem);
+ int8_t tryLockMutex(MUTEX_ID sem);
+ int8_t unlockMutex(MUTEX_ID sem);
+
+ SEMAPHORE_ID initializeSemaphore(uint32_t initial_value);
+ void deleteSemaphore(SEMAPHORE_ID sem);
+ int8_t takeSemaphore(SEMAPHORE_ID sem);
+ int8_t tryTakeSemaphore(SEMAPHORE_ID sem);
+ int8_t giveSemaphore(SEMAPHORE_ID sem);
+
+ MULTIWAIT_ID initializeMultiWait();
+ void deleteMultiWait(MULTIWAIT_ID sem);
+ int8_t takeMultiWait(MULTIWAIT_ID sem, MUTEX_ID m, int32_t timeout);
+ int8_t giveMultiWait(MULTIWAIT_ID sem);
+}
+
diff --git a/aos/externals/allwpilib/hal/include/HAL/Solenoid.hpp b/aos/externals/allwpilib/hal/include/HAL/Solenoid.hpp
new file mode 100644
index 0000000..1654dc2
--- /dev/null
+++ b/aos/externals/allwpilib/hal/include/HAL/Solenoid.hpp
@@ -0,0 +1,22 @@
+#pragma once
+
+#ifdef __vxworks
+#include <vxWorks.h>
+#else
+#include <stdint.h>
+#endif
+
+extern "C"
+{
+ void* initializeSolenoidPort(void* port_pointer, int32_t *status);
+ bool checkSolenoidModule(uint8_t module);
+
+ bool getSolenoid(void* solenoid_port_pointer, int32_t *status);
+ void setSolenoid(void* solenoid_port_pointer, bool value, int32_t *status);
+ void setSolenoids(void* solenoid_port_pointer, uint8_t value, uint8_t mask, int32_t *status);
+
+ int getPCMSolenoidBlackList(void* solenoid_port_pointer, int32_t *status);
+ bool getPCMSolenoidVoltageStickyFault(void* solenoid_port_pointer, int32_t *status);
+ bool getPCMSolenoidVoltageFault(void* solenoid_port_pointer, int32_t *status);
+ void clearAllPCMStickyFaults_sol(void *solenoid_port_pointer, int32_t *status);
+}
diff --git a/aos/externals/allwpilib/hal/include/HAL/Task.hpp b/aos/externals/allwpilib/hal/include/HAL/Task.hpp
new file mode 100644
index 0000000..ec3ed9b
--- /dev/null
+++ b/aos/externals/allwpilib/hal/include/HAL/Task.hpp
@@ -0,0 +1,55 @@
+#pragma once
+
+#ifdef __vxworks
+#include <vxWorks.h>
+#else
+#include <stdint.h>
+#include <pthread.h>
+#endif
+
+#ifndef _FUNCPTR_DEFINED
+#define _FUNCPTR_DEFINED
+#ifdef __cplusplus
+typedef int (*FUNCPTR)(...);
+/* ptr to function returning int */
+#else
+typedef int (*FUNCPTR) (); /* ptr to function returning int */
+#endif /* __cplusplus */
+#endif /* _FUNCPTR_DEFINED */
+
+#ifndef _STATUS_DEFINED
+#define _STATUS_DEFINED
+typedef int STATUS;
+#endif /* _STATUS_DEFINED */
+
+#ifdef __vxworks
+#define NULL_TASK -1
+typedef int32_t TASK;
+#else
+#define NULL_TASK NULL
+typedef pthread_t* TASK;
+#endif
+
+extern "C"
+{
+ extern const uint32_t VXWORKS_FP_TASK;
+ extern const int32_t HAL_objLib_OBJ_ID_ERROR;
+ extern const int32_t HAL_objLib_OBJ_DELETED;
+ extern const int32_t HAL_taskLib_ILLEGAL_OPTIONS;
+ extern const int32_t HAL_memLib_NOT_ENOUGH_MEMORY;
+ extern const int32_t HAL_taskLib_ILLEGAL_PRIORITY;
+
+ TASK spawnTask(char * name, int priority, int options, int stackSize, FUNCPTR entryPt,
+ uint32_t arg0, uint32_t arg1, uint32_t arg2, uint32_t arg3, uint32_t arg4,
+ uint32_t arg5, uint32_t arg6, uint32_t arg7, uint32_t arg8, uint32_t arg9);
+ STATUS restartTask(TASK task);
+ STATUS deleteTask(TASK task);
+ STATUS isTaskReady(TASK task);
+ STATUS isTaskSuspended(TASK task);
+ STATUS suspendTask(TASK task);
+ STATUS resumeTask(TASK task);
+ STATUS verifyTaskID(TASK task);
+ STATUS setTaskPriority(TASK task, int priority);
+ STATUS getTaskPriority(TASK task, int* priority);
+}
+
diff --git a/aos/externals/allwpilib/hal/include/HAL/Utilities.hpp b/aos/externals/allwpilib/hal/include/HAL/Utilities.hpp
new file mode 100644
index 0000000..b24cd29
--- /dev/null
+++ b/aos/externals/allwpilib/hal/include/HAL/Utilities.hpp
@@ -0,0 +1,17 @@
+#pragma once
+
+#ifdef __vxworks
+#include <vxWorks.h>
+#else
+#include <stdint.h>
+#endif
+
+extern "C"
+{
+ extern const int32_t HAL_NO_WAIT;
+ extern const int32_t HAL_WAIT_FOREVER;
+
+ void delayTicks(int32_t ticks);
+ void delayMillis(double ms);
+ void delaySeconds(double s);
+}
diff --git a/aos/externals/allwpilib/hal/include/HAL/cpp/Resource.hpp b/aos/externals/allwpilib/hal/include/HAL/cpp/Resource.hpp
new file mode 100644
index 0000000..931407f
--- /dev/null
+++ b/aos/externals/allwpilib/hal/include/HAL/cpp/Resource.hpp
@@ -0,0 +1,45 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+#pragma once
+
+#include "../Errors.hpp"
+#include "Synchronized.hpp"
+#ifdef __vxworks
+#include <vxWorks.h>
+#else
+#include <stdint.h>
+#endif
+
+/**
+ * The Resource class is a convenient way to track allocated resources.
+ * It tracks them as indicies in the range [0 .. elements - 1].
+ * E.g. the library uses this to track hardware channel allocation.
+ *
+ * The Resource class does not allocate the hardware channels or other
+ * resources; it just tracks which indices were marked in use by
+ * Allocate and not yet freed by Free.
+ */
+class Resource
+{
+public:
+ virtual ~Resource();
+ static void CreateResourceObject(Resource **r, uint32_t elements);
+ uint32_t Allocate(const char *resourceDesc);
+ uint32_t Allocate(uint32_t index, const char *resourceDesc);
+ void Free(uint32_t index);
+
+private:
+ explicit Resource(uint32_t size);
+
+ bool *m_isAllocated;
+ ReentrantMutex m_allocateLock;
+ uint32_t m_size;
+
+ static ReentrantMutex m_createLock;
+
+ DISALLOW_COPY_AND_ASSIGN(Resource);
+};
+
diff --git a/aos/externals/allwpilib/hal/include/HAL/cpp/Synchronized.hpp b/aos/externals/allwpilib/hal/include/HAL/cpp/Synchronized.hpp
new file mode 100644
index 0000000..af8b149
--- /dev/null
+++ b/aos/externals/allwpilib/hal/include/HAL/cpp/Synchronized.hpp
@@ -0,0 +1,144 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+#pragma once
+
+#include <mutex>
+#include "HAL/Semaphore.hpp"
+
+// A macro to disallow the copy constructor and operator= functions
+// This should be used in the private: declarations for a class
+#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
+ TypeName(const TypeName&) = delete; \
+ void operator=(const TypeName&) = delete
+
+class Synchronized;
+
+/**
+ * Wrap a pthead mutex for easier use in C++. For a static
+ * instance, the constructor runs at program load time before main() can spawn
+ * any tasks. Use that to fix race conditions in setup code.
+ *
+ * This uses a pthread mutex which has PTHREAD_MUTEX_RECURSIVE set, making it
+ * "reentrant" in the sense that the owning task can lock it more than once. The
+ * task will need to unlock the mutex the same number of times to make it
+ * available for other threads to acquire.
+ *
+ * This class is safe to use in static variables because it does not depend on
+ * any other C++ static constructors or destructors.
+ */
+class ReentrantMutex {
+ public:
+ typedef pthread_mutex_t *native_handle_type;
+
+ constexpr ReentrantMutex() noexcept = default;
+ ReentrantMutex(const ReentrantMutex &) = delete;
+ ReentrantMutex &operator=(const ReentrantMutex &) = delete;
+
+ /**
+ * Lock the mutex, blocking until it's available.
+ */
+ void lock() { pthread_mutex_lock(&mutex_); }
+
+ /**
+ * Unlock the mutex.
+ */
+ void unlock() { pthread_mutex_unlock(&mutex_); }
+
+ /**
+ * Tries to lock the mutex.
+ */
+ bool try_lock() noexcept { return !pthread_mutex_trylock(&mutex_); }
+
+ pthread_mutex_t *native_handle() { return &mutex_; }
+
+ private:
+ // Do the equivalent of setting PTHREAD_PRIO_INHERIT and
+ // PTHREAD_MUTEX_RECURSIVE_NP.
+#if __WORDSIZE == 64
+ #error "Should work, but need to run test case to verify"
+ pthread_mutex_t mutex_ = {
+ {0, 0, 0, 0, 0x20 | PTHREAD_MUTEX_RECURSIVE_NP, 0, {0, 0}}};
+#else
+ pthread_mutex_t mutex_ = {
+ {0, 0, 0, 0x20 | PTHREAD_MUTEX_RECURSIVE_NP, 0, {0}}};
+#endif
+};
+
+/**
+ * Wrap a pthead mutex for easier use in C++. For a static
+ * instance, the constructor runs at program load time before main() can spawn
+ * any tasks. Use that to fix race conditions in setup code.
+ *
+ * This class is safe to use in static variables because it does not depend on
+ * any other C++ static constructors or destructors.
+ */
+class Mutex {
+ public:
+ typedef pthread_mutex_t *native_handle_type;
+
+ constexpr Mutex() noexcept = default;
+ Mutex(const Mutex &) = delete;
+ Mutex &operator=(const Mutex &) = delete;
+
+ /**
+ * Lock the mutex, blocking until it's available.
+ */
+ void lock() { pthread_mutex_lock(&mutex_); }
+
+ /**
+ * Unlock the mutex.
+ */
+ void unlock() { pthread_mutex_unlock(&mutex_); }
+
+ /**
+ * Tries to lock the mutex.
+ */
+ bool try_lock() noexcept { return !pthread_mutex_trylock(&mutex_); }
+
+ native_handle_type native_handle() { return &mutex_; }
+
+ private:
+ // Do the equivalent of setting PTHREAD_PRIO_INHERIT.
+#if __WORDSIZE == 64
+ #error "Should work, but need to run test case to verify"
+ pthread_mutex_t mutex_ = { { 0, 0, 0, 0, 0x20, 0, { 0, 0 } } };
+#else
+ pthread_mutex_t mutex_ = { { 0, 0, 0, 0x20, 0, { 0 } } };
+#endif
+};
+
+
+/**
+ * Provide easy support for critical regions.
+ *
+ * A critical region is an area of code that is always executed under mutual exclusion. Only
+ * one task can be executing this code at any time. The idea is that code that manipulates data
+ * that is shared between two or more tasks has to be prevented from executing at the same time
+ * otherwise a race condition is possible when both tasks try to update the data. Typically
+ * semaphores are used to ensure only single task access to the data.
+ *
+ * Synchronized objects are a simple wrapper around semaphores to help ensure
+ * that semaphores are always unlocked (semGive) after locking (semTake).
+ *
+ * You allocate a Synchronized as a local variable, *not* on the heap. That
+ * makes it a "stack object" whose destructor runs automatically when it goes
+ * out of scope. E.g.
+ *
+ * { Synchronized _sync(aReentrantSemaphore); ... critical region ... }
+ */
+class Synchronized
+{
+public:
+#ifndef __vxworks
+ explicit Synchronized(SEMAPHORE_ID);
+#endif
+ virtual ~Synchronized();
+private:
+ SEMAPHORE_ID m_semaphore;
+
+ DISALLOW_COPY_AND_ASSIGN(Synchronized);
+};
+
diff --git a/aos/externals/allwpilib/hal/include/Log.hpp b/aos/externals/allwpilib/hal/include/Log.hpp
new file mode 100644
index 0000000..eb221e7
--- /dev/null
+++ b/aos/externals/allwpilib/hal/include/Log.hpp
@@ -0,0 +1,103 @@
+#pragma once
+
+#include <sstream>
+#include <iomanip>
+#include <string>
+#include <stdio.h>
+#include <sys/time.h>
+
+inline std::string NowTime();
+
+enum TLogLevel {logNONE, logERROR, logWARNING, logINFO, logDEBUG, logDEBUG1, logDEBUG2, logDEBUG3, logDEBUG4};
+
+class Log
+{
+public:
+ Log();
+ virtual ~Log();
+ std::ostringstream& Get(TLogLevel level = logINFO);
+public:
+ static TLogLevel& ReportingLevel();
+ static std::string ToString(TLogLevel level);
+ static TLogLevel FromString(const std::string& level);
+protected:
+ std::ostringstream os;
+private:
+ Log(const Log&);
+ Log& operator =(const Log&);
+};
+
+inline Log::Log()
+{
+}
+
+inline std::ostringstream& Log::Get(TLogLevel level)
+{
+ os << "- " << NowTime();
+ os << " " << ToString(level) << ": ";
+ os << std::string(level > logDEBUG ? level - logDEBUG : 0, '\t');
+ return os;
+}
+
+inline Log::~Log()
+{
+ os << std::endl;
+ fprintf(stderr, "%s", os.str().c_str());
+ fflush(stderr);
+}
+
+inline TLogLevel& Log::ReportingLevel()
+{
+ static TLogLevel reportingLevel = logDEBUG4;
+ return reportingLevel;
+}
+
+inline std::string Log::ToString(TLogLevel level)
+{
+ static const char* const buffer[] = {"NONE", "ERROR", "WARNING", "INFO", "DEBUG", "DEBUG1", "DEBUG2", "DEBUG3", "DEBUG4"};
+ return buffer[level];
+}
+
+inline TLogLevel Log::FromString(const std::string& level)
+{
+ if (level == "DEBUG4")
+ return logDEBUG4;
+ if (level == "DEBUG3")
+ return logDEBUG3;
+ if (level == "DEBUG2")
+ return logDEBUG2;
+ if (level == "DEBUG1")
+ return logDEBUG1;
+ if (level == "DEBUG")
+ return logDEBUG;
+ if (level == "INFO")
+ return logINFO;
+ if (level == "WARNING")
+ return logWARNING;
+ if (level == "ERROR")
+ return logERROR;
+ if (level == "NONE")
+ return logNONE;
+ Log().Get(logWARNING) << "Unknown logging level '" << level << "'. Using INFO level as default.";
+ return logINFO;
+}
+
+typedef Log FILELog;
+
+#define FILE_LOG(level) \
+ if (level > FILELog::ReportingLevel()) ; \
+ else Log().Get(level)
+
+inline std::string NowTime()
+{
+ char buffer[11];
+ time_t t;
+ time(&t);
+ tm * r = gmtime(&t);
+ strftime(buffer, sizeof(buffer), "%H:%M:%S", r);
+ struct timeval tv;
+ gettimeofday(&tv, 0);
+ char result[100] = {0};
+ sprintf(result, "%s.%03ld", buffer, (long)tv.tv_usec / 1000);
+ return result;
+}
diff --git a/aos/externals/allwpilib/hal/lib/Athena/Accelerometer.cpp b/aos/externals/allwpilib/hal/lib/Athena/Accelerometer.cpp
new file mode 100644
index 0000000..0895c9c
--- /dev/null
+++ b/aos/externals/allwpilib/hal/lib/Athena/Accelerometer.cpp
@@ -0,0 +1,234 @@
+#include "HAL/Accelerometer.hpp"
+
+#include "ChipObject.h"
+#include <stdint.h>
+#include <stdio.h>
+#include <assert.h>
+
+// The 7-bit I2C address with a 0 "send" bit
+static const uint8_t kSendAddress = (0x1c << 1) | 0;
+
+// The 7-bit I2C address with a 1 "receive" bit
+static const uint8_t kReceiveAddress = (0x1c << 1) | 1;
+
+static const uint8_t kControlTxRx = 1;
+static const uint8_t kControlStart = 2;
+static const uint8_t kControlStop = 4;
+
+static tAccel *accel = 0;
+static AccelerometerRange accelerometerRange;
+
+// Register addresses
+enum Register {
+ kReg_Status = 0x00,
+ kReg_OutXMSB = 0x01,
+ kReg_OutXLSB = 0x02,
+ kReg_OutYMSB = 0x03,
+ kReg_OutYLSB = 0x04,
+ kReg_OutZMSB = 0x05,
+ kReg_OutZLSB = 0x06,
+ kReg_Sysmod = 0x0B,
+ kReg_IntSource = 0x0C,
+ kReg_WhoAmI = 0x0D,
+ kReg_XYZDataCfg = 0x0E,
+ kReg_HPFilterCutoff = 0x0F,
+ kReg_PLStatus = 0x10,
+ kReg_PLCfg = 0x11,
+ kReg_PLCount = 0x12,
+ kReg_PLBfZcomp = 0x13,
+ kReg_PLThsReg = 0x14,
+ kReg_FFMtCfg = 0x15,
+ kReg_FFMtSrc = 0x16,
+ kReg_FFMtThs = 0x17,
+ kReg_FFMtCount = 0x18,
+ kReg_TransientCfg = 0x1D,
+ kReg_TransientSrc = 0x1E,
+ kReg_TransientThs = 0x1F,
+ kReg_TransientCount = 0x20,
+ kReg_PulseCfg = 0x21,
+ kReg_PulseSrc = 0x22,
+ kReg_PulseThsx = 0x23,
+ kReg_PulseThsy = 0x24,
+ kReg_PulseThsz = 0x25,
+ kReg_PulseTmlt = 0x26,
+ kReg_PulseLtcy = 0x27,
+ kReg_PulseWind = 0x28,
+ kReg_ASlpCount = 0x29,
+ kReg_CtrlReg1 = 0x2A,
+ kReg_CtrlReg2 = 0x2B,
+ kReg_CtrlReg3 = 0x2C,
+ kReg_CtrlReg4 = 0x2D,
+ kReg_CtrlReg5 = 0x2E,
+ kReg_OffX = 0x2F,
+ kReg_OffY = 0x30,
+ kReg_OffZ = 0x31
+};
+
+extern "C" uint32_t getFPGATime(int32_t *status);
+
+static void writeRegister(Register reg, uint8_t data);
+static uint8_t readRegister(Register reg);
+
+/**
+ * Initialize the accelerometer.
+ */
+static void initializeAccelerometer() {
+ int32_t status;
+
+ if(!accel) {
+ accel = tAccel::create(&status);
+
+ // Enable I2C
+ accel->writeCNFG(1, &status);
+
+ // Set the counter to 100 kbps
+ accel->writeCNTR(213, &status);
+
+ // The device identification number should be 0x2a
+ assert(readRegister(kReg_WhoAmI) == 0x2a);
+ }
+}
+
+static void writeRegister(Register reg, uint8_t data) {
+ int32_t status = 0;
+ uint32_t initialTime;
+
+ accel->writeADDR(kSendAddress, &status);
+
+ // Send a start transmit/receive message with the register address
+ accel->writeCNTL(kControlStart | kControlTxRx, &status);
+ accel->writeDATO(reg, &status);
+ accel->strobeGO(&status);
+
+ // Execute and wait until it's done (up to a millisecond)
+ initialTime = getFPGATime(&status);
+ while(accel->readSTAT(&status) & 1) {
+ if(getFPGATime(&status) > initialTime + 1000) break;
+ }
+
+ // Send a stop transmit/receive message with the data
+ accel->writeCNTL(kControlStop | kControlTxRx, &status);
+ accel->writeDATO(data, &status);
+ accel->strobeGO(&status);
+
+ // Execute and wait until it's done (up to a millisecond)
+ initialTime = getFPGATime(&status);
+ while(accel->readSTAT(&status) & 1) {
+ if(getFPGATime(&status) > initialTime + 1000) break;
+ }
+
+ fflush(stdout);
+}
+
+static uint8_t readRegister(Register reg) {
+ int32_t status = 0;
+ uint32_t initialTime;
+
+ // Send a start transmit/receive message with the register address
+ accel->writeADDR(kSendAddress, &status);
+ accel->writeCNTL(kControlStart | kControlTxRx, &status);
+ accel->writeDATO(reg, &status);
+ accel->strobeGO(&status);
+
+ // Execute and wait until it's done (up to a millisecond)
+ initialTime = getFPGATime(&status);
+ while(accel->readSTAT(&status) & 1) {
+ if(getFPGATime(&status) > initialTime + 1000) break;
+ }
+
+ // Receive a message with the data and stop
+ accel->writeADDR(kReceiveAddress, &status);
+ accel->writeCNTL(kControlStart | kControlStop | kControlTxRx, &status);
+ accel->strobeGO(&status);
+
+ // Execute and wait until it's done (up to a millisecond)
+ initialTime = getFPGATime(&status);
+ while(accel->readSTAT(&status) & 1) {
+ if(getFPGATime(&status) > initialTime + 1000) break;
+ }
+
+ fflush(stdout);
+
+ return accel->readDATI(&status);
+}
+
+/**
+ * Convert a 12-bit raw acceleration value into a scaled double in units of
+ * 1 g-force, taking into account the accelerometer range.
+ */
+static double unpackAxis(int16_t raw) {
+ // The raw value is actually 12 bits, not 16, so we need to propogate the
+ // 2's complement sign bit to the unused 4 bits for this to work with
+ // negative numbers.
+ raw <<= 4;
+ raw >>= 4;
+
+ switch(accelerometerRange) {
+ case kRange_2G: return raw / 1024.0;
+ case kRange_4G: return raw / 512.0;
+ case kRange_8G: return raw / 256.0;
+ default: return 0.0;
+ }
+}
+
+/**
+ * Set the accelerometer to active or standby mode. It must be in standby
+ * mode to change any configuration.
+ */
+void setAccelerometerActive(bool active) {
+ initializeAccelerometer();
+
+ uint8_t ctrlReg1 = readRegister(kReg_CtrlReg1);
+ ctrlReg1 &= ~1; // Clear the existing active bit
+ writeRegister(kReg_CtrlReg1, ctrlReg1 | (active? 1 : 0));
+}
+
+/**
+ * Set the range of values that can be measured (either 2, 4, or 8 g-forces).
+ * The accelerometer should be in standby mode when this is called.
+ */
+void setAccelerometerRange(AccelerometerRange range) {
+ initializeAccelerometer();
+
+ accelerometerRange = range;
+
+ uint8_t xyzDataCfg = readRegister(kReg_XYZDataCfg);
+ xyzDataCfg &= ~3; // Clear the existing two range bits
+ writeRegister(kReg_XYZDataCfg, xyzDataCfg | range);
+}
+
+/**
+ * Get the x-axis acceleration
+ *
+ * This is a floating point value in units of 1 g-force
+ */
+double getAccelerometerX() {
+ initializeAccelerometer();
+
+ int raw = (readRegister(kReg_OutXMSB) << 4) | (readRegister(kReg_OutXLSB) >> 4);
+ return unpackAxis(raw);
+}
+
+/**
+ * Get the y-axis acceleration
+ *
+ * This is a floating point value in units of 1 g-force
+ */
+double getAccelerometerY() {
+ initializeAccelerometer();
+
+ int raw = (readRegister(kReg_OutYMSB) << 4) | (readRegister(kReg_OutYLSB) >> 4);
+ return unpackAxis(raw);
+}
+
+/**
+ * Get the z-axis acceleration
+ *
+ * This is a floating point value in units of 1 g-force
+ */
+double getAccelerometerZ() {
+ initializeAccelerometer();
+
+ int raw = (readRegister(kReg_OutZMSB) << 4) | (readRegister(kReg_OutZLSB) >> 4);
+ return unpackAxis(raw);
+}
diff --git a/aos/externals/allwpilib/hal/lib/Athena/Analog.cpp b/aos/externals/allwpilib/hal/lib/Athena/Analog.cpp
new file mode 100644
index 0000000..bba7043
--- /dev/null
+++ b/aos/externals/allwpilib/hal/lib/Athena/Analog.cpp
@@ -0,0 +1,725 @@
+
+#include "HAL/Analog.hpp"
+
+#include "Port.h"
+#include "HAL/HAL.hpp"
+#include "ChipObject.h"
+#include "HAL/cpp/Synchronized.hpp"
+#include "HAL/cpp/Resource.hpp"
+#include "NetworkCommunication/AICalibration.h"
+#include "NetworkCommunication/LoadOut.h"
+
+static const long kTimebase = 40000000; ///< 40 MHz clock
+static const long kDefaultOversampleBits = 0;
+static const long kDefaultAverageBits = 7;
+static const float kDefaultSampleRate = 50000.0;
+static const uint32_t kAnalogInputPins = 8;
+static const uint32_t kAnalogOutputPins = 2;
+
+static const uint32_t kAccumulatorNumChannels = 2;
+static const uint32_t kAccumulatorChannels[] = {0, 1};
+
+struct AnalogPort {
+ Port port;
+ tAccumulator *accumulator;
+};
+
+bool analogSampleRateSet = false;
+ReentrantMutex analogRegisterWindowMutex;
+tAI* analogInputSystem = NULL;
+tAO* analogOutputSystem = NULL;
+uint32_t analogNumChannelsToActivate = 0;
+
+// Utility methods defined below.
+uint32_t getAnalogNumActiveChannels(int32_t *status);
+uint32_t getAnalogNumChannelsToActivate(int32_t *status);
+void setAnalogNumChannelsToActivate(uint32_t channels);
+
+bool analogSystemInitialized = false;
+
+/**
+ * Initialize the analog System.
+ */
+void initializeAnalog(int32_t *status) {
+ ::std::unique_lock<ReentrantMutex> sync(analogRegisterWindowMutex);
+ if (analogSystemInitialized) return;
+ analogInputSystem = tAI::create(status);
+ analogOutputSystem = tAO::create(status);
+ setAnalogNumChannelsToActivate(kAnalogInputPins);
+ setAnalogSampleRate(kDefaultSampleRate, status);
+ analogSystemInitialized = true;
+}
+
+/**
+ * Initialize the analog input port using the given port object.
+ */
+void* initializeAnalogInputPort(void* port_pointer, int32_t *status) {
+ initializeAnalog(status);
+ Port* port = (Port*) port_pointer;
+
+ // Initialize port structure
+ AnalogPort* analog_port = new AnalogPort();
+ analog_port->port = *port;
+ if (isAccumulatorChannel(analog_port, status)) {
+ analog_port->accumulator = tAccumulator::create(port->pin, status);
+ } else analog_port->accumulator = NULL;
+
+ // Set default configuration
+ analogInputSystem->writeScanList(port->pin, port->pin, status);
+ setAnalogAverageBits(analog_port, kDefaultAverageBits, status);
+ setAnalogOversampleBits(analog_port, kDefaultOversampleBits, status);
+ return analog_port;
+}
+
+/**
+ * Initialize the analog output port using the given port object.
+ */
+void* initializeAnalogOutputPort(void* port_pointer, int32_t *status) {
+ initializeAnalog(status);
+ Port* port = (Port*) port_pointer;
+
+ // Initialize port structure
+ AnalogPort* analog_port = new AnalogPort();
+ analog_port->port = *port;
+ return analog_port;
+}
+
+
+/**
+ * Check that the analog module number is valid.
+ *
+ * @return Analog module is valid and present
+ */
+bool checkAnalogModule(uint8_t module) {
+ return module == 1;
+}
+
+/**
+ * Check that the analog output channel number is value.
+ * Verify that the analog channel number is one of the legal channel numbers. Channel numbers
+ * are 0-based.
+ *
+ * @return Analog channel is valid
+ */
+bool checkAnalogInputChannel(uint32_t pin) {
+ if (pin < kAnalogInputPins)
+ return true;
+ return false;
+}
+
+/**
+ * Check that the analog output channel number is value.
+ * Verify that the analog channel number is one of the legal channel numbers. Channel numbers
+ * are 0-based.
+ *
+ * @return Analog channel is valid
+ */
+bool checkAnalogOutputChannel(uint32_t pin) {
+ if (pin < kAnalogOutputPins)
+ return true;
+ return false;
+}
+
+void setAnalogOutput(void* analog_port_pointer, double voltage, int32_t *status) {
+ AnalogPort* port = (AnalogPort*) analog_port_pointer;
+
+ uint16_t rawValue = (uint16_t)(voltage / 5.0 * 0x1000);
+
+ if(voltage < 0.0) rawValue = 0;
+ else if(voltage > 5.0) rawValue = 0x1000;
+
+ analogOutputSystem->writeMXP(port->port.pin, rawValue, status);
+}
+
+double getAnalogOutput(void* analog_port_pointer, int32_t *status) {
+ AnalogPort* port = (AnalogPort*) analog_port_pointer;
+
+ uint16_t rawValue = analogOutputSystem->readMXP(port->port.pin, status);
+
+ return rawValue * 5.0 / 0x1000;
+}
+
+/**
+ * Set the sample rate.
+ *
+ * This is a global setting for the Athena and effects all channels.
+ *
+ * @param samplesPerSecond The number of samples per channel per second.
+ */
+void setAnalogSampleRate(double samplesPerSecond, int32_t *status) {
+ // TODO: This will change when variable size scan lists are implemented.
+ // TODO: Need float comparison with epsilon.
+ //wpi_assert(!sampleRateSet || GetSampleRate() == samplesPerSecond);
+ analogSampleRateSet = true;
+
+ // Compute the convert rate
+ uint32_t ticksPerSample = (uint32_t)((float)kTimebase / samplesPerSecond);
+ uint32_t ticksPerConversion = ticksPerSample / getAnalogNumChannelsToActivate(status);
+ // ticksPerConversion must be at least 80
+ if (ticksPerConversion < 80) {
+ if ((*status) >= 0) *status = SAMPLE_RATE_TOO_HIGH;
+ ticksPerConversion = 80;
+ }
+
+ // Atomically set the scan size and the convert rate so that the sample rate is constant
+ tAI::tConfig config;
+ config.ScanSize = getAnalogNumChannelsToActivate(status);
+ config.ConvertRate = ticksPerConversion;
+ analogInputSystem->writeConfig(config, status);
+
+ // Indicate that the scan size has been commited to hardware.
+ setAnalogNumChannelsToActivate(0);
+}
+
+/**
+ * Get the current sample rate.
+ *
+ * This assumes one entry in the scan list.
+ * This is a global setting for the Athena and effects all channels.
+ *
+ * @return Sample rate.
+ */
+float getAnalogSampleRate(int32_t *status) {
+ uint32_t ticksPerConversion = analogInputSystem->readLoopTiming(status);
+ uint32_t ticksPerSample = ticksPerConversion * getAnalogNumActiveChannels(status);
+ return (float)kTimebase / (float)ticksPerSample;
+}
+
+/**
+ * Set the number of averaging bits.
+ *
+ * This sets the number of averaging bits. The actual number of averaged samples is 2**bits.
+ * Use averaging to improve the stability of your measurement at the expense of sampling rate.
+ * The averaging is done automatically in the FPGA.
+ *
+ * @param analog_port_pointer Pointer to the analog port to configure.
+ * @param bits Number of bits to average.
+ */
+void setAnalogAverageBits(void* analog_port_pointer, uint32_t bits, int32_t *status) {
+ AnalogPort* port = (AnalogPort*) analog_port_pointer;
+ analogInputSystem->writeAverageBits(port->port.pin, bits, status);
+}
+
+/**
+ * Get the number of averaging bits.
+ *
+ * This gets the number of averaging bits from the FPGA. The actual number of averaged samples is 2**bits.
+ * The averaging is done automatically in the FPGA.
+ *
+ * @param analog_port_pointer Pointer to the analog port to use.
+ * @return Bits to average.
+ */
+uint32_t getAnalogAverageBits(void* analog_port_pointer, int32_t *status) {
+ AnalogPort* port = (AnalogPort*) analog_port_pointer;
+ uint32_t result = analogInputSystem->readAverageBits(port->port.pin, status);
+ return result;
+}
+
+/**
+ * Set the number of oversample bits.
+ *
+ * This sets the number of oversample bits. The actual number of oversampled values is 2**bits.
+ * Use oversampling to improve the resolution of your measurements at the expense of sampling rate.
+ * The oversampling is done automatically in the FPGA.
+ *
+ * @param analog_port_pointer Pointer to the analog port to use.
+ * @param bits Number of bits to oversample.
+ */
+void setAnalogOversampleBits(void* analog_port_pointer, uint32_t bits, int32_t *status) {
+ AnalogPort* port = (AnalogPort*) analog_port_pointer;
+ analogInputSystem->writeOversampleBits(port->port.pin, bits, status);
+}
+
+
+/**
+ * Get the number of oversample bits.
+ *
+ * This gets the number of oversample bits from the FPGA. The actual number of oversampled values is
+ * 2**bits. The oversampling is done automatically in the FPGA.
+ *
+ * @param analog_port_pointer Pointer to the analog port to use.
+ * @return Bits to oversample.
+ */
+uint32_t getAnalogOversampleBits(void* analog_port_pointer, int32_t *status) {
+ AnalogPort* port = (AnalogPort*) analog_port_pointer;
+ uint32_t result = analogInputSystem->readOversampleBits(port->port.pin, status);
+ return result;
+}
+
+/**
+ * Get a sample straight from the channel on this module.
+ *
+ * The sample is a 12-bit value representing the 0V to 5V range of the A/D converter in the module.
+ * The units are in A/D converter codes. Use GetVoltage() to get the analog value in calibrated units.
+ *
+ * @param analog_port_pointer Pointer to the analog port to use.
+ * @return A sample straight from the channel on this module.
+ */
+int16_t getAnalogValue(void* analog_port_pointer, int32_t *status) {
+ AnalogPort* port = (AnalogPort*) analog_port_pointer;
+ int16_t value;
+ checkAnalogInputChannel(port->port.pin);
+
+ tAI::tReadSelect readSelect;
+ readSelect.Channel = port->port.pin;
+ readSelect.Averaged = false;
+
+ {
+ ::std::unique_lock<ReentrantMutex> sync(analogRegisterWindowMutex);
+ analogInputSystem->writeReadSelect(readSelect, status);
+ analogInputSystem->strobeLatchOutput(status);
+ value = (int16_t) analogInputSystem->readOutput(status);
+ }
+
+ return value;
+}
+
+/**
+ * Get a sample from the output of the oversample and average engine for the channel.
+ *
+ * The sample is 12-bit + the value configured in SetOversampleBits().
+ * The value configured in SetAverageBits() will cause this value to be averaged 2**bits number of samples.
+ * This is not a sliding window. The sample will not change until 2**(OversamplBits + AverageBits) samples
+ * have been acquired from the module on this channel.
+ * Use GetAverageVoltage() to get the analog value in calibrated units.
+ *
+ * @param analog_port_pointer Pointer to the analog port to use.
+ * @return A sample from the oversample and average engine for the channel.
+ */
+int32_t getAnalogAverageValue(void* analog_port_pointer, int32_t *status) {
+ AnalogPort* port = (AnalogPort*) analog_port_pointer;
+ int32_t value;
+ checkAnalogInputChannel(port->port.pin);
+
+ tAI::tReadSelect readSelect;
+ readSelect.Channel = port->port.pin;
+ readSelect.Averaged = true;
+
+ {
+ ::std::unique_lock<ReentrantMutex> sync(analogRegisterWindowMutex);
+ analogInputSystem->writeReadSelect(readSelect, status);
+ analogInputSystem->strobeLatchOutput(status);
+ value = (int32_t) analogInputSystem->readOutput(status);
+ }
+
+ return value;
+}
+
+/**
+ * Get a scaled sample straight from the channel on this module.
+ *
+ * The value is scaled to units of Volts using the calibrated scaling data from GetLSBWeight() and GetOffset().
+ *
+ * @param analog_port_pointer Pointer to the analog port to use.
+ * @return A scaled sample straight from the channel on this module.
+ */
+float getAnalogVoltage(void* analog_port_pointer, int32_t *status) {
+ int16_t value = getAnalogValue(analog_port_pointer, status);
+ uint32_t LSBWeight = getAnalogLSBWeight(analog_port_pointer, status);
+ int32_t offset = getAnalogOffset(analog_port_pointer, status);
+ float voltage = LSBWeight * 1.0e-9 * value - offset * 1.0e-9;
+ return voltage;
+}
+
+/**
+ * Get a scaled sample from the output of the oversample and average engine for the channel.
+ *
+ * The value is scaled to units of Volts using the calibrated scaling data from GetLSBWeight() and GetOffset().
+ * Using oversampling will cause this value to be higher resolution, but it will update more slowly.
+ * Using averaging will cause this value to be more stable, but it will update more slowly.
+ *
+ * @param analog_port_pointer Pointer to the analog port to use.
+ * @return A scaled sample from the output of the oversample and average engine for the channel.
+ */
+float getAnalogAverageVoltage(void* analog_port_pointer, int32_t *status) {
+ int32_t value = getAnalogAverageValue(analog_port_pointer, status);
+ uint32_t LSBWeight = getAnalogLSBWeight(analog_port_pointer, status);
+ int32_t offset = getAnalogOffset(analog_port_pointer, status);
+ uint32_t oversampleBits = getAnalogOversampleBits(analog_port_pointer, status);
+ float voltage = ((LSBWeight * 1.0e-9 * value) / (float)(1 << oversampleBits)) - offset * 1.0e-9;
+ return voltage;
+}
+
+/**
+ * Convert a voltage to a raw value for a specified channel.
+ *
+ * This process depends on the calibration of each channel, so the channel
+ * must be specified.
+ *
+ * @todo This assumes raw values. Oversampling not supported as is.
+ *
+ * @param analog_port_pointer Pointer to the analog port to use.
+ * @param voltage The voltage to convert.
+ * @return The raw value for the channel.
+ */
+int32_t getAnalogVoltsToValue(void* analog_port_pointer, double voltage, int32_t *status) {
+ if (voltage > 5.0) {
+ voltage = 5.0;
+ *status = VOLTAGE_OUT_OF_RANGE;
+ }
+ if (voltage < 0.0) {
+ voltage = 0.0;
+ *status = VOLTAGE_OUT_OF_RANGE;
+ }
+ uint32_t LSBWeight = getAnalogLSBWeight(analog_port_pointer, status);
+ int32_t offset = getAnalogOffset(analog_port_pointer, status);
+ int32_t value = (int32_t) ((voltage + offset * 1.0e-9) / (LSBWeight * 1.0e-9));
+ return value;
+}
+
+/**
+ * Get the factory scaling least significant bit weight constant.
+ * The least significant bit weight constant for the channel that was calibrated in
+ * manufacturing and stored in an eeprom in the module.
+ *
+ * Volts = ((LSB_Weight * 1e-9) * raw) - (Offset * 1e-9)
+ *
+ * @param analog_port_pointer Pointer to the analog port to use.
+ * @return Least significant bit weight.
+ */
+uint32_t getAnalogLSBWeight(void* analog_port_pointer, int32_t *status) {
+ AnalogPort* port = (AnalogPort*) analog_port_pointer;
+ uint32_t lsbWeight = FRC_NetworkCommunication_nAICalibration_getLSBWeight(0, port->port.pin, status); // XXX: aiSystemIndex == 0?
+ return lsbWeight;
+}
+
+/**
+ * Get the factory scaling offset constant.
+ * The offset constant for the channel that was calibrated in manufacturing and stored
+ * in an eeprom in the module.
+ *
+ * Volts = ((LSB_Weight * 1e-9) * raw) - (Offset * 1e-9)
+ *
+ * @param analog_port_pointer Pointer to the analog port to use.
+ * @return Offset constant.
+ */
+int32_t getAnalogOffset(void* analog_port_pointer, int32_t *status) {
+ AnalogPort* port = (AnalogPort*) analog_port_pointer;
+ int32_t offset = FRC_NetworkCommunication_nAICalibration_getOffset(0, port->port.pin, status); // XXX: aiSystemIndex == 0?
+ return offset;
+}
+
+
+/**
+ * Return the number of channels on the module in use.
+ *
+ * @return Active channels.
+ */
+uint32_t getAnalogNumActiveChannels(int32_t *status) {
+ uint32_t scanSize = analogInputSystem->readConfig_ScanSize(status);
+ if (scanSize == 0)
+ return 8;
+ return scanSize;
+}
+
+/**
+ * Get the number of active channels.
+ *
+ * This is an internal function to allow the atomic update of both the
+ * number of active channels and the sample rate.
+ *
+ * When the number of channels changes, use the new value. Otherwise,
+ * return the curent value.
+ *
+ * @return Value to write to the active channels field.
+ */
+uint32_t getAnalogNumChannelsToActivate(int32_t *status) {
+ if(analogNumChannelsToActivate == 0) return getAnalogNumActiveChannels(status);
+ return analogNumChannelsToActivate;
+}
+
+/**
+ * Set the number of active channels.
+ *
+ * Store the number of active channels to set. Don't actually commit to hardware
+ * until SetSampleRate().
+ *
+ * @param channels Number of active channels.
+ */
+void setAnalogNumChannelsToActivate(uint32_t channels) {
+ analogNumChannelsToActivate = channels;
+}
+
+//// Accumulator Stuff
+
+/**
+ * Is the channel attached to an accumulator.
+ *
+ * @return The analog channel is attached to an accumulator.
+ */
+bool isAccumulatorChannel(void* analog_port_pointer, int32_t *status) {
+ AnalogPort* port = (AnalogPort*) analog_port_pointer;
+ for (uint32_t i=0; i < kAccumulatorNumChannels; i++) {
+ if (port->port.pin == kAccumulatorChannels[i]) return true;
+ }
+ return false;
+}
+
+/**
+ * Initialize the accumulator.
+ */
+void initAccumulator(void* analog_port_pointer, int32_t *status) {
+ setAccumulatorCenter(analog_port_pointer, 0, status);
+ resetAccumulator(analog_port_pointer, status);
+}
+
+/**
+ * Resets the accumulator to the initial value.
+ */
+void resetAccumulator(void* analog_port_pointer, int32_t *status) {
+ AnalogPort* port = (AnalogPort*) analog_port_pointer;
+ if (port->accumulator == NULL) {
+ *status = NULL_PARAMETER;
+ return;
+ }
+ port->accumulator->strobeReset(status);
+}
+
+/**
+ * Set the center value of the accumulator.
+ *
+ * The center value is subtracted from each A/D value before it is added to the accumulator. This
+ * is used for the center value of devices like gyros and accelerometers to make integration work
+ * and to take the device offset into account when integrating.
+ *
+ * This center value is based on the output of the oversampled and averaged source from channel 1.
+ * Because of this, any non-zero oversample bits will affect the size of the value for this field.
+ */
+void setAccumulatorCenter(void* analog_port_pointer, int32_t center, int32_t *status) {
+ AnalogPort* port = (AnalogPort*) analog_port_pointer;
+ if (port->accumulator == NULL) {
+ *status = NULL_PARAMETER;
+ return;
+ }
+ port->accumulator->writeCenter(center, status);
+}
+
+/**
+ * Set the accumulator's deadband.
+ */
+void setAccumulatorDeadband(void* analog_port_pointer, int32_t deadband, int32_t *status) {
+ AnalogPort* port = (AnalogPort*) analog_port_pointer;
+ if (port->accumulator == NULL) {
+ *status = NULL_PARAMETER;
+ return;
+ }
+ port->accumulator->writeDeadband(deadband, status);
+}
+
+/**
+ * Read the accumulated value.
+ *
+ * Read the value that has been accumulating on channel 1.
+ * The accumulator is attached after the oversample and average engine.
+ *
+ * @return The 64-bit value accumulated since the last Reset().
+ */
+int64_t getAccumulatorValue(void* analog_port_pointer, int32_t *status) {
+ AnalogPort* port = (AnalogPort*) analog_port_pointer;
+ if (port->accumulator == NULL) {
+ *status = NULL_PARAMETER;
+ return 0;
+ }
+ int64_t value = port->accumulator->readOutput_Value(status);
+ return value;
+}
+
+/**
+ * Read the number of accumulated values.
+ *
+ * Read the count of the accumulated values since the accumulator was last Reset().
+ *
+ * @return The number of times samples from the channel were accumulated.
+ */
+uint32_t getAccumulatorCount(void* analog_port_pointer, int32_t *status) {
+ AnalogPort* port = (AnalogPort*) analog_port_pointer;
+ if (port->accumulator == NULL) {
+ *status = NULL_PARAMETER;
+ return 0;
+ }
+ return port->accumulator->readOutput_Count(status);
+}
+
+/**
+ * Read the accumulated value and the number of accumulated values atomically.
+ *
+ * This function reads the value and count from the FPGA atomically.
+ * This can be used for averaging.
+ *
+ * @param value Pointer to the 64-bit accumulated output.
+ * @param count Pointer to the number of accumulation cycles.
+ */
+void getAccumulatorOutput(void* analog_port_pointer, int64_t *value, uint32_t *count, int32_t *status) {
+ AnalogPort* port = (AnalogPort*) analog_port_pointer;
+ if (port->accumulator == NULL) {
+ *status = NULL_PARAMETER;
+ return;
+ }
+ if (value == NULL || count == NULL) {
+ *status = NULL_PARAMETER;
+ return;
+ }
+
+ tAccumulator::tOutput output = port->accumulator->readOutput(status);
+
+ *value = output.Value;
+ *count = output.Count;
+}
+
+
+struct trigger_t {
+ tAnalogTrigger* trigger;
+ AnalogPort* port;
+ uint32_t index;
+};
+typedef struct trigger_t AnalogTrigger;
+
+static Resource *triggers = NULL;
+
+void* initializeAnalogTrigger(void* port_pointer, uint32_t *index, int32_t *status) {
+ Port* port = (Port*) port_pointer;
+ Resource::CreateResourceObject(&triggers, tAnalogTrigger::kNumSystems);
+
+ AnalogTrigger* trigger = new AnalogTrigger();
+ trigger->port = (AnalogPort*) initializeAnalogInputPort(port, status);
+ trigger->index = triggers->Allocate("Analog Trigger");
+ *index = trigger->index;
+ // TODO: if (index == ~0ul) { CloneError(triggers); return; }
+
+ trigger->trigger = tAnalogTrigger::create(trigger->index, status);
+ trigger->trigger->writeSourceSelect_Channel(port->pin, status);
+
+ return trigger;
+}
+
+void cleanAnalogTrigger(void* analog_trigger_pointer, int32_t *status) {
+ AnalogTrigger* trigger = (AnalogTrigger*) analog_trigger_pointer;
+ triggers->Free(trigger->index);
+ delete trigger->trigger;
+ delete trigger;
+}
+
+void setAnalogTriggerLimitsRaw(void* analog_trigger_pointer, int32_t lower, int32_t upper, int32_t *status) {
+ AnalogTrigger* trigger = (AnalogTrigger*) analog_trigger_pointer;
+ if (lower > upper) {
+ *status = ANALOG_TRIGGER_LIMIT_ORDER_ERROR;
+ }
+ trigger->trigger->writeLowerLimit(lower, status);
+ trigger->trigger->writeUpperLimit(upper, status);
+}
+
+/**
+ * Set the upper and lower limits of the analog trigger.
+ * The limits are given as floating point voltage values.
+ */
+void setAnalogTriggerLimitsVoltage(void* analog_trigger_pointer, double lower, double upper, int32_t *status) {
+ AnalogTrigger* trigger = (AnalogTrigger*) analog_trigger_pointer;
+ if (lower > upper) {
+ *status = ANALOG_TRIGGER_LIMIT_ORDER_ERROR;
+ }
+ // TODO: This depends on the averaged setting. Only raw values will work as is.
+ trigger->trigger->writeLowerLimit(getAnalogVoltsToValue(trigger->port, lower, status), status);
+ trigger->trigger->writeUpperLimit(getAnalogVoltsToValue(trigger->port, upper, status), status);
+}
+
+/**
+ * Configure the analog trigger to use the averaged vs. raw values.
+ * If the value is true, then the averaged value is selected for the analog trigger, otherwise
+ * the immediate value is used.
+ */
+void setAnalogTriggerAveraged(void* analog_trigger_pointer, bool useAveragedValue, int32_t *status) {
+ AnalogTrigger* trigger = (AnalogTrigger*) analog_trigger_pointer;
+ if (trigger->trigger->readSourceSelect_Filter(status) != 0) {
+ *status = INCOMPATIBLE_STATE;
+ // TODO: wpi_setWPIErrorWithContext(IncompatibleMode, "Hardware does not support average and filtering at the same time.");
+ }
+ trigger->trigger->writeSourceSelect_Averaged(useAveragedValue, status);
+}
+
+/**
+ * Configure the analog trigger to use a filtered value.
+ * The analog trigger will operate with a 3 point average rejection filter. This is designed to
+ * help with 360 degree pot applications for the period where the pot crosses through zero.
+ */
+void setAnalogTriggerFiltered(void* analog_trigger_pointer, bool useFilteredValue, int32_t *status) {
+ AnalogTrigger* trigger = (AnalogTrigger*) analog_trigger_pointer;
+ if (trigger->trigger->readSourceSelect_Averaged(status) != 0) {
+ *status = INCOMPATIBLE_STATE;
+ // TODO: wpi_setWPIErrorWithContext(IncompatibleMode, "Hardware does not support average and filtering at the same time.");
+ }
+ trigger->trigger->writeSourceSelect_Filter(useFilteredValue, status);
+}
+
+/**
+ * Return the InWindow output of the analog trigger.
+ * True if the analog input is between the upper and lower limits.
+ * @return The InWindow output of the analog trigger.
+ */
+bool getAnalogTriggerInWindow(void* analog_trigger_pointer, int32_t *status) {
+ AnalogTrigger* trigger = (AnalogTrigger*) analog_trigger_pointer;
+ return trigger->trigger->readOutput_InHysteresis(trigger->index, status) != 0;
+}
+
+/**
+ * Return the TriggerState output of the analog trigger.
+ * True if above upper limit.
+ * False if below lower limit.
+ * If in Hysteresis, maintain previous state.
+ * @return The TriggerState output of the analog trigger.
+ */
+bool getAnalogTriggerTriggerState(void* analog_trigger_pointer, int32_t *status) {
+ AnalogTrigger* trigger = (AnalogTrigger*) analog_trigger_pointer;
+ return trigger->trigger->readOutput_OverLimit(trigger->index, status) != 0;
+}
+
+/**
+ * Get the state of the analog trigger output.
+ * @return The state of the analog trigger output.
+ */
+bool getAnalogTriggerOutput(void* analog_trigger_pointer, AnalogTriggerType type, int32_t *status) {
+ AnalogTrigger* trigger = (AnalogTrigger*) analog_trigger_pointer;
+ bool result = false;
+ switch(type) {
+ case kInWindow:
+ result = trigger->trigger->readOutput_InHysteresis(trigger->index, status);
+ break; // XXX: Backport
+ case kState:
+ result = trigger->trigger->readOutput_OverLimit(trigger->index, status);
+ break; // XXX: Backport
+ case kRisingPulse:
+ case kFallingPulse:
+ *status = ANALOG_TRIGGER_PULSE_OUTPUT_ERROR;
+ return false;
+ }
+ return result;
+}
+
+
+
+//// Float JNA Hack
+// Float
+int getAnalogSampleRateIntHack(int32_t *status) {
+ return floatToInt(getAnalogSampleRate(status));
+}
+
+int getAnalogVoltageIntHack(void* analog_port_pointer, int32_t *status) {
+ return floatToInt(getAnalogVoltage(analog_port_pointer, status));
+}
+
+int getAnalogAverageVoltageIntHack(void* analog_port_pointer, int32_t *status) {
+ return floatToInt(getAnalogAverageVoltage(analog_port_pointer, status));
+}
+
+
+// Doubles
+void setAnalogSampleRateIntHack(int samplesPerSecond, int32_t *status) {
+ setAnalogSampleRate(intToFloat(samplesPerSecond), status);
+}
+
+int32_t getAnalogVoltsToValueIntHack(void* analog_port_pointer, int voltage, int32_t *status) {
+ return getAnalogVoltsToValue(analog_port_pointer, intToFloat(voltage), status);
+}
+
+void setAnalogTriggerLimitsVoltageIntHack(void* analog_trigger_pointer, int lower, int upper, int32_t *status) {
+ setAnalogTriggerLimitsVoltage(analog_trigger_pointer, intToFloat(lower), intToFloat(upper), status);
+}
diff --git a/aos/externals/allwpilib/hal/lib/Athena/ChipObject.h b/aos/externals/allwpilib/hal/lib/Athena/ChipObject.h
new file mode 100644
index 0000000..630c34d
--- /dev/null
+++ b/aos/externals/allwpilib/hal/lib/Athena/ChipObject.h
@@ -0,0 +1,43 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+#pragma once
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wpedantic"
+#pragma GCC diagnostic ignored "-Wignored-qualifiers"
+
+#include <stdint.h>
+
+#include "FRC_FPGA_ChipObject/RoboRIO_FRC_ChipObject_Aliases.h"
+#include "FRC_FPGA_ChipObject/tDMAChannelDescriptor.h"
+#include "FRC_FPGA_ChipObject/tDMAManager.h"
+#include "FRC_FPGA_ChipObject/tInterruptManager.h"
+#include "FRC_FPGA_ChipObject/tSystem.h"
+#include "FRC_FPGA_ChipObject/tSystemInterface.h"
+
+#include "FRC_FPGA_ChipObject/nRoboRIO_FPGANamespace/nInterfaceGlobals.h"
+#include "FRC_FPGA_ChipObject/nRoboRIO_FPGANamespace/tAccel.h"
+#include "FRC_FPGA_ChipObject/nRoboRIO_FPGANamespace/tAccumulator.h"
+#include "FRC_FPGA_ChipObject/nRoboRIO_FPGANamespace/tAI.h"
+#include "FRC_FPGA_ChipObject/nRoboRIO_FPGANamespace/tAlarm.h"
+#include "FRC_FPGA_ChipObject/nRoboRIO_FPGANamespace/tAnalogTrigger.h"
+#include "FRC_FPGA_ChipObject/nRoboRIO_FPGANamespace/tAO.h"
+#include "FRC_FPGA_ChipObject/nRoboRIO_FPGANamespace/tBIST.h"
+#include "FRC_FPGA_ChipObject/nRoboRIO_FPGANamespace/tCounter.h"
+#include "FRC_FPGA_ChipObject/nRoboRIO_FPGANamespace/tDIO.h"
+#include "FRC_FPGA_ChipObject/nRoboRIO_FPGANamespace/tDMA.h"
+#include "FRC_FPGA_ChipObject/nRoboRIO_FPGANamespace/tEncoder.h"
+#include "FRC_FPGA_ChipObject/nRoboRIO_FPGANamespace/tGlobal.h"
+#include "FRC_FPGA_ChipObject/nRoboRIO_FPGANamespace/tInterrupt.h"
+#include "FRC_FPGA_ChipObject/nRoboRIO_FPGANamespace/tPower.h"
+#include "FRC_FPGA_ChipObject/nRoboRIO_FPGANamespace/tPWM.h"
+#include "FRC_FPGA_ChipObject/nRoboRIO_FPGANamespace/tRelay.h"
+#include "FRC_FPGA_ChipObject/nRoboRIO_FPGANamespace/tSPI.h"
+#include "FRC_FPGA_ChipObject/nRoboRIO_FPGANamespace/tSysWatchdog.h"
+
+// FIXME: these should not be here!
+using namespace nFPGA;
+using namespace nRoboRIO_FPGANamespace;
+#pragma GCC diagnostic pop
diff --git a/aos/externals/allwpilib/hal/lib/Athena/Compressor.cpp b/aos/externals/allwpilib/hal/lib/Athena/Compressor.cpp
new file mode 100644
index 0000000..9305097
--- /dev/null
+++ b/aos/externals/allwpilib/hal/lib/Athena/Compressor.cpp
@@ -0,0 +1,116 @@
+#include "HAL/Compressor.hpp"
+#include "ctre/PCM.h"
+#include <iostream>
+
+static const int NUM_MODULE_NUMBERS = 63;
+extern PCM *modules[NUM_MODULE_NUMBERS];
+extern void initializePCM(int module);
+
+void *initializeCompressor(uint8_t module) {
+ initializePCM(module);
+
+ return modules[module];
+}
+
+bool checkCompressorModule(uint8_t module) {
+ return module < NUM_MODULE_NUMBERS;
+}
+
+bool getCompressor(void *pcm_pointer, int32_t *status) {
+ PCM *module = (PCM *)pcm_pointer;
+ bool value;
+
+ *status = module->GetCompressor(value);
+
+ return value;
+}
+
+
+void setClosedLoopControl(void *pcm_pointer, bool value, int32_t *status) {
+ PCM *module = (PCM *)pcm_pointer;
+
+ *status = module->SetClosedLoopControl(value);
+}
+
+
+bool getClosedLoopControl(void *pcm_pointer, int32_t *status) {
+ PCM *module = (PCM *)pcm_pointer;
+ bool value;
+
+ *status = module->GetClosedLoopControl(value);
+
+ return value;
+}
+
+
+bool getPressureSwitch(void *pcm_pointer, int32_t *status) {
+ PCM *module = (PCM *)pcm_pointer;
+ bool value;
+
+ *status = module->GetPressure(value);
+
+ return value;
+}
+
+
+float getCompressorCurrent(void *pcm_pointer, int32_t *status) {
+ PCM *module = (PCM *)pcm_pointer;
+ float value;
+
+ *status = module->GetCompressorCurrent(value);
+
+ return value;
+}
+bool getCompressorCurrentTooHighFault(void *pcm_pointer, int32_t *status) {
+ PCM *module = (PCM *)pcm_pointer;
+ bool value;
+
+ *status = module->GetCompressorCurrentTooHighFault(value);
+
+ return value;
+}
+bool getCompressorCurrentTooHighStickyFault(void *pcm_pointer, int32_t *status) {
+ PCM *module = (PCM *)pcm_pointer;
+ bool value;
+
+ *status = module->GetCompressorCurrentTooHighStickyFault(value);
+
+ return value;
+}
+bool getCompressorShortedStickyFault(void *pcm_pointer, int32_t *status) {
+ PCM *module = (PCM *)pcm_pointer;
+ bool value;
+
+ *status = module->GetCompressorShortedStickyFault(value);
+
+ return value;
+}
+bool getCompressorShortedFault(void *pcm_pointer, int32_t *status) {
+ PCM *module = (PCM *)pcm_pointer;
+ bool value;
+
+ *status = module->GetCompressorShortedFault(value);
+
+ return value;
+}
+bool getCompressorNotConnectedStickyFault(void *pcm_pointer, int32_t *status) {
+ PCM *module = (PCM *)pcm_pointer;
+ bool value;
+
+ *status = module->GetCompressorNotConnectedStickyFault(value);
+
+ return value;
+}
+bool getCompressorNotConnectedFault(void *pcm_pointer, int32_t *status) {
+ PCM *module = (PCM *)pcm_pointer;
+ bool value;
+
+ *status = module->GetCompressorNotConnectedFault(value);
+
+ return value;
+}
+void clearAllPCMStickyFaults(void *pcm_pointer, int32_t *status) {
+ PCM *module = (PCM *)pcm_pointer;
+
+ *status = module->ClearStickyFaults();
+}
diff --git a/aos/externals/allwpilib/hal/lib/Athena/Digital.cpp b/aos/externals/allwpilib/hal/lib/Athena/Digital.cpp
new file mode 100644
index 0000000..3daaf09
--- /dev/null
+++ b/aos/externals/allwpilib/hal/lib/Athena/Digital.cpp
@@ -0,0 +1,1563 @@
+
+#include "HAL/Digital.hpp"
+
+#include "Port.h"
+#include "HAL/HAL.hpp"
+#include "ChipObject.h"
+#include "HAL/cpp/Synchronized.hpp"
+#include "HAL/cpp/Resource.hpp"
+#include "NetworkCommunication/LoadOut.h"
+#include <stdio.h>
+#include <math.h>
+#include "i2clib/i2c-lib.h"
+#include "spilib/spi-lib.h"
+
+static const uint32_t kExpectedLoopTiming = 40;
+static const uint32_t kDigitalPins = 26;
+static const uint32_t kPwmPins = 20;
+static const uint32_t kRelayPins = 8;
+static const uint32_t kNumHeaders = 10; // Number of non-MXP pins
+
+ReentrantMutex& spiGetSemaphore(uint8_t port);
+
+/**
+ * kDefaultPwmPeriod is in ms
+ *
+ * - 20ms periods (50 Hz) are the "safest" setting in that this works for all devices
+ * - 20ms periods seem to be desirable for Vex Motors
+ * - 20ms periods are the specified period for HS-322HD servos, but work reliably down
+ * to 10.0 ms; starting at about 8.5ms, the servo sometimes hums and get hot;
+ * by 5.0ms the hum is nearly continuous
+ * - 10ms periods work well for Victor 884
+ * - 5ms periods allows higher update rates for Luminary Micro Jaguar speed controllers.
+ * Due to the shipping firmware on the Jaguar, we can't run the update period less
+ * than 5.05 ms.
+ *
+ * kDefaultPwmPeriod is the 1x period (5.05 ms). In hardware, the period scaling is implemented as an
+ * output squelch to get longer periods for old devices.
+ */
+static const float kDefaultPwmPeriod = 5.05;
+/**
+ * kDefaultPwmCenter is the PWM range center in ms
+ */
+static const float kDefaultPwmCenter = 1.5;
+/**
+ * kDefaultPWMStepsDown is the number of PWM steps below the centerpoint
+ */
+static const int32_t kDefaultPwmStepsDown = 1000;
+static const int32_t kPwmDisabled = 0;
+
+struct DigitalPort {
+ Port port;
+ uint32_t PWMGeneratorID;
+};
+
+// XXX: Set these back to static once we figure out the memory clobbering issue
+// Create a semaphore to protect changes to the digital output values
+ReentrantMutex digitalDIOSemaphore;
+// Create a semaphore to protect changes to the relay values
+ReentrantMutex digitalRelaySemaphore;
+// Create a semaphore to protect changes to the DO PWM config
+ReentrantMutex digitalPwmSemaphore;
+ReentrantMutex digitalI2COnBoardSemaphore;
+ReentrantMutex digitalI2CMXPSemaphore;
+
+tDIO* digitalSystem = NULL;
+tRelay* relaySystem = NULL;
+tPWM* pwmSystem = NULL;
+Resource *DIOChannels = NULL;
+Resource *DO_PWMGenerators = NULL;
+Resource *PWMChannels = NULL;
+
+bool digitalSystemsInitialized = false;
+
+uint8_t i2COnboardObjCount = 0;
+uint8_t i2CMXPObjCount = 0;
+uint8_t i2COnBoardHandle = 0;
+uint8_t i2CMXPHandle = 0;
+
+int32_t m_spiCS0Handle = 0;
+int32_t m_spiCS1Handle = 0;
+int32_t m_spiCS2Handle = 0;
+int32_t m_spiCS3Handle = 0;
+int32_t m_spiMXPHandle = 0;
+ReentrantMutex spiOnboardSemaphore;
+ReentrantMutex spiMXPSemaphore;
+tSPI *spiSystem;
+
+/**
+ * Initialize the digital system.
+ */
+void initializeDigital(int32_t *status) {
+ if (digitalSystemsInitialized) return;
+
+ Resource::CreateResourceObject(&DIOChannels, tDIO::kNumSystems * kDigitalPins);
+ Resource::CreateResourceObject(&DO_PWMGenerators, tDIO::kNumPWMDutyCycleAElements + tDIO::kNumPWMDutyCycleBElements);
+ Resource::CreateResourceObject(&PWMChannels, tPWM::kNumSystems * kPwmPins);
+ digitalSystem = tDIO::create(status);
+
+ // Relay Setup
+ relaySystem = tRelay::create(status);
+
+ // Turn off all relay outputs.
+ relaySystem->writeValue_Forward(0, status);
+ relaySystem->writeValue_Reverse(0, status);
+
+ // PWM Setup
+ pwmSystem = tPWM::create(status);
+
+ // Make sure that the 9403 IONode has had a chance to initialize before continuing.
+ while(pwmSystem->readLoopTiming(status) == 0) delayTicks(1);
+
+ if (pwmSystem->readLoopTiming(status) != kExpectedLoopTiming) {
+ // TODO: char err[128];
+ // TODO: sprintf(err, "DIO LoopTiming: %d, expecting: %lu\n", digitalModules[port->module-1]->readLoopTiming(status), kExpectedLoopTiming);
+ *status = LOOP_TIMING_ERROR; // NOTE: Doesn't display the error
+ }
+
+ //Calculate the length, in ms, of one DIO loop
+ double loopTime = pwmSystem->readLoopTiming(status)/(kSystemClockTicksPerMicrosecond*1e3);
+
+ pwmSystem->writeConfig_Period((uint16_t) (kDefaultPwmPeriod/loopTime + .5), status);
+ uint16_t minHigh = (uint16_t) ((kDefaultPwmCenter-kDefaultPwmStepsDown*loopTime)/loopTime + .5);
+ pwmSystem->writeConfig_MinHigh(minHigh, status);
+// printf("MinHigh: %d\n", minHigh);
+ // Ensure that PWM output values are set to OFF
+ for (uint32_t pwm_index = 0; pwm_index < kPwmPins; pwm_index++) {
+ // Initialize port structure
+ DigitalPort* digital_port = new DigitalPort();
+ digital_port->port.pin = pwm_index;
+
+ setPWM(digital_port, kPwmDisabled, status);
+ setPWMPeriodScale(digital_port, 3, status); // Set all to 4x by default.
+ }
+
+ digitalSystemsInitialized = true;
+}
+
+/**
+ * Create a new instance of a digital port.
+ */
+void* initializeDigitalPort(void* port_pointer, int32_t *status) {
+ initializeDigital(status);
+ Port* port = (Port*) port_pointer;
+
+ // Initialize port structure
+ DigitalPort* digital_port = new DigitalPort();
+ digital_port->port = *port;
+
+ return digital_port;
+}
+
+bool checkPWMChannel(void* digital_port_pointer) {
+ DigitalPort* port = (DigitalPort*) digital_port_pointer;
+ return port->port.pin < kPwmPins;
+}
+
+bool checkRelayChannel(void* digital_port_pointer) {
+ DigitalPort* port = (DigitalPort*) digital_port_pointer;
+ return port->port.pin < kRelayPins;
+}
+
+/**
+ * Map DIO pin numbers from their physical number (10 to 26) to their position
+ * in the bit field.
+ */
+uint32_t remapMXPChannel(uint32_t pin) {
+ return pin - 10;
+}
+
+uint32_t remapMXPPWMChannel(uint32_t pin) {
+ if(pin < 14) {
+ return pin - 10; //first block of 4 pwms (MXP 0-3)
+ } else {
+ return pin - 6; //block of PWMs after SPI
+ }
+}
+
+/**
+ * Set a PWM channel to the desired value. The values range from 0 to 255 and the period is controlled
+ * by the PWM Period and MinHigh registers.
+ *
+ * @param channel The PWM channel to set.
+ * @param value The PWM value to set.
+ */
+void setPWM(void* digital_port_pointer, unsigned short value, int32_t *status) {
+ DigitalPort* port = (DigitalPort*) digital_port_pointer;
+ checkPWMChannel(port);
+
+ if(port->port.pin < tPWM::kNumHdrRegisters) {
+ pwmSystem->writeHdr(port->port.pin, value, status);
+ } else {
+ pwmSystem->writeMXP(port->port.pin - tPWM::kNumHdrRegisters, value, status);
+ }
+}
+
+/**
+ * Get a value from a PWM channel. The values range from 0 to 255.
+ *
+ * @param channel The PWM channel to read from.
+ * @return The raw PWM value.
+ */
+unsigned short getPWM(void* digital_port_pointer, int32_t *status) {
+ DigitalPort* port = (DigitalPort*) digital_port_pointer;
+ checkPWMChannel(port);
+
+ if(port->port.pin < tPWM::kNumHdrRegisters) {
+ return pwmSystem->readHdr(port->port.pin, status);
+ } else {
+ return pwmSystem->readMXP(port->port.pin - tPWM::kNumHdrRegisters, status);
+ }
+}
+
+void latchPWMZero(void* digital_port_pointer, int32_t *status) {
+ DigitalPort* port = (DigitalPort*) digital_port_pointer;
+ pwmSystem->writeZeroLatch(port->port.pin, true, status);
+ pwmSystem->writeZeroLatch(port->port.pin, false, status);
+}
+
+/**
+ * Set how how often the PWM signal is squelched, thus scaling the period.
+ *
+ * @param channel The PWM channel to configure.
+ * @param squelchMask The 2-bit mask of outputs to squelch.
+ */
+void setPWMPeriodScale(void* digital_port_pointer, uint32_t squelchMask, int32_t *status) {
+ DigitalPort* port = (DigitalPort*) digital_port_pointer;
+ checkPWMChannel(port);
+
+ if(port->port.pin < tPWM::kNumPeriodScaleHdrElements) {
+ pwmSystem->writePeriodScaleHdr(port->port.pin, squelchMask, status);
+ } else {
+ pwmSystem->writePeriodScaleMXP(port->port.pin - tPWM::kNumPeriodScaleHdrElements, squelchMask, status);
+ }
+}
+
+/**
+ * Allocate a DO PWM Generator.
+ * Allocate PWM generators so that they are not accidentally reused.
+ *
+ * @return PWM Generator refnum
+ */
+void* allocatePWM(int32_t *status) {
+ return (void*)DO_PWMGenerators->Allocate("DO_PWM");
+}
+
+/**
+ * Free the resource associated with a DO PWM generator.
+ *
+ * @param pwmGenerator The pwmGen to free that was allocated with AllocateDO_PWM()
+ */
+void freePWM(void* pwmGenerator, int32_t *status) {
+ uint32_t id = (uint32_t) pwmGenerator;
+ if (id == ~0ul) return;
+ DO_PWMGenerators->Free(id);
+}
+
+/**
+ * Change the frequency of the DO PWM generator.
+ *
+ * The valid range is from 0.6 Hz to 19 kHz. The frequency resolution is logarithmic.
+ *
+ * @param rate The frequency to output all digital output PWM signals.
+ */
+void setPWMRate(double rate, int32_t *status) {
+ // Currently rounding in the log rate domain... heavy weight toward picking a higher freq.
+ // TODO: Round in the linear rate domain.
+ uint8_t pwmPeriodPower = (uint8_t)(log(1.0 / (pwmSystem->readLoopTiming(status) * 0.25E-6 * rate))/log(2.0) + 0.5);
+ digitalSystem->writePWMPeriodPower(pwmPeriodPower, status);
+}
+
+
+/**
+ * Configure the duty-cycle of the PWM generator
+ *
+ * @param pwmGenerator The generator index reserved by AllocateDO_PWM()
+ * @param dutyCycle The percent duty cycle to output [0..1].
+ */
+void setPWMDutyCycle(void* pwmGenerator, double dutyCycle, int32_t *status) {
+ uint32_t id = (uint32_t) pwmGenerator;
+ if (id == ~0ul) return;
+ if (dutyCycle > 1.0) dutyCycle = 1.0;
+ if (dutyCycle < 0.0) dutyCycle = 0.0;
+ float rawDutyCycle = 256.0 * dutyCycle;
+ if (rawDutyCycle > 255.5) rawDutyCycle = 255.5;
+ {
+ ::std::unique_lock<ReentrantMutex> sync(digitalPwmSemaphore);
+ uint8_t pwmPeriodPower = digitalSystem->readPWMPeriodPower(status);
+ if (pwmPeriodPower < 4) {
+ // The resolution of the duty cycle drops close to the highest frequencies.
+ rawDutyCycle = rawDutyCycle / pow(2.0, 4 - pwmPeriodPower);
+ }
+ if(id < 4)
+ digitalSystem->writePWMDutyCycleA(id, (uint8_t)rawDutyCycle, status);
+ else
+ digitalSystem->writePWMDutyCycleB(id - 4, (uint8_t)rawDutyCycle, status);
+ }
+}
+
+/**
+ * Configure which DO channel the PWM signal is output on
+ *
+ * @param pwmGenerator The generator index reserved by AllocateDO_PWM()
+ * @param channel The Digital Output channel to output on
+ */
+void setPWMOutputChannel(void* pwmGenerator, uint32_t pin, int32_t *status) {
+ uint32_t id = (uint32_t) pwmGenerator;
+ if (id > 5) return;
+ digitalSystem->writePWMOutputSelect(id, pin, status);
+}
+
+/**
+ * Set the state of a relay.
+ * Set the state of a relay output to be forward. Relays have two outputs and each is
+ * independently set to 0v or 12v.
+ */
+void setRelayForward(void* digital_port_pointer, bool on, int32_t *status) {
+ DigitalPort* port = (DigitalPort*) digital_port_pointer;
+ checkRelayChannel(port);
+ {
+ ::std::unique_lock<ReentrantMutex> sync(digitalRelaySemaphore);
+ uint8_t forwardRelays = relaySystem->readValue_Forward(status);
+ if (on)
+ forwardRelays |= 1 << port->port.pin;
+ else
+ forwardRelays &= ~(1 << port->port.pin);
+ relaySystem->writeValue_Forward(forwardRelays, status);
+ }
+}
+
+/**
+ * Set the state of a relay.
+ * Set the state of a relay output to be reverse. Relays have two outputs and each is
+ * independently set to 0v or 12v.
+ */
+void setRelayReverse(void* digital_port_pointer, bool on, int32_t *status) {
+ DigitalPort* port = (DigitalPort*) digital_port_pointer;
+ checkRelayChannel(port);
+ {
+ ::std::unique_lock<ReentrantMutex> sync(digitalRelaySemaphore);
+ uint8_t reverseRelays = relaySystem->readValue_Reverse(status);
+ if (on)
+ reverseRelays |= 1 << port->port.pin;
+ else
+ reverseRelays &= ~(1 << port->port.pin);
+ relaySystem->writeValue_Reverse(reverseRelays, status);
+ }
+}
+
+/**
+ * Get the current state of the forward relay channel
+ */
+bool getRelayForward(void* digital_port_pointer, int32_t *status) {
+ DigitalPort* port = (DigitalPort*) digital_port_pointer;
+ uint8_t forwardRelays = relaySystem->readValue_Forward(status);
+ return (forwardRelays & (1 << port->port.pin)) != 0;
+}
+
+/**
+ * Get the current state of the reverse relay channel
+ */
+bool getRelayReverse(void* digital_port_pointer, int32_t *status) {
+ DigitalPort* port = (DigitalPort*) digital_port_pointer;
+ uint8_t reverseRelays = relaySystem->readValue_Reverse(status);
+ return (reverseRelays & (1 << port->port.pin)) != 0;
+}
+
+/**
+ * Allocate Digital I/O channels.
+ * Allocate channels so that they are not accidently reused. Also the direction is set at the
+ * time of the allocation.
+ *
+ * @param channel The Digital I/O channel
+ * @param input If true open as input; if false open as output
+ * @return Was successfully allocated
+ */
+bool allocateDIO(void* digital_port_pointer, bool input, int32_t *status) {
+ DigitalPort* port = (DigitalPort*) digital_port_pointer;
+ char buf[64];
+ snprintf(buf, 64, "DIO %d", port->port.pin);
+ if (DIOChannels->Allocate(port->port.pin, buf) == ~0ul) {
+ *status = RESOURCE_IS_ALLOCATED;
+ return false;
+ }
+
+ {
+ ::std::unique_lock<ReentrantMutex> sync(digitalDIOSemaphore);
+
+ tDIO::tOutputEnable outputEnable = digitalSystem->readOutputEnable(status);
+
+ if(port->port.pin < kNumHeaders) {
+ uint32_t bitToSet = 1 << port->port.pin;
+ if (input) {
+ outputEnable.Headers = outputEnable.Headers & (~bitToSet); // clear the bit for read
+ } else {
+ outputEnable.Headers = outputEnable.Headers | bitToSet; // set the bit for write
+ }
+ } else {
+ uint32_t bitToSet = 1 << remapMXPChannel(port->port.pin);
+
+ // Disable special functions on this pin
+ short specialFunctions = digitalSystem->readEnableMXPSpecialFunction(status);
+ digitalSystem->writeEnableMXPSpecialFunction(specialFunctions & ~bitToSet, status);
+
+ if (input) {
+ outputEnable.MXP = outputEnable.MXP & (~bitToSet); // clear the bit for read
+ } else {
+ outputEnable.MXP = outputEnable.MXP | bitToSet; // set the bit for write
+ }
+ }
+
+ digitalSystem->writeOutputEnable(outputEnable, status);
+ }
+ return true;
+}
+
+bool allocatePWMChannel(void* digital_port_pointer, int32_t *status) {
+ DigitalPort* port = (DigitalPort*) digital_port_pointer;
+ char buf[64];
+ snprintf(buf, 64, "PWM %d", port->port.pin);
+ if (PWMChannels->Allocate(port->port.pin, buf) == ~0ul) {
+ *status = RESOURCE_IS_ALLOCATED;
+ return false;
+ }
+
+ if (port->port.pin > tPWM::kNumHdrRegisters-1) {
+ snprintf(buf, 64, "PWM %d and DIO %d", port->port.pin, remapMXPPWMChannel(port->port.pin) + 10);
+ if (DIOChannels->Allocate(remapMXPPWMChannel(port->port.pin) + 10, buf) == ~0ul) return false;
+ uint32_t bitToSet = 1 << remapMXPPWMChannel(port->port.pin);
+ short specialFunctions = digitalSystem->readEnableMXPSpecialFunction(status);
+ digitalSystem->writeEnableMXPSpecialFunction(specialFunctions | bitToSet, status);
+ }
+ return true;
+}
+
+void freePWMChannel(void* digital_port_pointer, int32_t *status) {
+ DigitalPort* port = (DigitalPort*) digital_port_pointer;
+ PWMChannels->Free(port->port.pin);
+ if(port->port.pin > tPWM::kNumHdrRegisters-1) {
+ DIOChannels->Free(remapMXPPWMChannel(port->port.pin) + 10);
+ uint32_t bitToUnset = 1 << remapMXPPWMChannel(port->port.pin);
+ short specialFunctions = digitalSystem->readEnableMXPSpecialFunction(status);
+ digitalSystem->writeEnableMXPSpecialFunction(specialFunctions & ~bitToUnset, status);
+ }
+}
+
+/**
+ * Free the resource associated with a digital I/O channel.
+ *
+ * @param channel The Digital I/O channel to free
+ */
+void freeDIO(void* digital_port_pointer, int32_t *status) {
+ DigitalPort* port = (DigitalPort*) digital_port_pointer;
+ DIOChannels->Free(port->port.pin);
+}
+
+/**
+ * Write a digital I/O bit to the FPGA.
+ * Set a single value on a digital I/O channel.
+ *
+ * @param channel The Digital I/O channel
+ * @param value The state to set the digital channel (if it is configured as an output)
+ */
+void setDIO(void* digital_port_pointer, short value, int32_t *status) {
+ DigitalPort* port = (DigitalPort*) digital_port_pointer;
+ if (value != 0 && value != 1) {
+ if (value != 0)
+ value = 1;
+ }
+ {
+ ::std::unique_lock<ReentrantMutex> sync(digitalDIOSemaphore);
+ tDIO::tDO currentDIO = digitalSystem->readDO(status);
+
+ if(port->port.pin < kNumHeaders) {
+ if(value == 0) {
+ currentDIO.Headers = currentDIO.Headers & ~(1 << port->port.pin);
+ } else if (value == 1) {
+ currentDIO.Headers = currentDIO.Headers | (1 << port->port.pin);
+ }
+ } else {
+ if(value == 0) {
+ currentDIO.MXP = currentDIO.MXP & ~(1 << remapMXPChannel(port->port.pin));
+ } else if (value == 1) {
+ currentDIO.MXP = currentDIO.MXP | (1 << remapMXPChannel(port->port.pin));
+ }
+
+ uint32_t bitToSet = 1 << remapMXPChannel(port->port.pin);
+ short specialFunctions = digitalSystem->readEnableMXPSpecialFunction(status);
+ digitalSystem->writeEnableMXPSpecialFunction(specialFunctions & ~bitToSet, status);
+ }
+ digitalSystem->writeDO(currentDIO, status);
+ }
+}
+
+/**
+ * Write the filter index from the FPGA.
+ * Set the filter index used to filter out short pulses.
+ *
+ * @param digital_port_pointer The digital I/O channel
+ * @param filter_index The filter index. Must be between 0 and 3.
+ */
+void setFilterSelect(void* digital_port_pointer, int filter_index,
+ int32_t* status) {
+ DigitalPort* port = (DigitalPort*)digital_port_pointer;
+ {
+ ::std::unique_lock<ReentrantMutex> sync(digitalDIOSemaphore);
+ if (port->port.pin < kNumHeaders) {
+ digitalSystem->writeFilterSelectHdr(port->port.pin, filter_index, status);
+ } else {
+ digitalSystem->writeFilterSelectMXP(remapMXPChannel(port->port.pin),
+ filter_index, status);
+ }
+ }
+}
+
+/**
+ * Read the filter index from the FPGA.
+ * Get the filter index used to filter out short pulses.
+ *
+ * @param digital_port_pointer The digital I/O channel
+ * @return filter_index The filter index. Must be between 0 and 3.
+ */
+int getFilterSelect(void* digital_port_pointer, int32_t* status) {
+ DigitalPort* port = (DigitalPort*)digital_port_pointer;
+ {
+ ::std::unique_lock<ReentrantMutex> sync(digitalDIOSemaphore);
+ if (port->port.pin < kNumHeaders) {
+ return digitalSystem->readFilterSelectHdr(port->port.pin, status);
+ } else {
+ return digitalSystem->readFilterSelectMXP(remapMXPChannel(port->port.pin),
+ status);
+ }
+ }
+}
+
+/**
+ * Set the filter period for the specified filter index.
+ *
+ * Set the filter period in FPGA cycles. Even though there are 2 different
+ * filter index domains (MXP vs HDR), ignore that distinction for now since it
+ * compilicates the interface. That can be changed later.
+ *
+ * @param filter_index The filter index, 0 - 2.
+ * @param value The number of cycles that the signal must not transition to be
+ * counted as a transition.
+ */
+void setFilterPeriod(int filter_index, uint32_t value, int32_t* status) {
+ {
+ ::std::unique_lock<ReentrantMutex> sync(digitalDIOSemaphore);
+ digitalSystem->writeFilterPeriodHdr(filter_index, value, status);
+ if (*status == 0) {
+ digitalSystem->writeFilterPeriodMXP(filter_index, value, status);
+ }
+ }
+}
+
+/**
+ * Get the filter period for the specified filter index.
+ *
+ * Get the filter period in FPGA cycles. Even though there are 2 different
+ * filter index domains (MXP vs HDR), ignore that distinction for now since it
+ * compilicates the interface. Set status to NiFpga_Status_SoftwareFault if the
+ * filter values miss-match.
+ *
+ * @param filter_index The filter index, 0 - 2.
+ * @param value The number of cycles that the signal must not transition to be
+ * counted as a transition.
+ */
+uint32_t getFilterPeriod(int filter_index, int32_t* status) {
+ uint32_t hdr_period = 0;
+ uint32_t mxp_period = 0;
+ {
+ ::std::unique_lock<ReentrantMutex> sync(digitalDIOSemaphore);
+ hdr_period = digitalSystem->readFilterPeriodHdr(filter_index, status);
+ if (*status == 0) {
+ mxp_period = digitalSystem->readFilterPeriodMXP(filter_index, status);
+ }
+ }
+ if (hdr_period != mxp_period) {
+ printf("Periods %d %d\n", hdr_period, mxp_period);
+ *status = NiFpga_Status_SoftwareFault;
+ return -1;
+ }
+ return hdr_period;
+}
+
+/**
+ * Read a digital I/O bit from the FPGA.
+ * Get a single value from a digital I/O channel.
+ *
+ * @param channel The digital I/O channel
+ * @return The state of the specified channel
+ */
+bool getDIO(void* digital_port_pointer, int32_t *status) {
+ DigitalPort* port = (DigitalPort*) digital_port_pointer;
+ tDIO::tDI currentDIO = digitalSystem->readDI(status);
+ //Shift 00000001 over channel-1 places.
+ //AND it against the currentDIO
+ //if it == 0, then return false
+ //else return true
+
+ if(port->port.pin < kNumHeaders) {
+ return ((currentDIO.Headers >> port->port.pin) & 1) != 0;
+ } else {
+ // Disable special functions
+ uint32_t bitToSet = 1 << remapMXPChannel(port->port.pin);
+ short specialFunctions = digitalSystem->readEnableMXPSpecialFunction(status);
+ digitalSystem->writeEnableMXPSpecialFunction(specialFunctions & ~bitToSet, status);
+
+ return ((currentDIO.MXP >> remapMXPChannel(port->port.pin)) & 1) != 0;
+ }
+}
+
+/**
+ * Read the direction of a the Digital I/O lines
+ * A 1 bit means output and a 0 bit means input.
+ *
+ * @param channel The digital I/O channel
+ * @return The direction of the specified channel
+ */
+bool getDIODirection(void* digital_port_pointer, int32_t *status) {
+ DigitalPort* port = (DigitalPort*) digital_port_pointer;
+ tDIO::tOutputEnable currentOutputEnable = digitalSystem->readOutputEnable(status);
+ //Shift 00000001 over port->port.pin-1 places.
+ //AND it against the currentOutputEnable
+ //if it == 0, then return false
+ //else return true
+
+ if(port->port.pin < kNumHeaders) {
+ return ((currentOutputEnable.Headers >> port->port.pin) & 1) != 0;
+ } else {
+ return ((currentOutputEnable.MXP >> remapMXPChannel(port->port.pin)) & 1) != 0;
+ }
+}
+
+/**
+ * Generate a single pulse.
+ * Write a pulse to the specified digital output channel. There can only be a single pulse going at any time.
+ *
+ * @param channel The Digital Output channel that the pulse should be output on
+ * @param pulseLength The active length of the pulse (in seconds)
+ */
+void pulse(void* digital_port_pointer, double pulseLength, int32_t *status) {
+ DigitalPort* port = (DigitalPort*) digital_port_pointer;
+ tDIO::tPulse pulse;
+
+ if(port->port.pin < kNumHeaders) {
+ pulse.Headers = 1 << port->port.pin;
+ } else {
+ pulse.MXP = 1 << remapMXPChannel(port->port.pin);
+ }
+
+ digitalSystem->writePulseLength((uint8_t)(1.0e9 * pulseLength / (pwmSystem->readLoopTiming(status) * 25)), status);
+ digitalSystem->writePulse(pulse, status);
+}
+
+/**
+ * Check a DIO line to see if it is currently generating a pulse.
+ *
+ * @return A pulse is in progress
+ */
+bool isPulsing(void* digital_port_pointer, int32_t *status) {
+ DigitalPort* port = (DigitalPort*) digital_port_pointer;
+ tDIO::tPulse pulseRegister = digitalSystem->readPulse(status);
+
+ if(port->port.pin < kNumHeaders) {
+ return (pulseRegister.Headers & (1 << port->port.pin)) != 0;
+ } else {
+ return (pulseRegister.MXP & (1 << remapMXPChannel(port->port.pin))) != 0;
+ }
+}
+
+/**
+ * Check if any DIO line is currently generating a pulse.
+ *
+ * @return A pulse on some line is in progress
+ */
+bool isAnyPulsing(int32_t *status) {
+ tDIO::tPulse pulseRegister = digitalSystem->readPulse(status);
+ return pulseRegister.Headers != 0 && pulseRegister.MXP != 0;
+}
+
+struct counter_t {
+ tCounter* counter;
+ uint32_t index;
+};
+typedef struct counter_t Counter;
+
+static Resource *counters = NULL;
+
+void* initializeCounter(Mode mode, uint32_t *index, int32_t *status) {
+ Resource::CreateResourceObject(&counters, tCounter::kNumSystems);
+ *index = counters->Allocate("Counter");
+ if (*index == ~0ul) {
+ *status = NO_AVAILABLE_RESOURCES;
+ return NULL;
+ }
+ Counter* counter = new Counter();
+ counter->counter = tCounter::create(*index, status);
+ counter->counter->writeConfig_Mode(mode, status);
+ counter->counter->writeTimerConfig_AverageSize(1, status);
+ counter->index = *index;
+ return counter;
+}
+
+void freeCounter(void* counter_pointer, int32_t *status) {
+ if (counter_pointer != NULL) {
+ Counter* counter = (Counter*) counter_pointer;
+ delete counter->counter;
+ counters->Free(counter->index);
+ } else {
+ *status = NULL_PARAMETER;
+ }
+}
+
+void setCounterAverageSize(void* counter_pointer, int32_t size, int32_t *status) {
+ Counter* counter = (Counter*) counter_pointer;
+ counter->counter->writeTimerConfig_AverageSize(size, status);
+}
+
+/**
+ * Set the source object that causes the counter to count up.
+ * Set the up counting DigitalSource.
+ */
+void setCounterUpSource(void* counter_pointer, uint32_t pin, bool analogTrigger, int32_t *status) {
+ Counter* counter = (Counter*) counter_pointer;
+
+ uint8_t module;
+
+ if(pin >= kNumHeaders) {
+ pin = remapMXPChannel(pin);
+ module = 1;
+ } else {
+ module = 0;
+ }
+
+ counter->counter->writeConfig_UpSource_Module(module, status);
+ counter->counter->writeConfig_UpSource_Channel(pin, status);
+ counter->counter->writeConfig_UpSource_AnalogTrigger(analogTrigger, status);
+
+ if(counter->counter->readConfig_Mode(status) == kTwoPulse ||
+ counter->counter->readConfig_Mode(status) == kExternalDirection) {
+ setCounterUpSourceEdge(counter_pointer, true, false, status);
+ }
+ counter->counter->strobeReset(status);
+}
+
+/**
+ * Set the edge sensitivity on an up counting source.
+ * Set the up source to either detect rising edges or falling edges.
+ */
+void setCounterUpSourceEdge(void* counter_pointer, bool risingEdge, bool fallingEdge, int32_t *status) {
+ Counter* counter = (Counter*) counter_pointer;
+ counter->counter->writeConfig_UpRisingEdge(risingEdge, status);
+ counter->counter->writeConfig_UpFallingEdge(fallingEdge, status);
+}
+
+/**
+ * Disable the up counting source to the counter.
+ */
+void clearCounterUpSource(void* counter_pointer, int32_t *status) {
+ Counter* counter = (Counter*) counter_pointer;
+ counter->counter->writeConfig_UpFallingEdge(false, status);
+ counter->counter->writeConfig_UpRisingEdge(false, status);
+ // Index 0 of digital is always 0.
+ counter->counter->writeConfig_UpSource_Channel(0, status);
+ counter->counter->writeConfig_UpSource_AnalogTrigger(false, status);
+}
+
+/**
+ * Set the source object that causes the counter to count down.
+ * Set the down counting DigitalSource.
+ */
+void setCounterDownSource(void* counter_pointer, uint32_t pin, bool analogTrigger, int32_t *status) {
+ Counter* counter = (Counter*) counter_pointer;
+ unsigned char mode = counter->counter->readConfig_Mode(status);
+ if (mode != kTwoPulse && mode != kExternalDirection) {
+ // TODO: wpi_setWPIErrorWithContext(ParameterOutOfRange, "Counter only supports DownSource in TwoPulse and ExternalDirection modes.");
+ *status = PARAMETER_OUT_OF_RANGE;
+ return;
+ }
+
+ uint8_t module;
+
+ if(pin >= kNumHeaders) {
+ pin = remapMXPChannel(pin);
+ module = 1;
+ } else {
+ module = 0;
+ }
+
+ counter->counter->writeConfig_DownSource_Module(module, status);
+ counter->counter->writeConfig_DownSource_Channel(pin, status);
+ counter->counter->writeConfig_DownSource_AnalogTrigger(analogTrigger, status);
+
+ setCounterDownSourceEdge(counter_pointer, true, false, status);
+ counter->counter->strobeReset(status);
+}
+
+/**
+ * Set the edge sensitivity on a down counting source.
+ * Set the down source to either detect rising edges or falling edges.
+ */
+void setCounterDownSourceEdge(void* counter_pointer, bool risingEdge, bool fallingEdge, int32_t *status) {
+ Counter* counter = (Counter*) counter_pointer;
+ counter->counter->writeConfig_DownRisingEdge(risingEdge, status);
+ counter->counter->writeConfig_DownFallingEdge(fallingEdge, status);
+}
+
+/**
+ * Disable the down counting source to the counter.
+ */
+void clearCounterDownSource(void* counter_pointer, int32_t *status) {
+ Counter* counter = (Counter*) counter_pointer;
+ counter->counter->writeConfig_DownFallingEdge(false, status);
+ counter->counter->writeConfig_DownRisingEdge(false, status);
+ // Index 0 of digital is always 0.
+ counter->counter->writeConfig_DownSource_Channel(0, status);
+ counter->counter->writeConfig_DownSource_AnalogTrigger(false, status);
+}
+
+/**
+ * Set standard up / down counting mode on this counter.
+ * Up and down counts are sourced independently from two inputs.
+ */
+void setCounterUpDownMode(void* counter_pointer, int32_t *status) {
+ Counter* counter = (Counter*) counter_pointer;
+ counter->counter->writeConfig_Mode(kTwoPulse, status);
+}
+
+/**
+ * Set external direction mode on this counter.
+ * Counts are sourced on the Up counter input.
+ * The Down counter input represents the direction to count.
+ */
+void setCounterExternalDirectionMode(void* counter_pointer, int32_t *status) {
+ Counter* counter = (Counter*) counter_pointer;
+ counter->counter->writeConfig_Mode(kExternalDirection, status);
+}
+
+/**
+ * Set Semi-period mode on this counter.
+ * Counts up on both rising and falling edges.
+ */
+void setCounterSemiPeriodMode(void* counter_pointer, bool highSemiPeriod, int32_t *status) {
+ Counter* counter = (Counter*) counter_pointer;
+ counter->counter->writeConfig_Mode(kSemiperiod, status);
+ counter->counter->writeConfig_UpRisingEdge(highSemiPeriod, status);
+ setCounterUpdateWhenEmpty(counter_pointer, false, status);
+}
+
+/**
+ * Configure the counter to count in up or down based on the length of the input pulse.
+ * This mode is most useful for direction sensitive gear tooth sensors.
+ * @param threshold The pulse length beyond which the counter counts the opposite direction. Units are seconds.
+ */
+void setCounterPulseLengthMode(void* counter_pointer, double threshold, int32_t *status) {
+ Counter* counter = (Counter*) counter_pointer;
+ counter->counter->writeConfig_Mode(kPulseLength, status);
+ counter->counter->writeConfig_PulseLengthThreshold((uint32_t)(threshold * 1.0e6) * kSystemClockTicksPerMicrosecond, status);
+}
+
+/**
+ * Get the Samples to Average which specifies the number of samples of the timer to
+ * average when calculating the period. Perform averaging to account for
+ * mechanical imperfections or as oversampling to increase resolution.
+ * @return SamplesToAverage The number of samples being averaged (from 1 to 127)
+ */
+int32_t getCounterSamplesToAverage(void* counter_pointer, int32_t *status) {
+ Counter* counter = (Counter*) counter_pointer;
+ return counter->counter->readTimerConfig_AverageSize(status);
+}
+
+/**
+ * Set the Samples to Average which specifies the number of samples of the timer to
+ * average when calculating the period. Perform averaging to account for
+ * mechanical imperfections or as oversampling to increase resolution.
+ * @param samplesToAverage The number of samples to average from 1 to 127.
+ */
+void setCounterSamplesToAverage(void* counter_pointer, int samplesToAverage, int32_t *status) {
+ Counter* counter = (Counter*) counter_pointer;
+ if (samplesToAverage < 1 || samplesToAverage > 127) {
+ *status = PARAMETER_OUT_OF_RANGE;
+ }
+ counter->counter->writeTimerConfig_AverageSize(samplesToAverage, status);
+}
+
+/**
+ * Reset the Counter to zero.
+ * Set the counter value to zero. This doesn't effect the running state of the counter, just sets
+ * the current value to zero.
+ */
+void resetCounter(void* counter_pointer, int32_t *status) {
+ Counter* counter = (Counter*) counter_pointer;
+ counter->counter->strobeReset(status);
+}
+
+/**
+ * Read the current counter value.
+ * Read the value at this instant. It may still be running, so it reflects the current value. Next
+ * time it is read, it might have a different value.
+ */
+int32_t getCounter(void* counter_pointer, int32_t *status) {
+ Counter* counter = (Counter*) counter_pointer;
+ int32_t value = counter->counter->readOutput_Value(status);
+ return value;
+}
+
+/*
+ * Get the Period of the most recent count.
+ * Returns the time interval of the most recent count. This can be used for velocity calculations
+ * to determine shaft speed.
+ * @returns The period of the last two pulses in units of seconds.
+ */
+double getCounterPeriod(void* counter_pointer, int32_t *status) {
+ Counter* counter = (Counter*) counter_pointer;
+ tCounter::tTimerOutput output = counter->counter->readTimerOutput(status);
+ double period;
+ if (output.Stalled) {
+ // Return infinity
+ double zero = 0.0;
+ period = 1.0 / zero;
+ } else {
+ // output.Period is a fixed point number that counts by 2 (24 bits, 25 integer bits)
+ period = (double)(output.Period << 1) / (double)output.Count;
+ }
+ return period * 2.5e-8; // result * timebase (currently 40ns)
+}
+
+/**
+ * Set the maximum period where the device is still considered "moving".
+ * Sets the maximum period where the device is considered moving. This value is used to determine
+ * the "stopped" state of the counter using the GetStopped method.
+ * @param maxPeriod The maximum period where the counted device is considered moving in
+ * seconds.
+ */
+void setCounterMaxPeriod(void* counter_pointer, double maxPeriod, int32_t *status) {
+ Counter* counter = (Counter*) counter_pointer;
+ counter->counter->writeTimerConfig_StallPeriod((uint32_t)(maxPeriod * 4.0e8), status);
+}
+
+/**
+ * Select whether you want to continue updating the event timer output when there are no samples captured.
+ * The output of the event timer has a buffer of periods that are averaged and posted to
+ * a register on the FPGA. When the timer detects that the event source has stopped
+ * (based on the MaxPeriod) the buffer of samples to be averaged is emptied. If you
+ * enable the update when empty, you will be notified of the stopped source and the event
+ * time will report 0 samples. If you disable update when empty, the most recent average
+ * will remain on the output until a new sample is acquired. You will never see 0 samples
+ * output (except when there have been no events since an FPGA reset) and you will likely not
+ * see the stopped bit become true (since it is updated at the end of an average and there are
+ * no samples to average).
+ */
+void setCounterUpdateWhenEmpty(void* counter_pointer, bool enabled, int32_t *status) {
+ Counter* counter = (Counter*) counter_pointer;
+ counter->counter->writeTimerConfig_UpdateWhenEmpty(enabled, status);
+}
+
+/**
+ * Determine if the clock is stopped.
+ * Determine if the clocked input is stopped based on the MaxPeriod value set using the
+ * SetMaxPeriod method. If the clock exceeds the MaxPeriod, then the device (and counter) are
+ * assumed to be stopped and it returns true.
+ * @return Returns true if the most recent counter period exceeds the MaxPeriod value set by
+ * SetMaxPeriod.
+ */
+bool getCounterStopped(void* counter_pointer, int32_t *status) {
+ Counter* counter = (Counter*) counter_pointer;
+ return counter->counter->readTimerOutput_Stalled(status);
+}
+
+/**
+ * The last direction the counter value changed.
+ * @return The last direction the counter value changed.
+ */
+bool getCounterDirection(void* counter_pointer, int32_t *status) {
+ Counter* counter = (Counter*) counter_pointer;
+ bool value = counter->counter->readOutput_Direction(status);
+ return value;
+}
+
+/**
+ * Set the Counter to return reversed sensing on the direction.
+ * This allows counters to change the direction they are counting in the case of 1X and 2X
+ * quadrature encoding only. Any other counter mode isn't supported.
+ * @param reverseDirection true if the value counted should be negated.
+ */
+void setCounterReverseDirection(void* counter_pointer, bool reverseDirection, int32_t *status) {
+ Counter* counter = (Counter*) counter_pointer;
+ if (counter->counter->readConfig_Mode(status) == kExternalDirection) {
+ if (reverseDirection)
+ setCounterDownSourceEdge(counter_pointer, true, true, status);
+ else
+ setCounterDownSourceEdge(counter_pointer, false, true, status);
+ }
+}
+
+struct encoder_t {
+ tEncoder* encoder;
+ uint32_t index;
+};
+typedef struct encoder_t Encoder;
+
+static const double DECODING_SCALING_FACTOR = 0.25;
+static Resource *quadEncoders = NULL;
+
+void* initializeEncoder(uint8_t port_a_module, uint32_t port_a_pin, bool port_a_analog_trigger,
+ uint8_t port_b_module, uint32_t port_b_pin, bool port_b_analog_trigger,
+ bool reverseDirection, int32_t *index, int32_t *status) {
+
+ // Initialize encoder structure
+ Encoder* encoder = new Encoder();
+
+ if(port_a_pin >= kNumHeaders) {
+ port_a_pin = remapMXPChannel(port_a_pin);
+ port_a_module = 1;
+ }
+
+ if(port_b_pin >= kNumHeaders) {
+ port_b_pin = remapMXPChannel(port_b_pin);
+ port_b_module = 1;
+ }
+
+ Resource::CreateResourceObject(&quadEncoders, tEncoder::kNumSystems);
+ encoder->index = quadEncoders->Allocate("4X Encoder");
+ *index = encoder->index;
+ // TODO: if (index == ~0ul) { CloneError(quadEncoders); return; }
+ encoder->encoder = tEncoder::create(encoder->index, status);
+ encoder->encoder->writeConfig_ASource_Module(port_a_module, status);
+ encoder->encoder->writeConfig_ASource_Channel(port_a_pin, status);
+ encoder->encoder->writeConfig_ASource_AnalogTrigger(port_a_analog_trigger, status);
+ encoder->encoder->writeConfig_BSource_Module(port_b_module, status);
+ encoder->encoder->writeConfig_BSource_Channel(port_b_pin, status);
+ encoder->encoder->writeConfig_BSource_AnalogTrigger(port_b_analog_trigger, status);
+ encoder->encoder->strobeReset(status);
+ encoder->encoder->writeConfig_Reverse(reverseDirection, status);
+ encoder->encoder->writeTimerConfig_AverageSize(4, status);
+
+ return encoder;
+}
+
+void freeEncoder(void* encoder_pointer, int32_t *status) {
+ Encoder* encoder = (Encoder*) encoder_pointer;
+ quadEncoders->Free(encoder->index);
+ delete encoder->encoder;
+}
+
+/**
+ * Reset the Encoder distance to zero.
+ * Resets the current count to zero on the encoder.
+ */
+void resetEncoder(void* encoder_pointer, int32_t *status) {
+ Encoder* encoder = (Encoder*) encoder_pointer;
+ encoder->encoder->strobeReset(status);
+}
+
+/**
+ * Gets the raw value from the encoder.
+ * The raw value is the actual count unscaled by the 1x, 2x, or 4x scale
+ * factor.
+ * @return Current raw count from the encoder
+ */
+int32_t getEncoder(void* encoder_pointer, int32_t *status) {
+ Encoder* encoder = (Encoder*) encoder_pointer;
+ return encoder->encoder->readOutput_Value(status);
+}
+
+/**
+ * Returns the period of the most recent pulse.
+ * Returns the period of the most recent Encoder pulse in seconds.
+ * This method compenstates for the decoding type.
+ *
+ * @deprecated Use GetRate() in favor of this method. This returns unscaled periods and GetRate() scales using value from SetDistancePerPulse().
+ *
+ * @return Period in seconds of the most recent pulse.
+ */
+double getEncoderPeriod(void* encoder_pointer, int32_t *status) {
+ Encoder* encoder = (Encoder*) encoder_pointer;
+ tEncoder::tTimerOutput output = encoder->encoder->readTimerOutput(status);
+ double value;
+ if (output.Stalled) {
+ // Return infinity
+ double zero = 0.0;
+ value = 1.0 / zero;
+ } else {
+ // output.Period is a fixed point number that counts by 2 (24 bits, 25 integer bits)
+ value = (double)(output.Period << 1) / (double)output.Count;
+ }
+ double measuredPeriod = value * 2.5e-8;
+ return measuredPeriod / DECODING_SCALING_FACTOR;
+}
+
+/**
+ * Sets the maximum period for stopped detection.
+ * Sets the value that represents the maximum period of the Encoder before it will assume
+ * that the attached device is stopped. This timeout allows users to determine if the wheels or
+ * other shaft has stopped rotating.
+ * This method compensates for the decoding type.
+ *
+ * @deprecated Use SetMinRate() in favor of this method. This takes unscaled periods and SetMinRate() scales using value from SetDistancePerPulse().
+ *
+ * @param maxPeriod The maximum time between rising and falling edges before the FPGA will
+ * report the device stopped. This is expressed in seconds.
+ */
+void setEncoderMaxPeriod(void* encoder_pointer, double maxPeriod, int32_t *status) {
+ Encoder* encoder = (Encoder*) encoder_pointer;
+ encoder->encoder->writeTimerConfig_StallPeriod((uint32_t)(maxPeriod * 4.0e8 * DECODING_SCALING_FACTOR), status);
+}
+
+/**
+ * Determine if the encoder is stopped.
+ * Using the MaxPeriod value, a boolean is returned that is true if the encoder is considered
+ * stopped and false if it is still moving. A stopped encoder is one where the most recent pulse
+ * width exceeds the MaxPeriod.
+ * @return True if the encoder is considered stopped.
+ */
+bool getEncoderStopped(void* encoder_pointer, int32_t *status) {
+ Encoder* encoder = (Encoder*) encoder_pointer;
+ return encoder->encoder->readTimerOutput_Stalled(status) != 0;
+}
+
+/**
+ * The last direction the encoder value changed.
+ * @return The last direction the encoder value changed.
+ */
+bool getEncoderDirection(void* encoder_pointer, int32_t *status) {
+ Encoder* encoder = (Encoder*) encoder_pointer;
+ return encoder->encoder->readOutput_Direction(status);
+}
+
+/**
+ * Set the direction sensing for this encoder.
+ * This sets the direction sensing on the encoder so that it could count in the correct
+ * software direction regardless of the mounting.
+ * @param reverseDirection true if the encoder direction should be reversed
+ */
+void setEncoderReverseDirection(void* encoder_pointer, bool reverseDirection, int32_t *status) {
+ Encoder* encoder = (Encoder*) encoder_pointer;
+ encoder->encoder->writeConfig_Reverse(reverseDirection, status);
+}
+
+/**
+ * Set the Samples to Average which specifies the number of samples of the timer to
+ * average when calculating the period. Perform averaging to account for
+ * mechanical imperfections or as oversampling to increase resolution.
+ * @param samplesToAverage The number of samples to average from 1 to 127.
+ */
+void setEncoderSamplesToAverage(void* encoder_pointer, uint32_t samplesToAverage, int32_t *status) {
+ Encoder* encoder = (Encoder*) encoder_pointer;
+ if (samplesToAverage < 1 || samplesToAverage > 127) {
+ *status = PARAMETER_OUT_OF_RANGE;
+ }
+ encoder->encoder->writeTimerConfig_AverageSize(samplesToAverage, status);
+}
+
+/**
+ * Get the Samples to Average which specifies the number of samples of the timer to
+ * average when calculating the period. Perform averaging to account for
+ * mechanical imperfections or as oversampling to increase resolution.
+ * @return SamplesToAverage The number of samples being averaged (from 1 to 127)
+ */
+uint32_t getEncoderSamplesToAverage(void* encoder_pointer, int32_t *status) {
+ Encoder* encoder = (Encoder*) encoder_pointer;
+ return encoder->encoder->readTimerConfig_AverageSize(status);
+}
+
+/**
+ * Set an index source for an encoder, which is an input that resets the
+ * encoder's count.
+ */
+void setEncoderIndexSource(void *encoder_pointer, uint32_t pin, bool analogTrigger, bool activeHigh,
+ bool edgeSensitive, int32_t *status) {
+ Encoder* encoder = (Encoder*) encoder_pointer;
+ encoder->encoder->writeConfig_IndexSource_Channel((unsigned char)pin, status);
+ encoder->encoder->writeConfig_IndexSource_Module((unsigned char)0, status);
+ encoder->encoder->writeConfig_IndexSource_AnalogTrigger(analogTrigger, status);
+ encoder->encoder->writeConfig_IndexActiveHigh(activeHigh, status);
+ encoder->encoder->writeConfig_IndexEdgeSensitive(edgeSensitive, status);
+}
+
+/**
+ * Get the loop timing of the PWM system
+ *
+ * @return The loop time
+ */
+uint16_t getLoopTiming(int32_t *status) {
+ return pwmSystem->readLoopTiming(status);
+}
+
+/*
+ * Initialize the spi port. Opens the port if necessary and saves the handle.
+ * If opening the MXP port, also sets up the pin functions appropriately
+ * @param port The number of the port to use. 0-3 for Onboard CS0-CS2, 4 for MXP
+ */
+void spiInitialize(uint8_t port, int32_t *status) {
+ if(spiSystem == NULL)
+ spiSystem = tSPI::create(status);
+ if(spiGetHandle(port) !=0 ) return;
+ switch(port){
+ case 0:
+ spiSetHandle(0, spilib_open("/dev/spidev0.0"));
+ break;
+ case 1:
+ spiSetHandle(1, spilib_open("/dev/spidev0.1"));
+ break;
+ case 2:
+ spiSetHandle(2, spilib_open("/dev/spidev0.2"));
+ break;
+ case 3:
+ spiSetHandle(3, spilib_open("/dev/spidev0.3"));
+ break;
+ case 4:
+ initializeDigital(status);
+ if(!allocateDIO(getPort(14), false, status)){printf("Failed to allocate DIO 14\n"); return;}
+ if(!allocateDIO(getPort(15), false, status)) {printf("Failed to allocate DIO 15\n"); return;}
+ if(!allocateDIO(getPort(16), true, status)) {printf("Failed to allocate DIO 16\n"); return;}
+ if(!allocateDIO(getPort(17), false, status)) {printf("Failed to allocate DIO 17\n"); return;}
+ digitalSystem->writeEnableMXPSpecialFunction(digitalSystem->readEnableMXPSpecialFunction(status)|0x00F0, status);
+ spiSetHandle(4, spilib_open("/dev/spidev1.0"));
+ break;
+ default:
+ break;
+ }
+ return;
+}
+
+/**
+ * Generic transaction.
+ *
+ * This is a lower-level interface to the spi hardware giving you more control over each transaction.
+ *
+ * @param port The number of the port to use. 0-3 for Onboard CS0-CS2, 4 for MXP
+ * @param dataToSend Buffer of data to send as part of the transaction.
+ * @param dataReceived Buffer to read data into.
+ * @param size Number of bytes to transfer. [0..7]
+ * @return Number of bytes transferred, -1 for error
+ */
+int32_t spiTransaction(uint8_t port, uint8_t *dataToSend, uint8_t *dataReceived, uint8_t size)
+{
+ ::std::unique_lock<ReentrantMutex> sync(spiGetSemaphore(port));
+ return spilib_writeread(spiGetHandle(port), (const char*) dataToSend, (char*) dataReceived, (int32_t) size);
+}
+
+/**
+ * Execute a write transaction with the device.
+ *
+ * Write to a device and wait until the transaction is complete.
+ *
+ * @param port The number of the port to use. 0-3 for Onboard CS0-CS2, 4 for MXP
+ * @param datToSend The data to write to the register on the device.
+ * @param sendSize The number of bytes to be written
+ * @return The number of bytes written. -1 for an error
+ */
+int32_t spiWrite(uint8_t port, uint8_t* dataToSend, uint8_t sendSize)
+{
+ ::std::unique_lock<ReentrantMutex> sync(spiGetSemaphore(port));
+ return spilib_write(spiGetHandle(port), (const char*) dataToSend, (int32_t) sendSize);
+}
+
+/**
+ * Execute a read from the device.
+ *
+ * This methdod does not write any data out to the device
+ * Most spi devices will require a register address to be written before
+ * they begin returning data
+ *
+ * @param port The number of the port to use. 0-3 for Onboard CS0-CS2, 4 for MXP
+ * @param buffer A pointer to the array of bytes to store the data read from the device.
+ * @param count The number of bytes to read in the transaction. [1..7]
+ * @return Number of bytes read. -1 for error.
+ */
+int32_t spiRead(uint8_t port, uint8_t *buffer, uint8_t count)
+{
+ ::std::unique_lock<ReentrantMutex> sync(spiGetSemaphore(port));
+ return spilib_read(spiGetHandle(port), (char*) buffer, (int32_t) count);
+}
+
+/**
+ * Close the SPI port
+ *
+ * @param port The number of the port to use. 0-3 for Onboard CS0-CS2, 4 for MXP
+ */
+void spiClose(uint8_t port) {
+ ::std::unique_lock<ReentrantMutex> sync(spiGetSemaphore(port));
+ spilib_close(spiGetHandle(port));
+ spiSetHandle(port, 0);
+ return;
+}
+
+/**
+ * Set the clock speed for the SPI bus.
+ *
+ * @param port The number of the port to use. 0-3 for Onboard CS0-CS2, 4 for MXP
+ * @param speed The speed in Hz (0-1MHz)
+ */
+void spiSetSpeed(uint8_t port, uint32_t speed) {
+ ::std::unique_lock<ReentrantMutex> sync(spiGetSemaphore(port));
+ spilib_setspeed(spiGetHandle(port), speed);
+}
+
+/**
+ * Set the SPI options
+ *
+ * @param port The number of the port to use. 0-3 for Onboard CS0-CS2, 4 for MXP
+ * @param msb_first True to write the MSB first, False for LSB first
+ * @param sample_on_trailing True to sample on the trailing edge, False to sample on the leading edge
+ * @param clk_idle_high True to set the clock to active low, False to set the clock active high
+ */
+void spiSetOpts(uint8_t port, int msb_first, int sample_on_trailing, int clk_idle_high) {
+ ::std::unique_lock<ReentrantMutex> sync(spiGetSemaphore(port));
+ spilib_setopts(spiGetHandle(port), msb_first, sample_on_trailing, clk_idle_high);
+}
+
+/**
+ * Set the CS Active high for a SPI port
+ *
+ * @param port The number of the port to use. 0-3 for Onboard CS0-CS2, 4 for MXP
+ */
+void spiSetChipSelectActiveHigh(uint8_t port, int32_t *status){
+ ::std::unique_lock<ReentrantMutex> sync(spiGetSemaphore(port));
+ if(port < 4)
+ {
+ spiSystem->writeChipSelectActiveHigh_Hdr(spiSystem->readChipSelectActiveHigh_Hdr(status) | (1<<port), status);
+ }
+ else
+ {
+ spiSystem->writeChipSelectActiveHigh_MXP(1, status);
+ }
+}
+
+/**
+ * Set the CS Active low for a SPI port
+ *
+ * @param port The number of the port to use. 0-3 for Onboard CS0-CS2, 4 for MXP
+ */
+void spiSetChipSelectActiveLow(uint8_t port, int32_t *status){
+ ::std::unique_lock<ReentrantMutex> sync(spiGetSemaphore(port));
+ if(port < 4)
+ {
+ spiSystem->writeChipSelectActiveHigh_Hdr(spiSystem->readChipSelectActiveHigh_Hdr(status) & ~(1<<port), status);
+ }
+ else
+ {
+ spiSystem->writeChipSelectActiveHigh_MXP(0, status);
+ }
+}
+
+/**
+ * Get the stored handle for a SPI port
+ *
+ * @param port The number of the port to use. 0-3 for Onboard CS0-CS2, 4 for MXP
+ * @return The stored handle for the SPI port. 0 represents no stored handle.
+ */
+int32_t spiGetHandle(uint8_t port){
+ ::std::unique_lock<ReentrantMutex> sync(spiGetSemaphore(port));
+ switch(port){
+ case 0:
+ return m_spiCS0Handle;
+ case 1:
+ return m_spiCS1Handle;
+ case 2:
+ return m_spiCS2Handle;
+ case 3:
+ return m_spiCS3Handle;
+ case 4:
+ return m_spiMXPHandle;
+ default:
+ return 0;
+ }
+}
+
+/**
+ * Set the stored handle for a SPI port
+ *
+ * @param port The number of the port to use. 0-3 for Onboard CS0-CS2, 4 for MXP.
+ * @param handle The value of the handle for the port.
+ */
+void spiSetHandle(uint8_t port, int32_t handle){
+ ::std::unique_lock<ReentrantMutex> sync(spiGetSemaphore(port));
+ switch(port){
+ case 0:
+ m_spiCS0Handle = handle;
+ break;
+ case 1:
+ m_spiCS1Handle = handle;
+ break;
+ case 2:
+ m_spiCS2Handle = handle;
+ break;
+ case 3:
+ m_spiCS3Handle = handle;
+ break;
+ case 4:
+ m_spiMXPHandle = handle;
+ break;
+ default:
+ break;
+ }
+}
+
+/**
+ * Get the semaphore for a SPI port
+ *
+ * @param port The number of the port to use. 0-3 for Onboard CS0-CS2, 4 for MXP
+ * @return The semaphore for the SPI port.
+ */
+ReentrantMutex& spiGetSemaphore(uint8_t port) {
+ if(port < 4)
+ return spiOnboardSemaphore;
+ else
+ return spiMXPSemaphore;
+}
+
+/*
+ * Initialize the I2C port. Opens the port if necessary and saves the handle.
+ * If opening the MXP port, also sets up the pin functions appropriately
+ * @param port The port to open, 0 for the on-board, 1 for the MXP.
+ */
+void i2CInitialize(uint8_t port, int32_t *status) {
+ initializeDigital(status);
+
+ if(port > 1)
+ {
+ //Set port out of range error here
+ return;
+ }
+
+ ReentrantMutex &lock = port == 0 ? digitalI2COnBoardSemaphore : digitalI2CMXPSemaphore;
+ {
+ ::std::unique_lock<ReentrantMutex> sync(lock);
+ if(port == 0) {
+ i2COnboardObjCount++;
+ if (i2COnBoardHandle > 0) return;
+ i2COnBoardHandle = i2clib_open("/dev/i2c-2");
+ } else if(port == 1) {
+ i2CMXPObjCount++;
+ if (i2CMXPHandle > 0) return;
+ if(!allocateDIO(getPort(24), false, status)) return;
+ if(!allocateDIO(getPort(25), false, status)) return;
+ digitalSystem->writeEnableMXPSpecialFunction(digitalSystem->readEnableMXPSpecialFunction(status)|0xC000, status);
+ i2CMXPHandle = i2clib_open("/dev/i2c-1");
+ }
+ return;
+ }
+}
+
+/**
+ * Generic transaction.
+ *
+ * This is a lower-level interface to the I2C hardware giving you more control over each transaction.
+ *
+ * @param dataToSend Buffer of data to send as part of the transaction.
+ * @param sendSize Number of bytes to send as part of the transaction. [0..6]
+ * @param dataReceived Buffer to read data into.
+ * @param receiveSize Number of bytes to read from the device. [0..7]
+ * @return Transfer Aborted... false for success, true for aborted.
+ */
+int32_t i2CTransaction(uint8_t port, uint8_t deviceAddress, uint8_t *dataToSend, uint8_t sendSize, uint8_t *dataReceived, uint8_t receiveSize)
+{
+ if(port > 1) {
+ //Set port out of range error here
+ return -1;
+ }
+ /*if (sendSize > 6) // Optional, provides better error message. TODO: Are these limits still right? Implement error. Check for null buffer
+ {
+ wpi_setWPIErrorWithContext(ParameterOutOfRange, "sendSize");
+ return true;
+ }
+ if (receiveSize > 7) // Optional, provides better error message.
+ {
+ wpi_setWPIErrorWithContext(ParameterOutOfRange, "receiveSize");
+ return true;
+ }*/
+ int32_t handle = port == 0 ? i2COnBoardHandle:i2CMXPHandle;
+ ReentrantMutex &lock = port == 0 ? digitalI2COnBoardSemaphore : digitalI2CMXPSemaphore;
+
+ {
+ ::std::unique_lock<ReentrantMutex> sync(lock);
+ return i2clib_writeread(handle, deviceAddress, (const char*) dataToSend, (int32_t) sendSize, (char*) dataReceived, (int32_t) receiveSize);
+ }
+}
+
+/**
+ * Execute a write transaction with the device.
+ *
+ * Write a single byte to a register on a device and wait until the
+ * transaction is complete.
+ *
+ * @param registerAddress The address of the register on the device to be written.
+ * @param data The byte to write to the register on the device.
+ * @return Transfer Aborted... false for success, true for aborted.
+ */
+int32_t i2CWrite(uint8_t port, uint8_t deviceAddress, uint8_t* dataToSend, uint8_t sendSize)
+{
+ if(port > 1) {
+ //Set port out of range error here
+ return -1;
+ }
+ /*if (sendSize > 6) // Optional, provides better error message. TODO: Are these limits still right? Implement error. Check for null buffer
+ {
+ wpi_setWPIErrorWithContext(ParameterOutOfRange, "sendSize");
+ return true;
+ }*/
+ int32_t handle = port == 0 ? i2COnBoardHandle:i2CMXPHandle;
+ ReentrantMutex &lock = port == 0 ? digitalI2COnBoardSemaphore : digitalI2CMXPSemaphore;
+ {
+ ::std::unique_lock<ReentrantMutex> sync(lock);
+ return i2clib_write(handle, deviceAddress, (const char*) dataToSend, (int32_t) sendSize);
+ }
+}
+
+/**
+ * Execute a read transaction with the device.
+ *
+ * Read 1 to 7 bytes from a device.
+ * Most I2C devices will auto-increment the register pointer internally
+ * allowing you to read up to 7 consecutive registers on a device in a
+ * single transaction.
+ *
+ * @param registerAddress The register to read first in the transaction.
+ * @param count The number of bytes to read in the transaction. [1..7]
+ * @param buffer A pointer to the array of bytes to store the data read from the device.
+ * @return Transfer Aborted... false for success, true for aborted.
+ */
+int32_t i2CRead(uint8_t port, uint8_t deviceAddress, uint8_t *buffer, uint8_t count)
+{
+ if(port > 1) {
+ //Set port out of range error here
+ return -1;
+ }
+ /* if (count < 1 || count > 7) Todo: Are these limits still right? Implement error
+ {
+ wpi_setWPIErrorWithContext(ParameterOutOfRange, "count");
+ return true;
+ }
+ if (buffer == NULL)
+ {
+ wpi_setWPIErrorWithContext(NullParameter, "buffer");
+ return true;
+ }*/
+ int32_t handle = port == 0 ? i2COnBoardHandle:i2CMXPHandle;
+ ReentrantMutex &lock = port == 0 ? digitalI2COnBoardSemaphore : digitalI2CMXPSemaphore;
+ {
+ ::std::unique_lock<ReentrantMutex> sync(lock);
+ return i2clib_read(handle, deviceAddress, (char*) buffer, (int32_t) count);
+ }
+
+}
+
+void i2CClose(uint8_t port) {
+ if(port > 1) {
+ //Set port out of range error here
+ return;
+ }
+ ReentrantMutex &lock = port == 0 ? digitalI2COnBoardSemaphore : digitalI2CMXPSemaphore;
+ {
+ ::std::unique_lock<ReentrantMutex> sync(lock);
+ if((port == 0 ? i2COnboardObjCount--:i2CMXPObjCount--) == 0) {
+ int32_t handle = port == 0 ? i2COnBoardHandle:i2CMXPHandle;
+ i2clib_close(handle);
+ }
+ }
+ return;
+}
diff --git a/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/FRC_FPGA_ChipObject_Aliases.h b/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/FRC_FPGA_ChipObject_Aliases.h
new file mode 100644
index 0000000..d9fba25
--- /dev/null
+++ b/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/FRC_FPGA_ChipObject_Aliases.h
@@ -0,0 +1,10 @@
+// Copyright (c) National Instruments 2008. All Rights Reserved.
+// Do Not Edit... this file is generated!
+
+#ifndef __FRC_FPGA_ChipObject_Aliases_h__
+#define __FRC_FPGA_ChipObject_Aliases_h__
+
+#define nRuntimeFPGANamespace nFRC_2012_1_6_4
+#define nInvariantFPGANamespace nFRC_C0EF_1_1_0
+
+#endif // __FRC_FPGA_ChipObject_Aliases_h__
diff --git a/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/RoboRIO_FRC_ChipObject_Aliases.h b/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/RoboRIO_FRC_ChipObject_Aliases.h
new file mode 100644
index 0000000..7bae082
--- /dev/null
+++ b/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/RoboRIO_FRC_ChipObject_Aliases.h
@@ -0,0 +1,9 @@
+// Copyright (c) National Instruments 2008. All Rights Reserved.
+// Do Not Edit... this file is generated!
+
+#ifndef __RoboRIO_FRC_ChipObject_Aliases_h__
+#define __RoboRIO_FRC_ChipObject_Aliases_h__
+
+#define nRoboRIO_FPGANamespace nFRC_2015_1_0_A
+
+#endif // __RoboRIO_FRC_ChipObject_Aliases_h__
diff --git a/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/fpgainterfacecapi/NiFpga.h b/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/fpgainterfacecapi/NiFpga.h
new file mode 100644
index 0000000..27b0665
--- /dev/null
+++ b/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/fpgainterfacecapi/NiFpga.h
@@ -0,0 +1,2450 @@
+/*
+ * FPGA Interface C API 14.0 header file.
+ *
+ * Copyright (c) 2014,
+ * National Instruments Corporation.
+ * All rights reserved.
+ */
+
+#ifndef __NiFpga_h__
+#define __NiFpga_h__
+
+/*
+ * Determine platform details.
+ */
+#if defined(_M_IX86) \
+ || defined(_M_X64) \
+ || defined(_M_AMD64) \
+ || defined(i386) \
+ || defined(__i386) \
+ || defined(__i386__) \
+ || defined(__i486__) \
+ || defined(__i586__) \
+ || defined(__i686__) \
+ || defined(__amd64__) \
+ || defined(__amd64) \
+ || defined(__x86_64__) \
+ || defined(__x86_64) \
+ || defined(__IA32__) \
+ || defined(_X86_) \
+ || defined(__THW_INTEL__) \
+ || defined(__I86__) \
+ || defined(__INTEL__) \
+ || defined(__X86__) \
+ || defined(__386__) \
+ || defined(__I86__) \
+ || defined(M_I386) \
+ || defined(M_I86) \
+ || defined(_M_I386) \
+ || defined(_M_I86)
+ #if defined(_WIN32) \
+ || defined(_WIN64) \
+ || defined(__WIN32__) \
+ || defined(__TOS_WIN__) \
+ || defined(__WINDOWS__) \
+ || defined(_WINDOWS) \
+ || defined(__WINDOWS_386__) \
+ || defined(__CYGWIN__)
+ /* Either Windows or Phar Lap ETS. */
+ #define NiFpga_Windows 1
+ #elif defined(__linux__) \
+ || defined(__linux) \
+ || defined(linux) \
+ || defined(__gnu_linux__)
+ #define NiFpga_Linux 1
+ #elif defined(__APPLE__) && defined(__MACH__)
+ #define NiFpga_MacOsX 1
+ #else
+ #error Unsupported OS.
+ #endif
+#elif defined(__powerpc) \
+ || defined(__powerpc__) \
+ || defined(__POWERPC__) \
+ || defined(__ppc__) \
+ || defined(__PPC) \
+ || defined(_M_PPC) \
+ || defined(_ARCH_PPC) \
+ || defined(__PPC__) \
+ || defined(__ppc)
+ #if defined(__vxworks)
+ #define NiFpga_VxWorks 1
+ #else
+ #error Unsupported OS.
+ #endif
+#elif defined(__arm__) \
+ || defined(__thumb__) \
+ || defined(__TARGET_ARCH_ARM) \
+ || defined(__TARGET_ARCH_THUMB) \
+ || defined(_ARM) \
+ || defined(_M_ARM) \
+ || defined(_M_ARMT)
+#if defined(__linux__) \
+ || defined(__linux) \
+ || defined(linux) \
+ || defined(__gnu_linux__)
+ #define NiFpga_Linux 1
+#else
+ #error Unsupported OS.
+ #endif
+#else
+ #error Unsupported architecture.
+#endif
+
+/*
+ * Determine compiler.
+ */
+#if defined(_MSC_VER)
+ #define NiFpga_Msvc 1
+#elif defined(__GNUC__)
+ #define NiFpga_Gcc 1
+#elif defined(_CVI_) && !defined(_TPC_)
+ #define NiFpga_Cvi 1
+ /* Enables CVI Library Protection Errors. */
+ #pragma EnableLibraryRuntimeChecking
+#else
+ /* Unknown compiler. */
+#endif
+
+/*
+ * Determine compliance with different C/C++ language standards.
+ */
+#if defined(__cplusplus)
+ #define NiFpga_Cpp 1
+ #if __cplusplus >= 199707L
+ #define NiFpga_Cpp98 1
+ #if __cplusplus >= 201103L
+ #define NiFpga_Cpp11 1
+ #endif
+ #endif
+#endif
+#if defined(__STDC__)
+ #define NiFpga_C89 1
+ #if defined(__STDC_VERSION__)
+ #define NiFpga_C90 1
+ #if __STDC_VERSION__ >= 199409L
+ #define NiFpga_C94 1
+ #if __STDC_VERSION__ >= 199901L
+ #define NiFpga_C99 1
+ #if __STDC_VERSION__ >= 201112L
+ #define NiFpga_C11 1
+ #endif
+ #endif
+ #endif
+ #endif
+#endif
+
+/*
+ * Determine ability to inline functions.
+ */
+#if NiFpga_Cpp || NiFpga_C99
+ /* The inline keyword exists in C++ and C99. */
+#define NiFpga_Inline inline
+#elif NiFpga_Msvc
+ /* Visual C++ (at least since 6.0) also supports an alternate keyword. */
+ #define NiFpga_Inline __inline
+#elif NiFpga_Gcc
+ /* GCC (at least since 2.95.2) also supports an alternate keyword. */
+ #define NiFpga_Inline __inline__
+#elif !defined(NiFpga_Inline)
+ /*
+ * Disable inlining if inline support is unknown. To manually enable
+ * inlining, #define the following macro before #including NiFpga.h:
+ *
+ * #define NiFpga_Inline inline
+ */
+ #define NiFpga_Inline
+#endif
+
+/*
+ * Define exact-width integer types, if they have not already been defined.
+ */
+#if NiFpga_ExactWidthIntegerTypesDefined \
+ || defined(_STDINT) \
+ || defined(_STDINT_H) \
+ || defined(_STDINT_H_) \
+ || defined(_INTTYPES_H) \
+ || defined(_INTTYPES_H_) \
+ || defined(_SYS_STDINT_H) \
+ || defined(_SYS_STDINT_H_) \
+ || defined(_SYS_INTTYPES_H) \
+ || defined(_SYS_INTTYPES_H_) \
+ || defined(_STDINT_H_INCLUDED) \
+ || defined(_MSC_STDINT_H_) \
+ || defined(_PSTDINT_H_INCLUDED)
+ /* Assume that exact-width integer types have already been defined. */
+#elif NiFpga_VxWorks
+ /* VxWorks (at least 6.3 and earlier) did not have stdint.h. */
+ #include <types/vxTypes.h>
+#elif NiFpga_C99 \
+ || NiFpga_Gcc /* GCC (at least since 3.0) has a stdint.h. */ \
+ || defined(HAVE_STDINT_H)
+ /* Assume that stdint.h can be included. */
+ #include <stdint.h>
+#elif NiFpga_Msvc \
+ || NiFpga_Cvi
+ /* Manually define exact-width integer types. */
+ typedef signed char int8_t;
+ typedef unsigned char uint8_t;
+ typedef short int16_t;
+ typedef unsigned short uint16_t;
+ typedef int int32_t;
+ typedef unsigned int uint32_t;
+ typedef __int64 int64_t;
+ typedef unsigned __int64 uint64_t;
+#else
+ /*
+ * Exact-width integer types must be defined by the user, and the following
+ * macro must be #defined, before #including NiFpga.h:
+ *
+ * #define NiFpga_ExactWidthIntegerTypesDefined 1
+ */
+ #error Exact-width integer types must be defined by the user. See comment.
+#endif
+
+/* Included for definition of size_t. */
+#include <stddef.h>
+
+#if NiFpga_Cpp
+extern "C"
+{
+#endif
+
+/**
+ * A boolean value; either NiFpga_False or NiFpga_True.
+ */
+typedef uint8_t NiFpga_Bool;
+
+/**
+ * Represents a false condition.
+ */
+static const NiFpga_Bool NiFpga_False = 0;
+
+/**
+ * Represents a true condition.
+ */
+static const NiFpga_Bool NiFpga_True = 1;
+
+/**
+ * Represents the resulting status of a function call through its return value.
+ * 0 is success, negative values are errors, and positive values are warnings.
+ */
+typedef int32_t NiFpga_Status;
+
+/**
+ * No errors or warnings.
+ */
+static const NiFpga_Status NiFpga_Status_Success = 0;
+
+/**
+ * The timeout expired before the FIFO operation could complete.
+ */
+static const NiFpga_Status NiFpga_Status_FifoTimeout = -50400;
+
+/**
+ * No transfer is in progress because the transfer was aborted by the client.
+ * The operation could not be completed as specified.
+ */
+static const NiFpga_Status NiFpga_Status_TransferAborted = -50405;
+
+/**
+ * A memory allocation failed. Try again after rebooting.
+ */
+static const NiFpga_Status NiFpga_Status_MemoryFull = -52000;
+
+/**
+ * An unexpected software error occurred.
+ */
+static const NiFpga_Status NiFpga_Status_SoftwareFault = -52003;
+
+/**
+ * A parameter to a function was not valid. This could be a NULL pointer, a bad
+ * value, etc.
+ */
+static const NiFpga_Status NiFpga_Status_InvalidParameter = -52005;
+
+/**
+ * A required resource was not found. The NiFpga.* library, the RIO resource, or
+ * some other resource may be missing.
+ */
+static const NiFpga_Status NiFpga_Status_ResourceNotFound = -52006;
+
+/**
+ * A required resource was not properly initialized. This could occur if
+ * NiFpga_Initialize was not called or a required NiFpga_IrqContext was not
+ * reserved.
+ */
+static const NiFpga_Status NiFpga_Status_ResourceNotInitialized = -52010;
+
+/**
+ * A hardware failure has occurred. The operation could not be completed as
+ * specified.
+ */
+static const NiFpga_Status NiFpga_Status_HardwareFault = -52018;
+
+/**
+ * The FPGA is already running.
+ */
+static const NiFpga_Status NiFpga_Status_FpgaAlreadyRunning = -61003;
+
+/**
+ * An error occurred downloading the VI to the FPGA device. Verify that
+ * the target is connected and powered and that the resource of the target
+ * is properly configured.
+ */
+static const NiFpga_Status NiFpga_Status_DownloadError = -61018;
+
+/**
+ * The bitfile was not compiled for the specified resource's device type.
+ */
+static const NiFpga_Status NiFpga_Status_DeviceTypeMismatch = -61024;
+
+/**
+ * An error was detected in the communication between the host computer and the
+ * FPGA target.
+ */
+static const NiFpga_Status NiFpga_Status_CommunicationTimeout = -61046;
+
+/**
+ * The timeout expired before any of the IRQs were asserted.
+ */
+static const NiFpga_Status NiFpga_Status_IrqTimeout = -61060;
+
+/**
+ * The specified bitfile is invalid or corrupt.
+ */
+static const NiFpga_Status NiFpga_Status_CorruptBitfile = -61070;
+
+/**
+ * The requested FIFO depth is invalid. It is either 0 or an amount not
+ * supported by the hardware.
+ */
+static const NiFpga_Status NiFpga_Status_BadDepth = -61072;
+
+/**
+ * The number of FIFO elements is invalid. Either the number is greater than the
+ * depth of the host memory DMA FIFO, or more elements were requested for
+ * release than had been acquired.
+ */
+static const NiFpga_Status NiFpga_Status_BadReadWriteCount = -61073;
+
+/**
+ * A hardware clocking error occurred. A derived clock lost lock with its base
+ * clock during the execution of the LabVIEW FPGA VI. If any base clocks with
+ * derived clocks are referencing an external source, make sure that the
+ * external source is connected and within the supported frequency, jitter,
+ * accuracy, duty cycle, and voltage specifications. Also verify that the
+ * characteristics of the base clock match the configuration specified in the
+ * FPGA Base Clock Properties. If all base clocks with derived clocks are
+ * generated from free-running, on-board sources, please contact National
+ * Instruments technical support at ni.com/support.
+ */
+static const NiFpga_Status NiFpga_Status_ClockLostLock = -61083;
+
+/**
+ * The operation could not be performed because the FPGA is busy. Stop all
+ * activities on the FPGA before requesting this operation. If the target is in
+ * Scan Interface programming mode, put it in FPGA Interface programming mode.
+ */
+static const NiFpga_Status NiFpga_Status_FpgaBusy = -61141;
+
+/**
+ * The operation could not be performed because the FPGA is busy operating in
+ * FPGA Interface C API mode. Stop all activities on the FPGA before requesting
+ * this operation.
+ */
+static const NiFpga_Status NiFpga_Status_FpgaBusyFpgaInterfaceCApi = -61200;
+
+/**
+ * The chassis is in Scan Interface programming mode. In order to run FPGA VIs,
+ * you must go to the chassis properties page, select FPGA programming mode, and
+ * deploy settings.
+ */
+static const NiFpga_Status NiFpga_Status_FpgaBusyScanInterface = -61201;
+
+/**
+ * The operation could not be performed because the FPGA is busy operating in
+ * FPGA Interface mode. Stop all activities on the FPGA before requesting this
+ * operation.
+ */
+static const NiFpga_Status NiFpga_Status_FpgaBusyFpgaInterface = -61202;
+
+/**
+ * The operation could not be performed because the FPGA is busy operating in
+ * Interactive mode. Stop all activities on the FPGA before requesting this
+ * operation.
+ */
+static const NiFpga_Status NiFpga_Status_FpgaBusyInteractive = -61203;
+
+/**
+ * The operation could not be performed because the FPGA is busy operating in
+ * Emulation mode. Stop all activities on the FPGA before requesting this
+ * operation.
+ */
+static const NiFpga_Status NiFpga_Status_FpgaBusyEmulation = -61204;
+
+/**
+ * LabVIEW FPGA does not support the Reset method for bitfiles that allow
+ * removal of implicit enable signals in single-cycle Timed Loops.
+ */
+static const NiFpga_Status NiFpga_Status_ResetCalledWithImplicitEnableRemoval = -61211;
+
+/**
+ * LabVIEW FPGA does not support the Abort method for bitfiles that allow
+ * removal of implicit enable signals in single-cycle Timed Loops.
+ */
+static const NiFpga_Status NiFpga_Status_AbortCalledWithImplicitEnableRemoval = -61212;
+
+/**
+ * LabVIEW FPGA does not support Close and Reset if Last Reference for bitfiles
+ * that allow removal of implicit enable signals in single-cycle Timed Loops.
+ * Pass the NiFpga_CloseAttribute_NoResetIfLastSession attribute to NiFpga_Close
+ * instead of 0.
+ */
+static const NiFpga_Status NiFpga_Status_CloseAndResetCalledWithImplicitEnableRemoval = -61213;
+
+/**
+ * For bitfiles that allow removal of implicit enable signals in single-cycle
+ * Timed Loops, LabVIEW FPGA does not support this method prior to running the
+ * bitfile.
+ */
+static const NiFpga_Status NiFpga_Status_ImplicitEnableRemovalButNotYetRun = -61214;
+
+/**
+ * Bitfiles that allow removal of implicit enable signals in single-cycle Timed
+ * Loops can run only once. Download the bitfile again before re-running the VI.
+ */
+static const NiFpga_Status NiFpga_Status_RunAfterStoppedCalledWithImplicitEnableRemoval = -61215;
+
+/**
+ * A gated clock has violated the handshaking protocol. If you are using
+ * external gated clocks, ensure that they follow the required clock gating
+ * protocol. If you are generating your clocks internally, please contact
+ * National Instruments Technical Support.
+ */
+static const NiFpga_Status NiFpga_Status_GatedClockHandshakingViolation = -61216;
+
+/**
+ * The number of elements requested must be less than or equal to the number of
+ * unacquired elements left in the host memory DMA FIFO. There are currently
+ * fewer unacquired elements left in the FIFO than are being requested. Release
+ * some acquired elements before acquiring more elements.
+ */
+static const NiFpga_Status NiFpga_Status_ElementsNotPermissibleToBeAcquired = -61219;
+
+/**
+ * An unexpected internal error occurred.
+ */
+static const NiFpga_Status NiFpga_Status_InternalError = -61499;
+
+/**
+ * The NI-RIO driver was unable to allocate memory for a FIFO. This can happen
+ * when the combined depth of all DMA FIFOs exceeds the maximum depth for the
+ * controller, or when the controller runs out of system memory. You may be able
+ * to reconfigure the controller with a greater maximum FIFO depth. For more
+ * information, refer to the NI KnowledgeBase article 65OF2ERQ.
+ */
+static const NiFpga_Status NiFpga_Status_TotalDmaFifoDepthExceeded = -63003;
+
+/**
+ * Access to the remote system was denied. Use MAX to check the Remote Device
+ * Access settings under Software>>NI-RIO>>NI-RIO Settings on the remote system.
+ */
+static const NiFpga_Status NiFpga_Status_AccessDenied = -63033;
+
+/**
+ * The NI-RIO software on the host is not compatible with the software on the
+ * target. Upgrade the NI-RIO software on the host in order to connect to this
+ * target.
+ */
+static const NiFpga_Status NiFpga_Status_HostVersionMismatch = -63038;
+
+/**
+ * A connection could not be established to the specified remote device. Ensure
+ * that the device is on and accessible over the network, that NI-RIO software
+ * is installed, and that the RIO server is running and properly configured.
+ */
+static const NiFpga_Status NiFpga_Status_RpcConnectionError = -63040;
+
+/**
+ * The RPC session is invalid. The target may have reset or been rebooted. Check
+ * the network connection and retry the operation.
+ */
+static const NiFpga_Status NiFpga_Status_RpcSessionError = -63043;
+
+/**
+ * The operation could not complete because another session is accessing the
+ * FIFO. Close the other session and retry.
+ */
+static const NiFpga_Status NiFpga_Status_FifoReserved = -63082;
+
+/**
+ * A Configure FIFO, Stop FIFO, Read FIFO, or Write FIFO function was called
+ * while the host had acquired elements of the FIFO. Release all acquired
+ * elements before configuring, stopping, reading, or writing.
+ */
+static const NiFpga_Status NiFpga_Status_FifoElementsCurrentlyAcquired = -63083;
+
+/**
+ * A function was called using a misaligned address. The address must be a
+ * multiple of the size of the datatype.
+ */
+static const NiFpga_Status NiFpga_Status_MisalignedAccess = -63084;
+
+/**
+ * The FPGA Read/Write Control Function is accessing a control or indicator
+ * with data that exceeds the maximum size supported on the current target.
+ * Refer to the hardware documentation for the limitations on data types for
+ * this target.
+ */
+static const NiFpga_Status NiFpga_Status_ControlOrIndicatorTooLarge = -63085;
+
+/**
+ * A valid .lvbitx bitfile is required. If you are using a valid .lvbitx
+ * bitfile, the bitfile may not be compatible with the software you are using.
+ * Determine which version of LabVIEW was used to make the bitfile, update your
+ * software to that version or later, and try again.
+ */
+static const NiFpga_Status NiFpga_Status_BitfileReadError = -63101;
+
+/**
+ * The specified signature does not match the signature of the bitfile. If the
+ * bitfile has been recompiled, regenerate the C API and rebuild the
+ * application.
+ */
+static const NiFpga_Status NiFpga_Status_SignatureMismatch = -63106;
+
+/**
+ * The bitfile you are trying to use is not compatible with the version of
+ * NI-RIO installed on the target and/or the host. Determine which versions of
+ * NI-RIO and LabVIEW were used to make the bitfile, update the software on the
+ * target and host to that version or later, and try again.
+ */
+static const NiFpga_Status NiFpga_Status_IncompatibleBitfile = -63107;
+
+/**
+ * Either the supplied resource name is invalid as a RIO resource name, or the
+ * device was not found. Use MAX to find the proper resource name for the
+ * intended device.
+ */
+static const NiFpga_Status NiFpga_Status_InvalidResourceName = -63192;
+
+/**
+ * The requested feature is not supported.
+ */
+static const NiFpga_Status NiFpga_Status_FeatureNotSupported = -63193;
+
+/**
+ * The NI-RIO software on the target system is not compatible with this
+ * software. Upgrade the NI-RIO software on the target system.
+ */
+static const NiFpga_Status NiFpga_Status_VersionMismatch = -63194;
+
+/**
+ * The session is invalid or has been closed.
+ */
+static const NiFpga_Status NiFpga_Status_InvalidSession = -63195;
+
+/**
+ * The maximum number of open FPGA sessions has been reached. Close some open
+ * sessions.
+ */
+static const NiFpga_Status NiFpga_Status_OutOfHandles = -63198;
+
+/**
+ * Tests whether a status is an error.
+ *
+ * @param status status to check for an error
+ * @return whether the status was an error
+ */
+static NiFpga_Inline NiFpga_Bool NiFpga_IsError(const NiFpga_Status status)
+{
+ return status < NiFpga_Status_Success;
+}
+
+/**
+ * Tests whether a status is not an error. Success and warnings are not errors.
+ *
+ * @param status status to check for an error
+ * @return whether the status was a success or warning
+ */
+static NiFpga_Inline NiFpga_Bool NiFpga_IsNotError(const NiFpga_Status status)
+{
+ return status >= NiFpga_Status_Success;
+}
+
+/**
+ * Conditionally sets the status to a new value. The previous status is
+ * preserved unless the new status is more of an error, which means that
+ * warnings and errors overwrite successes, and errors overwrite warnings. New
+ * errors do not overwrite older errors, and new warnings do not overwrite
+ * older warnings.
+ *
+ * @param status status to conditionally set
+ * @param newStatus new status value that may be set
+ * @return the resulting status
+ */
+static NiFpga_Inline NiFpga_Status NiFpga_MergeStatus(
+ NiFpga_Status* const status,
+ const NiFpga_Status newStatus)
+{
+ if (!status)
+ return NiFpga_Status_InvalidParameter;
+ if (NiFpga_IsNotError(*status)
+ && (*status == NiFpga_Status_Success || NiFpga_IsError(newStatus)))
+ *status = newStatus;
+ return *status;
+}
+
+/**
+ * This macro evaluates the expression only if the status is not an error. The
+ * expression must evaluate to an NiFpga_Status, such as a call to any NiFpga_*
+ * function, because the status will be set to the returned status if the
+ * expression is evaluated.
+ *
+ * You can use this macro to mimic status chaining in LabVIEW, where the status
+ * does not have to be explicitly checked after each call. Such code may look
+ * like the following example.
+ *
+ * NiFpga_Status status = NiFpga_Status_Success;
+ * NiFpga_IfIsNotError(status, NiFpga_WriteU32(...));
+ * NiFpga_IfIsNotError(status, NiFpga_WriteU32(...));
+ * NiFpga_IfIsNotError(status, NiFpga_WriteU32(...));
+ *
+ * @param status status to check for an error
+ * @param expression expression to call if the incoming status is not an error
+ */
+#define NiFpga_IfIsNotError(status, expression) \
+ if (NiFpga_IsNotError(status)) \
+ NiFpga_MergeStatus(&status, (expression)); \
+
+/**
+ * You must call this function before all other function calls. This function
+ * loads the NiFpga library so that all the other functions will work. If this
+ * function succeeds, you must call NiFpga_Finalize after all other function
+ * calls.
+ *
+ * @warning This function is not thread safe.
+ *
+ * @return result of the call
+ */
+NiFpga_Status NiFpga_Initialize(void);
+
+/**
+ * You must call this function after all other function calls if
+ * NiFpga_Initialize succeeds. This function unloads the NiFpga library.
+ *
+ * @warning This function is not thread safe.
+ *
+ * @return result of the call
+ */
+NiFpga_Status NiFpga_Finalize(void);
+
+/**
+ * A handle to an FPGA session.
+ */
+typedef uint32_t NiFpga_Session;
+
+/**
+ * Attributes that NiFpga_Open accepts.
+ */
+typedef enum
+{
+ NiFpga_OpenAttribute_NoRun = 1
+} NiFpga_OpenAttribute;
+
+/**
+ * Opens a session to the FPGA. This call ensures that the contents of the
+ * bitfile are programmed to the FPGA. The FPGA runs unless the
+ * NiFpga_OpenAttribute_NoRun attribute is used.
+ *
+ * Because different operating systems have different default current working
+ * directories for applications, you must pass an absolute path for the bitfile
+ * parameter. If you pass only the filename instead of an absolute path, the
+ * operating system may not be able to locate the bitfile. For example, the
+ * default current working directories are C:\ni-rt\system\ for Phar Lap ETS and
+ * /c/ for VxWorks. Because the generated *_Bitfile constant is a #define to a
+ * string literal, you can use C/C++ string-literal concatenation to form an
+ * absolute path. For example, if the bitfile is in the root directory of a
+ * Phar Lap ETS system, pass the following for the bitfile parameter.
+ *
+ * "C:\\" NiFpga_MyApplication_Bitfile
+ *
+ * @param bitfile path to the bitfile
+ * @param signature signature of the bitfile
+ * @param resource RIO resource string to open ("RIO0" or "rio://mysystem/RIO")
+ * @param attribute bitwise OR of any NiFpga_OpenAttributes, or 0
+ * @param session outputs the session handle, which must be closed when no
+ * longer needed
+ * @return result of the call
+ */
+NiFpga_Status NiFpga_Open(const char* bitfile,
+ const char* signature,
+ const char* resource,
+ uint32_t attribute,
+ NiFpga_Session* session);
+
+/**
+ * Attributes that NiFpga_Close accepts.
+ */
+typedef enum
+{
+ NiFpga_CloseAttribute_NoResetIfLastSession = 1
+} NiFpga_CloseAttribute;
+
+/**
+ * Closes the session to the FPGA. The FPGA resets unless either another session
+ * is still open or you use the NiFpga_CloseAttribute_NoResetIfLastSession
+ * attribute.
+ *
+ * @param session handle to a currently open session
+ * @param attribute bitwise OR of any NiFpga_CloseAttributes, or 0
+ * @return result of the call
+ */
+NiFpga_Status NiFpga_Close(NiFpga_Session session,
+ uint32_t attribute);
+
+/**
+ * Attributes that NiFpga_Run accepts.
+ */
+typedef enum
+{
+ NiFpga_RunAttribute_WaitUntilDone = 1
+} NiFpga_RunAttribute;
+
+/**
+ * Runs the FPGA VI on the target. If you use NiFpga_RunAttribute_WaitUntilDone,
+ * NiFpga_Run blocks the thread until the FPGA finishes running.
+ *
+ * @param session handle to a currently open session
+ * @param attribute bitwise OR of any NiFpga_RunAttributes, or 0
+ * @return result of the call
+ */
+NiFpga_Status NiFpga_Run(NiFpga_Session session,
+ uint32_t attribute);
+
+/**
+ * Aborts the FPGA VI.
+ *
+ * @param session handle to a currently open session
+ * @return result of the call
+ */
+NiFpga_Status NiFpga_Abort(NiFpga_Session session);
+
+/**
+ * Resets the FPGA VI.
+ *
+ * @param session handle to a currently open session
+ * @return result of the call
+ */
+NiFpga_Status NiFpga_Reset(NiFpga_Session session);
+
+/**
+ * Re-downloads the FPGA bitstream to the target.
+ *
+ * @param session handle to a currently open session
+ * @return result of the call
+ */
+NiFpga_Status NiFpga_Download(NiFpga_Session session);
+
+/**
+ * Reads a boolean value from a given indicator or control.
+ *
+ * @param session handle to a currently open session
+ * @param indicator indicator or control from which to read
+ * @param value outputs the value that was read
+ * @return result of the call
+ */
+NiFpga_Status NiFpga_ReadBool(NiFpga_Session session,
+ uint32_t indicator,
+ NiFpga_Bool* value);
+
+/**
+ * Reads a signed 8-bit integer value from a given indicator or control.
+ *
+ * @param session handle to a currently open session
+ * @param indicator indicator or control from which to read
+ * @param value outputs the value that was read
+ * @return result of the call
+ */
+NiFpga_Status NiFpga_ReadI8(NiFpga_Session session,
+ uint32_t indicator,
+ int8_t* value);
+
+/**
+ * Reads an unsigned 8-bit integer value from a given indicator or control.
+ *
+ * @param session handle to a currently open session
+ * @param indicator indicator or control from which to read
+ * @param value outputs the value that was read
+ * @return result of the call
+ */
+NiFpga_Status NiFpga_ReadU8(NiFpga_Session session,
+ uint32_t indicator,
+ uint8_t* value);
+
+/**
+ * Reads a signed 16-bit integer value from a given indicator or control.
+ *
+ * @param session handle to a currently open session
+ * @param indicator indicator or control from which to read
+ * @param value outputs the value that was read
+ * @return result of the call
+ */
+NiFpga_Status NiFpga_ReadI16(NiFpga_Session session,
+ uint32_t indicator,
+ int16_t* value);
+
+/**
+ * Reads an unsigned 16-bit integer value from a given indicator or control.
+ *
+ * @param session handle to a currently open session
+ * @param indicator indicator or control from which to read
+ * @param value outputs the value that was read
+ * @return result of the call
+ */
+NiFpga_Status NiFpga_ReadU16(NiFpga_Session session,
+ uint32_t indicator,
+ uint16_t* value);
+
+/**
+ * Reads a signed 32-bit integer value from a given indicator or control.
+ *
+ * @param session handle to a currently open session
+ * @param indicator indicator or control from which to read
+ * @param value outputs the value that was read
+ * @return result of the call
+ */
+NiFpga_Status NiFpga_ReadI32(NiFpga_Session session,
+ uint32_t indicator,
+ int32_t* value);
+
+/**
+ * Reads an unsigned 32-bit integer value from a given indicator or control.
+ *
+ * @param session handle to a currently open session
+ * @param indicator indicator or control from which to read
+ * @param value outputs the value that was read
+ * @return result of the call
+ */
+NiFpga_Status NiFpga_ReadU32(NiFpga_Session session,
+ uint32_t indicator,
+ uint32_t* value);
+
+/**
+ * Reads a signed 64-bit integer value from a given indicator or control.
+ *
+ * @param session handle to a currently open session
+ * @param indicator indicator or control from which to read
+ * @param value outputs the value that was read
+ * @return result of the call
+ */
+NiFpga_Status NiFpga_ReadI64(NiFpga_Session session,
+ uint32_t indicator,
+ int64_t* value);
+
+/**
+ * Reads an unsigned 64-bit integer value from a given indicator or control.
+ *
+ * @param session handle to a currently open session
+ * @param indicator indicator or control from which to read
+ * @param value outputs the value that was read
+ * @return result of the call
+ */
+NiFpga_Status NiFpga_ReadU64(NiFpga_Session session,
+ uint32_t indicator,
+ uint64_t* value);
+
+/**
+ * Writes a boolean value to a given control or indicator.
+ *
+ * @param session handle to a currently open session
+ * @param control control or indicator to which to write
+ * @param value value to write
+ * @return result of the call
+ */
+NiFpga_Status NiFpga_WriteBool(NiFpga_Session session,
+ uint32_t control,
+ NiFpga_Bool value);
+
+/**
+ * Writes a signed 8-bit integer value to a given control or indicator.
+ *
+ * @param session handle to a currently open session
+ * @param control control or indicator to which to write
+ * @param value value to write
+ * @return result of the call
+ */
+NiFpga_Status NiFpga_WriteI8(NiFpga_Session session,
+ uint32_t control,
+ int8_t value);
+
+/**
+ * Writes an unsigned 8-bit integer value to a given control or indicator.
+ *
+ * @param session handle to a currently open session
+ * @param control control or indicator to which to write
+ * @param value value to write
+ * @return result of the call
+ */
+NiFpga_Status NiFpga_WriteU8(NiFpga_Session session,
+ uint32_t control,
+ uint8_t value);
+
+/**
+ * Writes a signed 16-bit integer value to a given control or indicator.
+ *
+ * @param session handle to a currently open session
+ * @param control control or indicator to which to write
+ * @param value value to write
+ * @return result of the call
+ */
+NiFpga_Status NiFpga_WriteI16(NiFpga_Session session,
+ uint32_t control,
+ int16_t value);
+
+/**
+ * Writes an unsigned 16-bit integer value to a given control or indicator.
+ *
+ * @param session handle to a currently open session
+ * @param control control or indicator to which to write
+ * @param value value to write
+ * @return result of the call
+ */
+NiFpga_Status NiFpga_WriteU16(NiFpga_Session session,
+ uint32_t control,
+ uint16_t value);
+
+/**
+ * Writes a signed 32-bit integer value to a given control or indicator.
+ *
+ * @param session handle to a currently open session
+ * @param control control or indicator to which to write
+ * @param value value to write
+ * @return result of the call
+ */
+NiFpga_Status NiFpga_WriteI32(NiFpga_Session session,
+ uint32_t control,
+ int32_t value);
+
+/**
+ * Writes an unsigned 32-bit integer value to a given control or indicator.
+ *
+ * @param session handle to a currently open session
+ * @param control control or indicator to which to write
+ * @param value value to write
+ * @return result of the call
+ */
+NiFpga_Status NiFpga_WriteU32(NiFpga_Session session,
+ uint32_t control,
+ uint32_t value);
+
+/**
+ * Writes a signed 64-bit integer value to a given control or indicator.
+ *
+ * @param session handle to a currently open session
+ * @param control control or indicator to which to write
+ * @param value value to write
+ * @return result of the call
+ */
+NiFpga_Status NiFpga_WriteI64(NiFpga_Session session,
+ uint32_t control,
+ int64_t value);
+
+/**
+ * Writes an unsigned 64-bit integer value to a given control or indicator.
+ *
+ * @param session handle to a currently open session
+ * @param control control or indicator to which to write
+ * @param value value to write
+ * @return result of the call
+ */
+NiFpga_Status NiFpga_WriteU64(NiFpga_Session session,
+ uint32_t control,
+ uint64_t value);
+
+/**
+ * Reads an entire array of boolean values from a given array indicator or
+ * control.
+ *
+ * @warning The size passed must be the exact number of elements in the
+ * indicator or control.
+ *
+ * @param session handle to a currently open session
+ * @param indicator indicator or control from which to read
+ * @param array outputs the entire array that was read
+ * @param size exact number of elements in the indicator or control
+ * @return result of the call
+ */
+NiFpga_Status NiFpga_ReadArrayBool(NiFpga_Session session,
+ uint32_t indicator,
+ NiFpga_Bool* array,
+ size_t size);
+
+/**
+ * Reads an entire array of signed 8-bit integer values from a given array
+ * indicator or control.
+ *
+ * @warning The size passed must be the exact number of elements in the
+ * indicator or control.
+ *
+ * @param session handle to a currently open session
+ * @param indicator indicator or control from which to read
+ * @param array outputs the entire array that was read
+ * @param size exact number of elements in the indicator or control
+ * @return result of the call
+ */
+NiFpga_Status NiFpga_ReadArrayI8(NiFpga_Session session,
+ uint32_t indicator,
+ int8_t* array,
+ size_t size);
+
+/**
+ * Reads an entire array of unsigned 8-bit integer values from a given array
+ * indicator or control.
+ *
+ * @warning The size passed must be the exact number of elements in the
+ * indicator or control.
+ *
+ * @param session handle to a currently open session
+ * @param indicator indicator or control from which to read
+ * @param array outputs the entire array that was read
+ * @param size exact number of elements in the indicator or control
+ * @return result of the call
+ */
+NiFpga_Status NiFpga_ReadArrayU8(NiFpga_Session session,
+ uint32_t indicator,
+ uint8_t* array,
+ size_t size);
+
+/**
+ * Reads an entire array of signed 16-bit integer values from a given array
+ * indicator or control.
+ *
+ * @warning The size passed must be the exact number of elements in the
+ * indicator or control.
+ *
+ * @param session handle to a currently open session
+ * @param indicator indicator or control from which to read
+ * @param array outputs the entire array that was read
+ * @param size exact number of elements in the indicator or control
+ * @return result of the call
+ */
+NiFpga_Status NiFpga_ReadArrayI16(NiFpga_Session session,
+ uint32_t indicator,
+ int16_t* array,
+ size_t size);
+
+/**
+ * Reads an entire array of unsigned 16-bit integer values from a given array
+ * indicator or control.
+ *
+ * @warning The size passed must be the exact number of elements in the
+ * indicator or control.
+ *
+ * @param session handle to a currently open session
+ * @param indicator indicator or control from which to read
+ * @param array outputs the entire array that was read
+ * @param size exact number of elements in the indicator or control
+ * @return result of the call
+ */
+NiFpga_Status NiFpga_ReadArrayU16(NiFpga_Session session,
+ uint32_t indicator,
+ uint16_t* array,
+ size_t size);
+
+/**
+ * Reads an entire array of signed 32-bit integer values from a given array
+ * indicator or control.
+ *
+ * @warning The size passed must be the exact number of elements in the
+ * indicator or control.
+ *
+ * @param session handle to a currently open session
+ * @param indicator indicator or control from which to read
+ * @param array outputs the entire array that was read
+ * @param size exact number of elements in the indicator or control
+ * @return result of the call
+ */
+NiFpga_Status NiFpga_ReadArrayI32(NiFpga_Session session,
+ uint32_t indicator,
+ int32_t* array,
+ size_t size);
+
+/**
+ * Reads an entire array of unsigned 32-bit integer values from a given array
+ * indicator or control.
+ *
+ * @warning The size passed must be the exact number of elements in the
+ * indicator or control.
+ *
+ * @param session handle to a currently open session
+ * @param indicator indicator or control from which to read
+ * @param array outputs the entire array that was read
+ * @param size exact number of elements in the indicator or control
+ * @return result of the call
+ */
+NiFpga_Status NiFpga_ReadArrayU32(NiFpga_Session session,
+ uint32_t indicator,
+ uint32_t* array,
+ size_t size);
+
+/**
+ * Reads an entire array of signed 64-bit integer values from a given array
+ * indicator or control.
+ *
+ * @warning The size passed must be the exact number of elements in the
+ * indicator or control.
+ *
+ * @param session handle to a currently open session
+ * @param indicator indicator or control from which to read
+ * @param array outputs the entire array that was read
+ * @param size exact number of elements in the indicator or control
+ * @return result of the call
+ */
+NiFpga_Status NiFpga_ReadArrayI64(NiFpga_Session session,
+ uint32_t indicator,
+ int64_t* array,
+ size_t size);
+
+/**
+ * Reads an entire array of unsigned 64-bit integer values from a given array
+ * indicator or control.
+ *
+ * @warning The size passed must be the exact number of elements in the
+ * indicator or control.
+ *
+ * @param session handle to a currently open session
+ * @param indicator indicator or control from which to read
+ * @param array outputs the entire array that was read
+ * @param size exact number of elements in the indicator or control
+ * @return result of the call
+ */
+NiFpga_Status NiFpga_ReadArrayU64(NiFpga_Session session,
+ uint32_t indicator,
+ uint64_t* array,
+ size_t size);
+
+/**
+ * Writes an entire array of boolean values to a given array control or
+ * indicator.
+ *
+ * @warning The size passed must be the exact number of elements in the
+ * control or indicator.
+ *
+ * @param session handle to a currently open session
+ * @param control control or indicator to which to write
+ * @param array entire array to write
+ * @param size exact number of elements in the control or indicator
+ * @return result of the call
+ */
+NiFpga_Status NiFpga_WriteArrayBool(NiFpga_Session session,
+ uint32_t control,
+ const NiFpga_Bool* array,
+ size_t size);
+
+/**
+ * Writes an entire array of signed 8-bit integer values to a given array
+ * control or indicator.
+ *
+ * @warning The size passed must be the exact number of elements in the
+ * control or indicator.
+ *
+ * @param session handle to a currently open session
+ * @param control control or indicator to which to write
+ * @param array entire array to write
+ * @param size exact number of elements in the control or indicator
+ * @return result of the call
+ */
+NiFpga_Status NiFpga_WriteArrayI8(NiFpga_Session session,
+ uint32_t control,
+ const int8_t* array,
+ size_t size);
+
+/**
+ * Writes an entire array of unsigned 8-bit integer values to a given array
+ * control or indicator.
+ *
+ * @warning The size passed must be the exact number of elements in the
+ * control or indicator.
+ *
+ * @param session handle to a currently open session
+ * @param control control or indicator to which to write
+ * @param array entire array to write
+ * @param size exact number of elements in the control or indicator
+ * @return result of the call
+ */
+NiFpga_Status NiFpga_WriteArrayU8(NiFpga_Session session,
+ uint32_t control,
+ const uint8_t* array,
+ size_t size);
+
+/**
+ * Writes an entire array of signed 16-bit integer values to a given array
+ * control or indicator.
+ *
+ * @warning The size passed must be the exact number of elements in the
+ * control or indicator.
+ *
+ * @param session handle to a currently open session
+ * @param control control or indicator to which to write
+ * @param array entire array to write
+ * @param size exact number of elements in the control or indicator
+ * @return result of the call
+ */
+NiFpga_Status NiFpga_WriteArrayI16(NiFpga_Session session,
+ uint32_t control,
+ const int16_t* array,
+ size_t size);
+
+/**
+ * Writes an entire array of unsigned 16-bit integer values to a given array
+ * control or indicator.
+ *
+ * @warning The size passed must be the exact number of elements in the
+ * control or indicator.
+ *
+ * @param session handle to a currently open session
+ * @param control control or indicator to which to write
+ * @param array entire array to write
+ * @param size exact number of elements in the control or indicator
+ * @return result of the call
+ */
+NiFpga_Status NiFpga_WriteArrayU16(NiFpga_Session session,
+ uint32_t control,
+ const uint16_t* array,
+ size_t size);
+
+/**
+ * Writes an entire array of signed 32-bit integer values to a given array
+ * control or indicator.
+ *
+ * @warning The size passed must be the exact number of elements in the
+ * control or indicator.
+ *
+ * @param session handle to a currently open session
+ * @param control control or indicator to which to write
+ * @param array entire array to write
+ * @param size exact number of elements in the control or indicator
+ * @return result of the call
+ */
+NiFpga_Status NiFpga_WriteArrayI32(NiFpga_Session session,
+ uint32_t control,
+ const int32_t* array,
+ size_t size);
+
+/**
+ * Writes an entire array of unsigned 32-bit integer values to a given array
+ * control or indicator.
+ *
+ * @warning The size passed must be the exact number of elements in the
+ * control or indicator.
+ *
+ * @param session handle to a currently open session
+ * @param control control or indicator to which to write
+ * @param array entire array to write
+ * @param size exact number of elements in the control or indicator
+ * @return result of the call
+ */
+NiFpga_Status NiFpga_WriteArrayU32(NiFpga_Session session,
+ uint32_t control,
+ const uint32_t* array,
+ size_t size);
+
+/**
+ * Writes an entire array of signed 64-bit integer values to a given array
+ * control or indicator.
+ *
+ * @warning The size passed must be the exact number of elements in the
+ * control or indicator.
+ *
+ * @param session handle to a currently open session
+ * @param control control or indicator to which to write
+ * @param array entire array to write
+ * @param size exact number of elements in the control or indicator
+ * @return result of the call
+ */
+NiFpga_Status NiFpga_WriteArrayI64(NiFpga_Session session,
+ uint32_t control,
+ const int64_t* array,
+ size_t size);
+
+/**
+ * Writes an entire array of unsigned 64-bit integer values to a given array
+ * control or indicator.
+ *
+ * @warning The size passed must be the exact number of elements in the
+ * control or indicator.
+ *
+ * @param session handle to a currently open session
+ * @param control control or indicator to which to write
+ * @param array entire array to write
+ * @param size exact number of elements in the control or indicator
+ * @return result of the call
+ */
+NiFpga_Status NiFpga_WriteArrayU64(NiFpga_Session session,
+ uint32_t control,
+ const uint64_t* array,
+ size_t size);
+
+/**
+ * Enumeration of all 32 possible IRQs. Multiple IRQs can be bitwise ORed
+ * together like this:
+ *
+ * NiFpga_Irq_3 | NiFpga_Irq_23
+ */
+typedef enum
+{
+ NiFpga_Irq_0 = 1 << 0,
+ NiFpga_Irq_1 = 1 << 1,
+ NiFpga_Irq_2 = 1 << 2,
+ NiFpga_Irq_3 = 1 << 3,
+ NiFpga_Irq_4 = 1 << 4,
+ NiFpga_Irq_5 = 1 << 5,
+ NiFpga_Irq_6 = 1 << 6,
+ NiFpga_Irq_7 = 1 << 7,
+ NiFpga_Irq_8 = 1 << 8,
+ NiFpga_Irq_9 = 1 << 9,
+ NiFpga_Irq_10 = 1 << 10,
+ NiFpga_Irq_11 = 1 << 11,
+ NiFpga_Irq_12 = 1 << 12,
+ NiFpga_Irq_13 = 1 << 13,
+ NiFpga_Irq_14 = 1 << 14,
+ NiFpga_Irq_15 = 1 << 15,
+ NiFpga_Irq_16 = 1 << 16,
+ NiFpga_Irq_17 = 1 << 17,
+ NiFpga_Irq_18 = 1 << 18,
+ NiFpga_Irq_19 = 1 << 19,
+ NiFpga_Irq_20 = 1 << 20,
+ NiFpga_Irq_21 = 1 << 21,
+ NiFpga_Irq_22 = 1 << 22,
+ NiFpga_Irq_23 = 1 << 23,
+ NiFpga_Irq_24 = 1 << 24,
+ NiFpga_Irq_25 = 1 << 25,
+ NiFpga_Irq_26 = 1 << 26,
+ NiFpga_Irq_27 = 1 << 27,
+ NiFpga_Irq_28 = 1 << 28,
+ NiFpga_Irq_29 = 1 << 29,
+ NiFpga_Irq_30 = 1 << 30,
+ NiFpga_Irq_31 = 1U << 31
+} NiFpga_Irq;
+
+/**
+ * Represents an infinite timeout.
+ */
+static const uint32_t NiFpga_InfiniteTimeout = 0xFFFFFFFF;
+
+/**
+ * See NiFpga_ReserveIrqContext for more information.
+ */
+typedef void* NiFpga_IrqContext;
+
+/**
+ * IRQ contexts are single-threaded; only one thread can wait with a
+ * particular context at any given time. To minimize jitter when first
+ * waiting on IRQs, reserve as many contexts as the application
+ * requires.
+ *
+ * If a context is successfully reserved (the returned status is not an error),
+ * it must be unreserved later. Otherwise a memory leak will occur.
+ *
+ * @param session handle to a currently open session
+ * @param context outputs the IRQ context
+ * @return result of the call
+ */
+NiFpga_Status NiFpga_ReserveIrqContext(NiFpga_Session session,
+ NiFpga_IrqContext* context);
+
+/**
+ * Unreserves an IRQ context obtained from NiFpga_ReserveIrqContext.
+ *
+ * @param session handle to a currently open session
+ * @param context IRQ context to unreserve
+ * @return result of the call
+ */
+NiFpga_Status NiFpga_UnreserveIrqContext(NiFpga_Session session,
+ NiFpga_IrqContext context);
+
+/**
+ * This is a blocking function that stops the calling thread until the
+ * FPGA asserts any IRQ in the irqs parameter, or until the function
+ * call times out. Before calling this function, use
+ * NiFpga_ReserveIrqContext to reserve an IRQ context. No other
+ * threads can use the same context when this function is called.
+ *
+ * You can use the irqsAsserted parameter to determine which IRQs were asserted
+ * for each function call.
+ *
+ * @param session handle to a currently open session
+ * @param context IRQ context with which to wait
+ * @param irqs bitwise OR of NiFpga_Irqs
+ * @param timeout timeout in milliseconds, or NiFpga_InfiniteTimeout
+ * @param irqsAsserted if non-NULL, outputs bitwise OR of IRQs that were
+ * asserted
+ * @param timedOut if non-NULL, outputs whether the timeout expired
+ * @return result of the call
+ */
+NiFpga_Status NiFpga_WaitOnIrqs(NiFpga_Session session,
+ NiFpga_IrqContext context,
+ uint32_t irqs,
+ uint32_t timeout,
+ uint32_t* irqsAsserted,
+ NiFpga_Bool* timedOut);
+
+/**
+ * Acknowledges an IRQ or set of IRQs.
+ *
+ * @param session handle to a currently open session
+ * @param irqs bitwise OR of NiFpga_Irqs
+ * @return result of the call
+ */
+NiFpga_Status NiFpga_AcknowledgeIrqs(NiFpga_Session session,
+ uint32_t irqs);
+
+/**
+ * Specifies the depth of the host memory part of the DMA FIFO. This method is
+ * optional. In order to see the actual depth configured, use
+ * NiFpga_ConfigureFifo2.
+ *
+ * @param session handle to a currently open session
+ * @param fifo FIFO to configure
+ * @param depth requested number of elements in the host memory part of the
+ * DMA FIFO
+ * @return result of the call
+ */
+NiFpga_Status NiFpga_ConfigureFifo(NiFpga_Session session,
+ uint32_t fifo,
+ size_t depth);
+
+/**
+ * Specifies the depth of the host memory part of the DMA FIFO. This method is
+ * optional.
+ *
+ * @param session handle to a currently open session
+ * @param fifo FIFO to configure
+ * @param requestedDepth requested number of elements in the host memory part
+ * of the DMA FIFO
+ * @param actualDepth if non-NULL, outputs the actual number of elements in the
+ * host memory part of the DMA FIFO, which may be more than
+ * the requested number
+ * @return result of the call
+ */
+NiFpga_Status NiFpga_ConfigureFifo2(NiFpga_Session session,
+ uint32_t fifo,
+ size_t requestedDepth,
+ size_t* actualDepth);
+
+/**
+ * Starts a FIFO. This method is optional.
+ *
+ * @param session handle to a currently open session
+ * @param fifo FIFO to start
+ * @return result of the call
+ */
+NiFpga_Status NiFpga_StartFifo(NiFpga_Session session,
+ uint32_t fifo);
+
+/**
+ * Stops a FIFO. This method is optional.
+ *
+ * @param session handle to a currently open session
+ * @param fifo FIFO to stop
+ * @return result of the call
+ */
+NiFpga_Status NiFpga_StopFifo(NiFpga_Session session,
+ uint32_t fifo);
+
+/**
+ * Reads from a target-to-host FIFO of booleans.
+ *
+ * @param session handle to a currently open session
+ * @param fifo target-to-host FIFO from which to read
+ * @param data outputs the data that was read
+ * @param numberOfElements number of elements to read
+ * @param timeout timeout in milliseconds, or NiFpga_InfiniteTimeout
+ * @param elementsRemaining if non-NULL, outputs the number of elements
+ * remaining in the host memory part of the DMA FIFO
+ * @return result of the call
+ */
+NiFpga_Status NiFpga_ReadFifoBool(NiFpga_Session session,
+ uint32_t fifo,
+ NiFpga_Bool* data,
+ size_t numberOfElements,
+ uint32_t timeout,
+ size_t* elementsRemaining);
+
+/**
+ * Reads from a target-to-host FIFO of signed 8-bit integers.
+ *
+ * @param session handle to a currently open session
+ * @param fifo target-to-host FIFO from which to read
+ * @param data outputs the data that was read
+ * @param numberOfElements number of elements to read
+ * @param timeout timeout in milliseconds, or NiFpga_InfiniteTimeout
+ * @param elementsRemaining if non-NULL, outputs the number of elements
+ * remaining in the host memory part of the DMA FIFO
+ * @return result of the call
+ */
+NiFpga_Status NiFpga_ReadFifoI8(NiFpga_Session session,
+ uint32_t fifo,
+ int8_t* data,
+ size_t numberOfElements,
+ uint32_t timeout,
+ size_t* elementsRemaining);
+
+/**
+ * Reads from a target-to-host FIFO of unsigned 8-bit integers.
+ *
+ * @param session handle to a currently open session
+ * @param fifo target-to-host FIFO from which to read
+ * @param data outputs the data that was read
+ * @param numberOfElements number of elements to read
+ * @param timeout timeout in milliseconds, or NiFpga_InfiniteTimeout
+ * @param elementsRemaining if non-NULL, outputs the number of elements
+ * remaining in the host memory part of the DMA FIFO
+ * @return result of the call
+ */
+NiFpga_Status NiFpga_ReadFifoU8(NiFpga_Session session,
+ uint32_t fifo,
+ uint8_t* data,
+ size_t numberOfElements,
+ uint32_t timeout,
+ size_t* elementsRemaining);
+
+/**
+ * Reads from a target-to-host FIFO of signed 16-bit integers.
+ *
+ * @param session handle to a currently open session
+ * @param fifo target-to-host FIFO from which to read
+ * @param data outputs the data that was read
+ * @param numberOfElements number of elements to read
+ * @param timeout timeout in milliseconds, or NiFpga_InfiniteTimeout
+ * @param elementsRemaining if non-NULL, outputs the number of elements
+ * remaining in the host memory part of the DMA FIFO
+ * @return result of the call
+ */
+NiFpga_Status NiFpga_ReadFifoI16(NiFpga_Session session,
+ uint32_t fifo,
+ int16_t* data,
+ size_t numberOfElements,
+ uint32_t timeout,
+ size_t* elementsRemaining);
+
+/**
+ * Reads from a target-to-host FIFO of unsigned 16-bit integers.
+ *
+ * @param session handle to a currently open session
+ * @param fifo target-to-host FIFO from which to read
+ * @param data outputs the data that was read
+ * @param numberOfElements number of elements to read
+ * @param timeout timeout in milliseconds, or NiFpga_InfiniteTimeout
+ * @param elementsRemaining if non-NULL, outputs the number of elements
+ * remaining in the host memory part of the DMA FIFO
+ * @return result of the call
+ */
+NiFpga_Status NiFpga_ReadFifoU16(NiFpga_Session session,
+ uint32_t fifo,
+ uint16_t* data,
+ size_t numberOfElements,
+ uint32_t timeout,
+ size_t* elementsRemaining);
+
+/**
+ * Reads from a target-to-host FIFO of signed 32-bit integers.
+ *
+ * @param session handle to a currently open session
+ * @param fifo target-to-host FIFO from which to read
+ * @param data outputs the data that was read
+ * @param numberOfElements number of elements to read
+ * @param timeout timeout in milliseconds, or NiFpga_InfiniteTimeout
+ * @param elementsRemaining if non-NULL, outputs the number of elements
+ * remaining in the host memory part of the DMA FIFO
+ * @return result of the call
+ */
+NiFpga_Status NiFpga_ReadFifoI32(NiFpga_Session session,
+ uint32_t fifo,
+ int32_t* data,
+ size_t numberOfElements,
+ uint32_t timeout,
+ size_t* elementsRemaining);
+
+/**
+ * Reads from a target-to-host FIFO of unsigned 32-bit integers.
+ *
+ * @param session handle to a currently open session
+ * @param fifo target-to-host FIFO from which to read
+ * @param data outputs the data that was read
+ * @param numberOfElements number of elements to read
+ * @param timeout timeout in milliseconds, or NiFpga_InfiniteTimeout
+ * @param elementsRemaining if non-NULL, outputs the number of elements
+ * remaining in the host memory part of the DMA FIFO
+ * @return result of the call
+ */
+NiFpga_Status NiFpga_ReadFifoU32(NiFpga_Session session,
+ uint32_t fifo,
+ uint32_t* data,
+ size_t numberOfElements,
+ uint32_t timeout,
+ size_t* elementsRemaining);
+
+/**
+ * Reads from a target-to-host FIFO of signed 64-bit integers.
+ *
+ * @param session handle to a currently open session
+ * @param fifo target-to-host FIFO from which to read
+ * @param data outputs the data that was read
+ * @param numberOfElements number of elements to read
+ * @param timeout timeout in milliseconds, or NiFpga_InfiniteTimeout
+ * @param elementsRemaining if non-NULL, outputs the number of elements
+ * remaining in the host memory part of the DMA FIFO
+ * @return result of the call
+ */
+NiFpga_Status NiFpga_ReadFifoI64(NiFpga_Session session,
+ uint32_t fifo,
+ int64_t* data,
+ size_t numberOfElements,
+ uint32_t timeout,
+ size_t* elementsRemaining);
+
+/**
+ * Reads from a target-to-host FIFO of unsigned 64-bit integers.
+ *
+ * @param session handle to a currently open session
+ * @param fifo target-to-host FIFO from which to read
+ * @param data outputs the data that was read
+ * @param numberOfElements number of elements to read
+ * @param timeout timeout in milliseconds, or NiFpga_InfiniteTimeout
+ * @param elementsRemaining if non-NULL, outputs the number of elements
+ * remaining in the host memory part of the DMA FIFO
+ * @return result of the call
+ */
+NiFpga_Status NiFpga_ReadFifoU64(NiFpga_Session session,
+ uint32_t fifo,
+ uint64_t* data,
+ size_t numberOfElements,
+ uint32_t timeout,
+ size_t* elementsRemaining);
+
+/**
+ * Writes to a host-to-target FIFO of booleans.
+ *
+ * @param session handle to a currently open session
+ * @param fifo host-to-target FIFO to which to write
+ * @param data data to write
+ * @param numberOfElements number of elements to write
+ * @param timeout timeout in milliseconds, or NiFpga_InfiniteTimeout
+ * @param emptyElementsRemaining if non-NULL, outputs the number of empty
+ * elements remaining in the host memory part of
+ * the DMA FIFO
+ * @return result of the call
+ */
+NiFpga_Status NiFpga_WriteFifoBool(NiFpga_Session session,
+ uint32_t fifo,
+ const NiFpga_Bool* data,
+ size_t numberOfElements,
+ uint32_t timeout,
+ size_t* emptyElementsRemaining);
+
+/**
+ * Writes to a host-to-target FIFO of signed 8-bit integers.
+ *
+ * @param session handle to a currently open session
+ * @param fifo host-to-target FIFO to which to write
+ * @param data data to write
+ * @param numberOfElements number of elements to write
+ * @param timeout timeout in milliseconds, or NiFpga_InfiniteTimeout
+ * @param emptyElementsRemaining if non-NULL, outputs the number of empty
+ * elements remaining in the host memory part of
+ * the DMA FIFO
+ * @return result of the call
+ */
+NiFpga_Status NiFpga_WriteFifoI8(NiFpga_Session session,
+ uint32_t fifo,
+ const int8_t* data,
+ size_t numberOfElements,
+ uint32_t timeout,
+ size_t* emptyElementsRemaining);
+
+/**
+ * Writes to a host-to-target FIFO of unsigned 8-bit integers.
+ *
+ * @param session handle to a currently open session
+ * @param fifo host-to-target FIFO to which to write
+ * @param data data to write
+ * @param numberOfElements number of elements to write
+ * @param timeout timeout in milliseconds, or NiFpga_InfiniteTimeout
+ * @param emptyElementsRemaining if non-NULL, outputs the number of empty
+ * elements remaining in the host memory part of
+ * the DMA FIFO
+ * @return result of the call
+ */
+NiFpga_Status NiFpga_WriteFifoU8(NiFpga_Session session,
+ uint32_t fifo,
+ const uint8_t* data,
+ size_t numberOfElements,
+ uint32_t timeout,
+ size_t* emptyElementsRemaining);
+
+/**
+ * Writes to a host-to-target FIFO of signed 16-bit integers.
+ *
+ * @param session handle to a currently open session
+ * @param fifo host-to-target FIFO to which to write
+ * @param data data to write
+ * @param numberOfElements number of elements to write
+ * @param timeout timeout in milliseconds, or NiFpga_InfiniteTimeout
+ * @param emptyElementsRemaining if non-NULL, outputs the number of empty
+ * elements remaining in the host memory part of
+ * the DMA FIFO
+ * @return result of the call
+ */
+NiFpga_Status NiFpga_WriteFifoI16(NiFpga_Session session,
+ uint32_t fifo,
+ const int16_t* data,
+ size_t numberOfElements,
+ uint32_t timeout,
+ size_t* emptyElementsRemaining);
+
+/**
+ * Writes to a host-to-target FIFO of unsigned 16-bit integers.
+ *
+ * @param session handle to a currently open session
+ * @param fifo host-to-target FIFO to which to write
+ * @param data data to write
+ * @param numberOfElements number of elements to write
+ * @param timeout timeout in milliseconds, or NiFpga_InfiniteTimeout
+ * @param emptyElementsRemaining if non-NULL, outputs the number of empty
+ * elements remaining in the host memory part of
+ * the DMA FIFO
+ * @return result of the call
+ */
+NiFpga_Status NiFpga_WriteFifoU16(NiFpga_Session session,
+ uint32_t fifo,
+ const uint16_t* data,
+ size_t numberOfElements,
+ uint32_t timeout,
+ size_t* emptyElementsRemaining);
+
+/**
+ * Writes to a host-to-target FIFO of signed 32-bit integers.
+ *
+ * @param session handle to a currently open session
+ * @param fifo host-to-target FIFO to which to write
+ * @param data data to write
+ * @param numberOfElements number of elements to write
+ * @param timeout timeout in milliseconds, or NiFpga_InfiniteTimeout
+ * @param emptyElementsRemaining if non-NULL, outputs the number of empty
+ * elements remaining in the host memory part of
+ * the DMA FIFO
+ * @return result of the call
+ */
+NiFpga_Status NiFpga_WriteFifoI32(NiFpga_Session session,
+ uint32_t fifo,
+ const int32_t* data,
+ size_t numberOfElements,
+ uint32_t timeout,
+ size_t* emptyElementsRemaining);
+
+/**
+ * Writes to a host-to-target FIFO of unsigned 32-bit integers.
+ *
+ * @param session handle to a currently open session
+ * @param fifo host-to-target FIFO to which to write
+ * @param data data to write
+ * @param numberOfElements number of elements to write
+ * @param timeout timeout in milliseconds, or NiFpga_InfiniteTimeout
+ * @param emptyElementsRemaining if non-NULL, outputs the number of empty
+ * elements remaining in the host memory part of
+ * the DMA FIFO
+ * @return result of the call
+ */
+NiFpga_Status NiFpga_WriteFifoU32(NiFpga_Session session,
+ uint32_t fifo,
+ const uint32_t* data,
+ size_t numberOfElements,
+ uint32_t timeout,
+ size_t* emptyElementsRemaining);
+
+/**
+ * Writes to a host-to-target FIFO of signed 64-bit integers.
+ *
+ * @param session handle to a currently open session
+ * @param fifo host-to-target FIFO to which to write
+ * @param data data to write
+ * @param numberOfElements number of elements to write
+ * @param timeout timeout in milliseconds, or NiFpga_InfiniteTimeout
+ * @param emptyElementsRemaining if non-NULL, outputs the number of empty
+ * elements remaining in the host memory part of
+ * the DMA FIFO
+ * @return result of the call
+ */
+NiFpga_Status NiFpga_WriteFifoI64(NiFpga_Session session,
+ uint32_t fifo,
+ const int64_t* data,
+ size_t numberOfElements,
+ uint32_t timeout,
+ size_t* emptyElementsRemaining);
+
+/**
+ * Writes to a host-to-target FIFO of unsigned 64-bit integers.
+ *
+ * @param session handle to a currently open session
+ * @param fifo host-to-target FIFO to which to write
+ * @param data data to write
+ * @param numberOfElements number of elements to write
+ * @param timeout timeout in milliseconds, or NiFpga_InfiniteTimeout
+ * @param emptyElementsRemaining if non-NULL, outputs the number of empty
+ * elements remaining in the host memory part of
+ * the DMA FIFO
+ * @return result of the call
+ */
+NiFpga_Status NiFpga_WriteFifoU64(NiFpga_Session session,
+ uint32_t fifo,
+ const uint64_t* data,
+ size_t numberOfElements,
+ uint32_t timeout,
+ size_t* emptyElementsRemaining);
+
+/**
+ * Acquires elements for reading from a target-to-host FIFO of booleans.
+ *
+ * Acquiring, reading, and releasing FIFO elements prevents the need to copy
+ * the contents of elements from the host memory buffer to a separate
+ * user-allocated buffer before reading. The FPGA target cannot write to
+ * elements acquired by the host. Therefore, the host must release elements
+ * after reading them. The number of elements acquired may differ from the
+ * number of elements requested if, for example, the number of elements
+ * requested reaches the end of the host memory buffer. Always release all
+ * acquired elements before closing the session. Do not attempt to access FIFO
+ * elements after the elements are released or the session is closed.
+ *
+ * @param session handle to a currently open session
+ * @param fifo target-to-host FIFO from which to read
+ * @param elements outputs a pointer to the elements acquired
+ * @param elementsRequested requested number of elements
+ * @param timeout timeout in milliseconds, or NiFpga_InfiniteTimeout
+ * @param elementsAcquired actual number of elements acquired, which may be
+ * less than the requested number
+ * @param elementsRemaining if non-NULL, outputs the number of elements
+ * remaining in the host memory part of the DMA FIFO
+ * @return result of the call
+ */
+NiFpga_Status NiFpga_AcquireFifoReadElementsBool(
+ NiFpga_Session session,
+ uint32_t fifo,
+ NiFpga_Bool** elements,
+ size_t elementsRequested,
+ uint32_t timeout,
+ size_t* elementsAcquired,
+ size_t* elementsRemaining);
+
+/**
+ * Acquires elements for reading from a target-to-host FIFO of signed 8-bit
+ * integers.
+ *
+ * Acquiring, reading, and releasing FIFO elements prevents the need to copy
+ * the contents of elements from the host memory buffer to a separate
+ * user-allocated buffer before reading. The FPGA target cannot write to
+ * elements acquired by the host. Therefore, the host must release elements
+ * after reading them. The number of elements acquired may differ from the
+ * number of elements requested if, for example, the number of elements
+ * requested reaches the end of the host memory buffer. Always release all
+ * acquired elements before closing the session. Do not attempt to access FIFO
+ * elements after the elements are released or the session is closed.
+ *
+ * @param session handle to a currently open session
+ * @param fifo target-to-host FIFO from which to read
+ * @param elements outputs a pointer to the elements acquired
+ * @param elementsRequested requested number of elements
+ * @param timeout timeout in milliseconds, or NiFpga_InfiniteTimeout
+ * @param elementsAcquired actual number of elements acquired, which may be
+ * less than the requested number
+ * @param elementsRemaining if non-NULL, outputs the number of elements
+ * remaining in the host memory part of the DMA FIFO
+ * @return result of the call
+ */
+NiFpga_Status NiFpga_AcquireFifoReadElementsI8(
+ NiFpga_Session session,
+ uint32_t fifo,
+ int8_t** elements,
+ size_t elementsRequested,
+ uint32_t timeout,
+ size_t* elementsAcquired,
+ size_t* elementsRemaining);
+
+/**
+ * Acquires elements for reading from a target-to-host FIFO of unsigned 8-bit
+ * integers.
+ *
+ * Acquiring, reading, and releasing FIFO elements prevents the need to copy
+ * the contents of elements from the host memory buffer to a separate
+ * user-allocated buffer before reading. The FPGA target cannot write to
+ * elements acquired by the host. Therefore, the host must release elements
+ * after reading them. The number of elements acquired may differ from the
+ * number of elements requested if, for example, the number of elements
+ * requested reaches the end of the host memory buffer. Always release all
+ * acquired elements before closing the session. Do not attempt to access FIFO
+ * elements after the elements are released or the session is closed.
+ *
+ * @param session handle to a currently open session
+ * @param fifo target-to-host FIFO from which to read
+ * @param elements outputs a pointer to the elements acquired
+ * @param elementsRequested requested number of elements
+ * @param timeout timeout in milliseconds, or NiFpga_InfiniteTimeout
+ * @param elementsAcquired actual number of elements acquired, which may be
+ * less than the requested number
+ * @param elementsRemaining if non-NULL, outputs the number of elements
+ * remaining in the host memory part of the DMA FIFO
+ * @return result of the call
+ */
+NiFpga_Status NiFpga_AcquireFifoReadElementsU8(
+ NiFpga_Session session,
+ uint32_t fifo,
+ uint8_t** elements,
+ size_t elementsRequested,
+ uint32_t timeout,
+ size_t* elementsAcquired,
+ size_t* elementsRemaining);
+
+/**
+ * Acquires elements for reading from a target-to-host FIFO of signed 16-bit
+ * integers.
+ *
+ * Acquiring, reading, and releasing FIFO elements prevents the need to copy
+ * the contents of elements from the host memory buffer to a separate
+ * user-allocated buffer before reading. The FPGA target cannot write to
+ * elements acquired by the host. Therefore, the host must release elements
+ * after reading them. The number of elements acquired may differ from the
+ * number of elements requested if, for example, the number of elements
+ * requested reaches the end of the host memory buffer. Always release all
+ * acquired elements before closing the session. Do not attempt to access FIFO
+ * elements after the elements are released or the session is closed.
+ *
+ * @param session handle to a currently open session
+ * @param fifo target-to-host FIFO from which to read
+ * @param elements outputs a pointer to the elements acquired
+ * @param elementsRequested requested number of elements
+ * @param timeout timeout in milliseconds, or NiFpga_InfiniteTimeout
+ * @param elementsAcquired actual number of elements acquired, which may be
+ * less than the requested number
+ * @param elementsRemaining if non-NULL, outputs the number of elements
+ * remaining in the host memory part of the DMA FIFO
+ * @return result of the call
+ */
+NiFpga_Status NiFpga_AcquireFifoReadElementsI16(
+ NiFpga_Session session,
+ uint32_t fifo,
+ int16_t** elements,
+ size_t elementsRequested,
+ uint32_t timeout,
+ size_t* elementsAcquired,
+ size_t* elementsRemaining);
+
+/**
+ * Acquires elements for reading from a target-to-host FIFO of unsigned 16-bit
+ * integers.
+ *
+ * Acquiring, reading, and releasing FIFO elements prevents the need to copy
+ * the contents of elements from the host memory buffer to a separate
+ * user-allocated buffer before reading. The FPGA target cannot write to
+ * elements acquired by the host. Therefore, the host must release elements
+ * after reading them. The number of elements acquired may differ from the
+ * number of elements requested if, for example, the number of elements
+ * requested reaches the end of the host memory buffer. Always release all
+ * acquired elements before closing the session. Do not attempt to access FIFO
+ * elements after the elements are released or the session is closed.
+ *
+ * @param session handle to a currently open session
+ * @param fifo target-to-host FIFO from which to read
+ * @param elements outputs a pointer to the elements acquired
+ * @param elementsRequested requested number of elements
+ * @param timeout timeout in milliseconds, or NiFpga_InfiniteTimeout
+ * @param elementsAcquired actual number of elements acquired, which may be
+ * less than the requested number
+ * @param elementsRemaining if non-NULL, outputs the number of elements
+ * remaining in the host memory part of the DMA FIFO
+ * @return result of the call
+ */
+NiFpga_Status NiFpga_AcquireFifoReadElementsU16(
+ NiFpga_Session session,
+ uint32_t fifo,
+ uint16_t** elements,
+ size_t elementsRequested,
+ uint32_t timeout,
+ size_t* elementsAcquired,
+ size_t* elementsRemaining);
+
+/**
+ * Acquires elements for reading from a target-to-host FIFO of signed 32-bit
+ * integers.
+ *
+ * Acquiring, reading, and releasing FIFO elements prevents the need to copy
+ * the contents of elements from the host memory buffer to a separate
+ * user-allocated buffer before reading. The FPGA target cannot write to
+ * elements acquired by the host. Therefore, the host must release elements
+ * after reading them. The number of elements acquired may differ from the
+ * number of elements requested if, for example, the number of elements
+ * requested reaches the end of the host memory buffer. Always release all
+ * acquired elements before closing the session. Do not attempt to access FIFO
+ * elements after the elements are released or the session is closed.
+ *
+ * @param session handle to a currently open session
+ * @param fifo target-to-host FIFO from which to read
+ * @param elements outputs a pointer to the elements acquired
+ * @param elementsRequested requested number of elements
+ * @param timeout timeout in milliseconds, or NiFpga_InfiniteTimeout
+ * @param elementsAcquired actual number of elements acquired, which may be
+ * less than the requested number
+ * @param elementsRemaining if non-NULL, outputs the number of elements
+ * remaining in the host memory part of the DMA FIFO
+ * @return result of the call
+ */
+NiFpga_Status NiFpga_AcquireFifoReadElementsI32(
+ NiFpga_Session session,
+ uint32_t fifo,
+ int32_t** elements,
+ size_t elementsRequested,
+ uint32_t timeout,
+ size_t* elementsAcquired,
+ size_t* elementsRemaining);
+
+/**
+ * Acquires elements for reading from a target-to-host FIFO of unsigned 32-bit
+ * integers.
+ *
+ * Acquiring, reading, and releasing FIFO elements prevents the need to copy
+ * the contents of elements from the host memory buffer to a separate
+ * user-allocated buffer before reading. The FPGA target cannot write to
+ * elements acquired by the host. Therefore, the host must release elements
+ * after reading them. The number of elements acquired may differ from the
+ * number of elements requested if, for example, the number of elements
+ * requested reaches the end of the host memory buffer. Always release all
+ * acquired elements before closing the session. Do not attempt to access FIFO
+ * elements after the elements are released or the session is closed.
+ *
+ * @param session handle to a currently open session
+ * @param fifo target-to-host FIFO from which to read
+ * @param elements outputs a pointer to the elements acquired
+ * @param elementsRequested requested number of elements
+ * @param timeout timeout in milliseconds, or NiFpga_InfiniteTimeout
+ * @param elementsAcquired actual number of elements acquired, which may be
+ * less than the requested number
+ * @param elementsRemaining if non-NULL, outputs the number of elements
+ * remaining in the host memory part of the DMA FIFO
+ * @return result of the call
+ */
+NiFpga_Status NiFpga_AcquireFifoReadElementsU32(
+ NiFpga_Session session,
+ uint32_t fifo,
+ uint32_t** elements,
+ size_t elementsRequested,
+ uint32_t timeout,
+ size_t* elementsAcquired,
+ size_t* elementsRemaining);
+
+/**
+ * Acquires elements for reading from a target-to-host FIFO of signed 64-bit
+ * integers.
+ *
+ * Acquiring, reading, and releasing FIFO elements prevents the need to copy
+ * the contents of elements from the host memory buffer to a separate
+ * user-allocated buffer before reading. The FPGA target cannot write to
+ * elements acquired by the host. Therefore, the host must release elements
+ * after reading them. The number of elements acquired may differ from the
+ * number of elements requested if, for example, the number of elements
+ * requested reaches the end of the host memory buffer. Always release all
+ * acquired elements before closing the session. Do not attempt to access FIFO
+ * elements after the elements are released or the session is closed.
+ *
+ * @param session handle to a currently open session
+ * @param fifo target-to-host FIFO from which to read
+ * @param elements outputs a pointer to the elements acquired
+ * @param elementsRequested requested number of elements
+ * @param timeout timeout in milliseconds, or NiFpga_InfiniteTimeout
+ * @param elementsAcquired actual number of elements acquired, which may be
+ * less than the requested number
+ * @param elementsRemaining if non-NULL, outputs the number of elements
+ * remaining in the host memory part of the DMA FIFO
+ * @return result of the call
+ */
+NiFpga_Status NiFpga_AcquireFifoReadElementsI64(
+ NiFpga_Session session,
+ uint32_t fifo,
+ int64_t** elements,
+ size_t elementsRequested,
+ uint32_t timeout,
+ size_t* elementsAcquired,
+ size_t* elementsRemaining);
+
+/**
+ * Acquires elements for reading from a target-to-host FIFO of unsigned 64-bit
+ * integers.
+ *
+ * Acquiring, reading, and releasing FIFO elements prevents the need to copy
+ * the contents of elements from the host memory buffer to a separate
+ * user-allocated buffer before reading. The FPGA target cannot write to
+ * elements acquired by the host. Therefore, the host must release elements
+ * after reading them. The number of elements acquired may differ from the
+ * number of elements requested if, for example, the number of elements
+ * requested reaches the end of the host memory buffer. Always release all
+ * acquired elements before closing the session. Do not attempt to access FIFO
+ * elements after the elements are released or the session is closed.
+ *
+ * @param session handle to a currently open session
+ * @param fifo target-to-host FIFO from which to read
+ * @param elements outputs a pointer to the elements acquired
+ * @param elementsRequested requested number of elements
+ * @param timeout timeout in milliseconds, or NiFpga_InfiniteTimeout
+ * @param elementsAcquired actual number of elements acquired, which may be
+ * less than the requested number
+ * @param elementsRemaining if non-NULL, outputs the number of elements
+ * remaining in the host memory part of the DMA FIFO
+ * @return result of the call
+ */
+NiFpga_Status NiFpga_AcquireFifoReadElementsU64(
+ NiFpga_Session session,
+ uint32_t fifo,
+ uint64_t** elements,
+ size_t elementsRequested,
+ uint32_t timeout,
+ size_t* elementsAcquired,
+ size_t* elementsRemaining);
+
+/**
+ * Acquires elements for writing to a host-to-target FIFO of booleans.
+ *
+ * Acquiring, writing, and releasing FIFO elements prevents the need to write
+ * first into a separate user-allocated buffer and then copy the contents of
+ * elements to the host memory buffer. The FPGA target cannot read elements
+ * acquired by the host. Therefore, the host must release elements after
+ * writing to them. The number of elements acquired may differ from the number
+ * of elements requested if, for example, the number of elements requested
+ * reaches the end of the host memory buffer. Always release all acquired
+ * elements before closing the session. Do not attempt to access FIFO elements
+ * after the elements are released or the session is closed.
+ *
+ * @param session handle to a currently open session
+ * @param fifo host-to-target FIFO to which to write
+ * @param elements outputs a pointer to the elements acquired
+ * @param elementsRequested requested number of elements
+ * @param timeout timeout in milliseconds, or NiFpga_InfiniteTimeout
+ * @param elementsAcquired actual number of elements acquired, which may be
+ * less than the requested number
+ * @param elementsRemaining if non-NULL, outputs the number of elements
+ * remaining in the host memory part of the DMA FIFO
+ * @return result of the call
+ */
+NiFpga_Status NiFpga_AcquireFifoWriteElementsBool(
+ NiFpga_Session session,
+ uint32_t fifo,
+ NiFpga_Bool** elements,
+ size_t elementsRequested,
+ uint32_t timeout,
+ size_t* elementsAcquired,
+ size_t* elementsRemaining);
+
+/**
+ * Acquires elements for writing to a host-to-target FIFO of signed 8-bit
+ * integers.
+ *
+ * Acquiring, writing, and releasing FIFO elements prevents the need to write
+ * first into a separate user-allocated buffer and then copy the contents of
+ * elements to the host memory buffer. The FPGA target cannot read elements
+ * acquired by the host. Therefore, the host must release elements after
+ * writing to them. The number of elements acquired may differ from the number
+ * of elements requested if, for example, the number of elements requested
+ * reaches the end of the host memory buffer. Always release all acquired
+ * elements before closing the session. Do not attempt to access FIFO elements
+ * after the elements are released or the session is closed.
+ *
+ * @param session handle to a currently open session
+ * @param fifo host-to-target FIFO to which to write
+ * @param elements outputs a pointer to the elements acquired
+ * @param elementsRequested requested number of elements
+ * @param timeout timeout in milliseconds, or NiFpga_InfiniteTimeout
+ * @param elementsAcquired actual number of elements acquired, which may be
+ * less than the requested number
+ * @param elementsRemaining if non-NULL, outputs the number of elements
+ * remaining in the host memory part of the DMA FIFO
+ * @return result of the call
+ */
+NiFpga_Status NiFpga_AcquireFifoWriteElementsI8(
+ NiFpga_Session session,
+ uint32_t fifo,
+ int8_t** elements,
+ size_t elementsRequested,
+ uint32_t timeout,
+ size_t* elementsAcquired,
+ size_t* elementsRemaining);
+
+/**
+ * Acquires elements for writing to a host-to-target FIFO of unsigned 8-bit
+ * integers.
+ *
+ * Acquiring, writing, and releasing FIFO elements prevents the need to write
+ * first into a separate user-allocated buffer and then copy the contents of
+ * elements to the host memory buffer. The FPGA target cannot read elements
+ * acquired by the host. Therefore, the host must release elements after
+ * writing to them. The number of elements acquired may differ from the number
+ * of elements requested if, for example, the number of elements requested
+ * reaches the end of the host memory buffer. Always release all acquired
+ * elements before closing the session. Do not attempt to access FIFO elements
+ * after the elements are released or the session is closed.
+ *
+ * @param session handle to a currently open session
+ * @param fifo host-to-target FIFO to which to write
+ * @param elements outputs a pointer to the elements acquired
+ * @param elementsRequested requested number of elements
+ * @param timeout timeout in milliseconds, or NiFpga_InfiniteTimeout
+ * @param elementsAcquired actual number of elements acquired, which may be
+ * less than the requested number
+ * @param elementsRemaining if non-NULL, outputs the number of elements
+ * remaining in the host memory part of the DMA FIFO
+ * @return result of the call
+ */
+NiFpga_Status NiFpga_AcquireFifoWriteElementsU8(
+ NiFpga_Session session,
+ uint32_t fifo,
+ uint8_t** elements,
+ size_t elementsRequested,
+ uint32_t timeout,
+ size_t* elementsAcquired,
+ size_t* elementsRemaining);
+
+/**
+ * Acquires elements for writing to a host-to-target FIFO of signed 16-bit
+ * integers.
+ *
+ * Acquiring, writing, and releasing FIFO elements prevents the need to write
+ * first into a separate user-allocated buffer and then copy the contents of
+ * elements to the host memory buffer. The FPGA target cannot read elements
+ * acquired by the host. Therefore, the host must release elements after
+ * writing to them. The number of elements acquired may differ from the number
+ * of elements requested if, for example, the number of elements requested
+ * reaches the end of the host memory buffer. Always release all acquired
+ * elements before closing the session. Do not attempt to access FIFO elements
+ * after the elements are released or the session is closed.
+ *
+ * @param session handle to a currently open session
+ * @param fifo host-to-target FIFO to which to write
+ * @param elements outputs a pointer to the elements acquired
+ * @param elementsRequested requested number of elements
+ * @param timeout timeout in milliseconds, or NiFpga_InfiniteTimeout
+ * @param elementsAcquired actual number of elements acquired, which may be
+ * less than the requested number
+ * @param elementsRemaining if non-NULL, outputs the number of elements
+ * remaining in the host memory part of the DMA FIFO
+ * @return result of the call
+ */
+NiFpga_Status NiFpga_AcquireFifoWriteElementsI16(
+ NiFpga_Session session,
+ uint32_t fifo,
+ int16_t** elements,
+ size_t elementsRequested,
+ uint32_t timeout,
+ size_t* elementsAcquired,
+ size_t* elementsRemaining);
+
+/**
+ * Acquires elements for writing to a host-to-target FIFO of unsigned 16-bit
+ * integers.
+ *
+ * Acquiring, writing, and releasing FIFO elements prevents the need to write
+ * first into a separate user-allocated buffer and then copy the contents of
+ * elements to the host memory buffer. The FPGA target cannot read elements
+ * acquired by the host. Therefore, the host must release elements after
+ * writing to them. The number of elements acquired may differ from the number
+ * of elements requested if, for example, the number of elements requested
+ * reaches the end of the host memory buffer. Always release all acquired
+ * elements before closing the session. Do not attempt to access FIFO elements
+ * after the elements are released or the session is closed.
+ *
+ * @param session handle to a currently open session
+ * @param fifo host-to-target FIFO to which to write
+ * @param elements outputs a pointer to the elements acquired
+ * @param elementsRequested requested number of elements
+ * @param timeout timeout in milliseconds, or NiFpga_InfiniteTimeout
+ * @param elementsAcquired actual number of elements acquired, which may be
+ * less than the requested number
+ * @param elementsRemaining if non-NULL, outputs the number of elements
+ * remaining in the host memory part of the DMA FIFO
+ * @return result of the call
+ */
+NiFpga_Status NiFpga_AcquireFifoWriteElementsU16(
+ NiFpga_Session session,
+ uint32_t fifo,
+ uint16_t** elements,
+ size_t elementsRequested,
+ uint32_t timeout,
+ size_t* elementsAcquired,
+ size_t* elementsRemaining);
+
+/**
+ * Acquires elements for writing to a host-to-target FIFO of signed 32-bit
+ * integers.
+ *
+ * Acquiring, writing, and releasing FIFO elements prevents the need to write
+ * first into a separate user-allocated buffer and then copy the contents of
+ * elements to the host memory buffer. The FPGA target cannot read elements
+ * acquired by the host. Therefore, the host must release elements after
+ * writing to them. The number of elements acquired may differ from the number
+ * of elements requested if, for example, the number of elements requested
+ * reaches the end of the host memory buffer. Always release all acquired
+ * elements before closing the session. Do not attempt to access FIFO elements
+ * after the elements are released or the session is closed.
+ *
+ * @param session handle to a currently open session
+ * @param fifo host-to-target FIFO to which to write
+ * @param elements outputs a pointer to the elements acquired
+ * @param elementsRequested requested number of elements
+ * @param timeout timeout in milliseconds, or NiFpga_InfiniteTimeout
+ * @param elementsAcquired actual number of elements acquired, which may be
+ * less than the requested number
+ * @param elementsRemaining if non-NULL, outputs the number of elements
+ * remaining in the host memory part of the DMA FIFO
+ * @return result of the call
+ */
+NiFpga_Status NiFpga_AcquireFifoWriteElementsI32(
+ NiFpga_Session session,
+ uint32_t fifo,
+ int32_t** elements,
+ size_t elementsRequested,
+ uint32_t timeout,
+ size_t* elementsAcquired,
+ size_t* elementsRemaining);
+
+/**
+ * Acquires elements for writing to a host-to-target FIFO of unsigned 32-bit
+ * integers.
+ *
+ * Acquiring, writing, and releasing FIFO elements prevents the need to write
+ * first into a separate user-allocated buffer and then copy the contents of
+ * elements to the host memory buffer. The FPGA target cannot read elements
+ * acquired by the host. Therefore, the host must release elements after
+ * writing to them. The number of elements acquired may differ from the number
+ * of elements requested if, for example, the number of elements requested
+ * reaches the end of the host memory buffer. Always release all acquired
+ * elements before closing the session. Do not attempt to access FIFO elements
+ * after the elements are released or the session is closed.
+ *
+ * @param session handle to a currently open session
+ * @param fifo host-to-target FIFO to which to write
+ * @param elements outputs a pointer to the elements acquired
+ * @param elementsRequested requested number of elements
+ * @param timeout timeout in milliseconds, or NiFpga_InfiniteTimeout
+ * @param elementsAcquired actual number of elements acquired, which may be
+ * less than the requested number
+ * @param elementsRemaining if non-NULL, outputs the number of elements
+ * remaining in the host memory part of the DMA FIFO
+ * @return result of the call
+ */
+NiFpga_Status NiFpga_AcquireFifoWriteElementsU32(
+ NiFpga_Session session,
+ uint32_t fifo,
+ uint32_t** elements,
+ size_t elementsRequested,
+ uint32_t timeout,
+ size_t* elementsAcquired,
+ size_t* elementsRemaining);
+
+/**
+ * Acquires elements for writing to a host-to-target FIFO of signed 64-bit
+ * integers.
+ *
+ * Acquiring, writing, and releasing FIFO elements prevents the need to write
+ * first into a separate user-allocated buffer and then copy the contents of
+ * elements to the host memory buffer. The FPGA target cannot read elements
+ * acquired by the host. Therefore, the host must release elements after
+ * writing to them. The number of elements acquired may differ from the number
+ * of elements requested if, for example, the number of elements requested
+ * reaches the end of the host memory buffer. Always release all acquired
+ * elements before closing the session. Do not attempt to access FIFO elements
+ * after the elements are released or the session is closed.
+ *
+ * @param session handle to a currently open session
+ * @param fifo host-to-target FIFO to which to write
+ * @param elements outputs a pointer to the elements acquired
+ * @param elementsRequested requested number of elements
+ * @param timeout timeout in milliseconds, or NiFpga_InfiniteTimeout
+ * @param elementsAcquired actual number of elements acquired, which may be
+ * less than the requested number
+ * @param elementsRemaining if non-NULL, outputs the number of elements
+ * remaining in the host memory part of the DMA FIFO
+ * @return result of the call
+ */
+NiFpga_Status NiFpga_AcquireFifoWriteElementsI64(
+ NiFpga_Session session,
+ uint32_t fifo,
+ int64_t** elements,
+ size_t elementsRequested,
+ uint32_t timeout,
+ size_t* elementsAcquired,
+ size_t* elementsRemaining);
+
+/**
+ * Acquires elements for writing to a host-to-target FIFO of unsigned 64-bit
+ * integers.
+ *
+ * Acquiring, writing, and releasing FIFO elements prevents the need to write
+ * first into a separate user-allocated buffer and then copy the contents of
+ * elements to the host memory buffer. The FPGA target cannot read elements
+ * acquired by the host. Therefore, the host must release elements after
+ * writing to them. The number of elements acquired may differ from the number
+ * of elements requested if, for example, the number of elements requested
+ * reaches the end of the host memory buffer. Always release all acquired
+ * elements before closing the session. Do not attempt to access FIFO elements
+ * after the elements are released or the session is closed.
+ *
+ * @param session handle to a currently open session
+ * @param fifo host-to-target FIFO to which to write
+ * @param elements outputs a pointer to the elements acquired
+ * @param elementsRequested requested number of elements
+ * @param timeout timeout in milliseconds, or NiFpga_InfiniteTimeout
+ * @param elementsAcquired actual number of elements acquired, which may be
+ * less than the requested number
+ * @param elementsRemaining if non-NULL, outputs the number of elements
+ * remaining in the host memory part of the DMA FIFO
+ * @return result of the call
+ */
+NiFpga_Status NiFpga_AcquireFifoWriteElementsU64(
+ NiFpga_Session session,
+ uint32_t fifo,
+ uint64_t** elements,
+ size_t elementsRequested,
+ uint32_t timeout,
+ size_t* elementsAcquired,
+ size_t* elementsRemaining);
+
+/**
+ * Releases previously acquired FIFO elements.
+ *
+ * The FPGA target cannot read elements acquired by the host. Therefore, the
+ * host must release elements after acquiring them. Always release all acquired
+ * elements before closing the session. Do not attempt to access FIFO elements
+ * after the elements are released or the session is closed.
+ *
+ * @param session handle to a currently open session
+ * @param fifo FIFO from which to release elements
+ * @param elements number of elements to release
+ * @return result of the call
+ */
+NiFpga_Status NiFpga_ReleaseFifoElements(NiFpga_Session session,
+ uint32_t fifo,
+ size_t elements);
+
+/**
+ * Gets an endpoint reference to a peer-to-peer FIFO.
+ *
+ * @param session handle to a currently open session
+ * @param fifo peer-to-peer FIFO
+ * @param endpoint outputs the endpoint reference
+ * @return result of the call
+ */
+NiFpga_Status NiFpga_GetPeerToPeerFifoEndpoint(NiFpga_Session session,
+ uint32_t fifo,
+ uint32_t* endpoint);
+
+#if NiFpga_Cpp
+}
+#endif
+
+#endif
diff --git a/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nInvariantFPGANamespace/nInterfaceGlobals.h b/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nInvariantFPGANamespace/nInterfaceGlobals.h
new file mode 100644
index 0000000..abd07d4
--- /dev/null
+++ b/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nInvariantFPGANamespace/nInterfaceGlobals.h
@@ -0,0 +1,15 @@
+// Copyright (c) National Instruments 2008. All Rights Reserved.
+// Do Not Edit... this file is generated!
+
+#ifndef __nFRC_C0EF_1_1_0_nInterfaceGlobals_h__
+#define __nFRC_C0EF_1_1_0_nInterfaceGlobals_h__
+
+namespace nFPGA
+{
+namespace nFRC_C0EF_1_1_0
+{
+ extern unsigned int g_currentTargetClass;
+}
+}
+
+#endif // __nFRC_C0EF_1_1_0_nInterfaceGlobals_h__
diff --git a/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nInvariantFPGANamespace/tAI.h b/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nInvariantFPGANamespace/tAI.h
new file mode 100644
index 0000000..14121b5
--- /dev/null
+++ b/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nInvariantFPGANamespace/tAI.h
@@ -0,0 +1,73 @@
+// Copyright (c) National Instruments 2008. All Rights Reserved.
+// Do Not Edit... this file is generated!
+
+#ifndef __nFRC_C0EF_1_1_0_AI_h__
+#define __nFRC_C0EF_1_1_0_AI_h__
+
+#include "tSystemInterface.h"
+
+namespace nFPGA
+{
+namespace nFRC_C0EF_1_1_0
+{
+
+class tAI
+{
+public:
+ tAI(){}
+ virtual ~tAI(){}
+
+ virtual tSystemInterface* getSystemInterface() = 0;
+ static tAI* create(unsigned char sys_index, tRioStatusCode *status);
+ virtual unsigned char getSystemIndex() = 0;
+
+
+ typedef enum
+ {
+ kNumSystems = 2,
+ } tIfaceConstants;
+
+
+
+ typedef enum
+ {
+ } tCalOK_IfaceConstants;
+
+ virtual bool readCalOK(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tDoneTime_IfaceConstants;
+
+ virtual unsigned int readDoneTime(tRioStatusCode *status) = 0;
+
+
+
+
+ typedef enum
+ {
+ kNumOffsetRegisters = 8,
+ } tOffset_IfaceConstants;
+
+ virtual signed int readOffset(unsigned char reg_index, tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ kNumLSBWeightRegisters = 8,
+ } tLSBWeight_IfaceConstants;
+
+ virtual unsigned int readLSBWeight(unsigned char reg_index, tRioStatusCode *status) = 0;
+
+
+
+private:
+ tAI(const tAI&);
+ void operator=(const tAI&);
+};
+
+}
+}
+
+#endif // __nFRC_C0EF_1_1_0_AI_h__
diff --git a/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nInvariantFPGANamespace/tGlobal.h b/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nInvariantFPGANamespace/tGlobal.h
new file mode 100644
index 0000000..0b74117
--- /dev/null
+++ b/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nInvariantFPGANamespace/tGlobal.h
@@ -0,0 +1,69 @@
+// Copyright (c) National Instruments 2008. All Rights Reserved.
+// Do Not Edit... this file is generated!
+
+#ifndef __nFRC_C0EF_1_1_0_Global_h__
+#define __nFRC_C0EF_1_1_0_Global_h__
+
+#include "tSystemInterface.h"
+
+namespace nFPGA
+{
+namespace nFRC_C0EF_1_1_0
+{
+
+class tGlobal
+{
+public:
+ tGlobal(){}
+ virtual ~tGlobal(){}
+
+ virtual tSystemInterface* getSystemInterface() = 0;
+ static tGlobal* create(tRioStatusCode *status);
+
+ typedef enum
+ {
+ kNumSystems = 1,
+ } tIfaceConstants;
+
+
+
+
+ typedef enum
+ {
+ } tVersion_IfaceConstants;
+
+ virtual unsigned short readVersion(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tLocalTime_IfaceConstants;
+
+ virtual unsigned int readLocalTime(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tRevision_IfaceConstants;
+
+ virtual unsigned int readRevision(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tReserved_IfaceConstants;
+
+ virtual unsigned char readReserved(tRioStatusCode *status) = 0;
+
+
+
+
+private:
+ tGlobal(const tGlobal&);
+ void operator=(const tGlobal&);
+};
+
+}
+}
+
+#endif // __nFRC_C0EF_1_1_0_Global_h__
diff --git a/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nInvariantFPGANamespace/tLoadOut.h b/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nInvariantFPGANamespace/tLoadOut.h
new file mode 100644
index 0000000..a7c4ebb
--- /dev/null
+++ b/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nInvariantFPGANamespace/tLoadOut.h
@@ -0,0 +1,79 @@
+// Copyright (c) National Instruments 2008. All Rights Reserved.
+// Do Not Edit... this file is generated!
+
+#ifndef __nFRC_C0EF_1_1_0_LoadOut_h__
+#define __nFRC_C0EF_1_1_0_LoadOut_h__
+
+#include "tSystemInterface.h"
+
+namespace nFPGA
+{
+namespace nFRC_C0EF_1_1_0
+{
+
+class tLoadOut
+{
+public:
+ tLoadOut(){}
+ virtual ~tLoadOut(){}
+
+ virtual tSystemInterface* getSystemInterface() = 0;
+ static tLoadOut* create(tRioStatusCode *status);
+
+ typedef enum
+ {
+ kNumSystems = 1,
+ } tIfaceConstants;
+
+
+
+
+ typedef enum
+ {
+ } tReady_IfaceConstants;
+
+ virtual bool readReady(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tDoneTime_IfaceConstants;
+
+ virtual unsigned int readDoneTime(tRioStatusCode *status) = 0;
+
+
+
+
+ typedef enum
+ {
+ kNumVendorIDRegisters = 8,
+ } tVendorID_IfaceConstants;
+
+ virtual unsigned short readVendorID(unsigned char reg_index, tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ kNumSerialNumberRegisters = 8,
+ } tSerialNumber_IfaceConstants;
+
+ virtual unsigned int readSerialNumber(unsigned char reg_index, tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ kNumModuleIDRegisters = 8,
+ } tModuleID_IfaceConstants;
+
+ virtual unsigned short readModuleID(unsigned char reg_index, tRioStatusCode *status) = 0;
+
+
+private:
+ tLoadOut(const tLoadOut&);
+ void operator=(const tLoadOut&);
+};
+
+}
+}
+
+#endif // __nFRC_C0EF_1_1_0_LoadOut_h__
diff --git a/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRoboRIO_FPGANamespace/nInterfaceGlobals.h b/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRoboRIO_FPGANamespace/nInterfaceGlobals.h
new file mode 100644
index 0000000..9ff5a0e
--- /dev/null
+++ b/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRoboRIO_FPGANamespace/nInterfaceGlobals.h
@@ -0,0 +1,15 @@
+// Copyright (c) National Instruments 2008. All Rights Reserved.
+// Do Not Edit... this file is generated!
+
+#ifndef __nFRC_2015_1_0_A_nInterfaceGlobals_h__
+#define __nFRC_2015_1_0_A_nInterfaceGlobals_h__
+
+namespace nFPGA
+{
+namespace nFRC_2015_1_0_A
+{
+ extern unsigned int g_currentTargetClass;
+}
+}
+
+#endif // __nFRC_2015_1_0_A_nInterfaceGlobals_h__
diff --git a/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRoboRIO_FPGANamespace/tAI.h b/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRoboRIO_FPGANamespace/tAI.h
new file mode 100644
index 0000000..2a8f614
--- /dev/null
+++ b/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRoboRIO_FPGANamespace/tAI.h
@@ -0,0 +1,143 @@
+// Copyright (c) National Instruments 2008. All Rights Reserved.
+// Do Not Edit... this file is generated!
+
+#ifndef __nFRC_2015_1_0_A_AI_h__
+#define __nFRC_2015_1_0_A_AI_h__
+
+#include "tSystemInterface.h"
+
+namespace nFPGA
+{
+namespace nFRC_2015_1_0_A
+{
+
+class tAI
+{
+public:
+ tAI(){}
+ virtual ~tAI(){}
+
+ virtual tSystemInterface* getSystemInterface() = 0;
+ static tAI* create(tRioStatusCode *status);
+
+ typedef enum
+ {
+ kNumSystems = 1,
+ } tIfaceConstants;
+
+ typedef
+ union{
+ struct{
+#ifdef __vxworks
+ unsigned ScanSize : 3;
+ unsigned ConvertRate : 26;
+#else
+ unsigned ConvertRate : 26;
+ unsigned ScanSize : 3;
+#endif
+ };
+ struct{
+ unsigned value : 29;
+ };
+ } tConfig;
+ typedef
+ union{
+ struct{
+#ifdef __vxworks
+ unsigned Channel : 3;
+ unsigned Averaged : 1;
+#else
+ unsigned Averaged : 1;
+ unsigned Channel : 3;
+#endif
+ };
+ struct{
+ unsigned value : 4;
+ };
+ } tReadSelect;
+
+
+
+ typedef enum
+ {
+ } tOutput_IfaceConstants;
+
+ virtual signed int readOutput(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tConfig_IfaceConstants;
+
+ virtual void writeConfig(tConfig value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_ScanSize(unsigned char value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_ConvertRate(unsigned int value, tRioStatusCode *status) = 0;
+ virtual tConfig readConfig(tRioStatusCode *status) = 0;
+ virtual unsigned char readConfig_ScanSize(tRioStatusCode *status) = 0;
+ virtual unsigned int readConfig_ConvertRate(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tLoopTiming_IfaceConstants;
+
+ virtual unsigned int readLoopTiming(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ kNumOversampleBitsElements = 8,
+ } tOversampleBits_IfaceConstants;
+
+ virtual void writeOversampleBits(unsigned char bitfield_index, unsigned char value, tRioStatusCode *status) = 0;
+ virtual unsigned char readOversampleBits(unsigned char bitfield_index, tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ kNumAverageBitsElements = 8,
+ } tAverageBits_IfaceConstants;
+
+ virtual void writeAverageBits(unsigned char bitfield_index, unsigned char value, tRioStatusCode *status) = 0;
+ virtual unsigned char readAverageBits(unsigned char bitfield_index, tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ kNumScanListElements = 8,
+ } tScanList_IfaceConstants;
+
+ virtual void writeScanList(unsigned char bitfield_index, unsigned char value, tRioStatusCode *status) = 0;
+ virtual unsigned char readScanList(unsigned char bitfield_index, tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tLatchOutput_IfaceConstants;
+
+ virtual void strobeLatchOutput(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tReadSelect_IfaceConstants;
+
+ virtual void writeReadSelect(tReadSelect value, tRioStatusCode *status) = 0;
+ virtual void writeReadSelect_Channel(unsigned char value, tRioStatusCode *status) = 0;
+ virtual void writeReadSelect_Averaged(bool value, tRioStatusCode *status) = 0;
+ virtual tReadSelect readReadSelect(tRioStatusCode *status) = 0;
+ virtual unsigned char readReadSelect_Channel(tRioStatusCode *status) = 0;
+ virtual bool readReadSelect_Averaged(tRioStatusCode *status) = 0;
+
+
+
+
+private:
+ tAI(const tAI&);
+ void operator=(const tAI&);
+};
+
+}
+}
+
+#endif // __nFRC_2015_1_0_A_AI_h__
diff --git a/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRoboRIO_FPGANamespace/tAO.h b/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRoboRIO_FPGANamespace/tAO.h
new file mode 100644
index 0000000..7160f04
--- /dev/null
+++ b/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRoboRIO_FPGANamespace/tAO.h
@@ -0,0 +1,50 @@
+// Copyright (c) National Instruments 2008. All Rights Reserved.
+// Do Not Edit... this file is generated!
+
+#ifndef __nFRC_2015_1_0_A_AO_h__
+#define __nFRC_2015_1_0_A_AO_h__
+
+#include "tSystemInterface.h"
+
+namespace nFPGA
+{
+namespace nFRC_2015_1_0_A
+{
+
+class tAO
+{
+public:
+ tAO(){}
+ virtual ~tAO(){}
+
+ virtual tSystemInterface* getSystemInterface() = 0;
+ static tAO* create(tRioStatusCode *status);
+
+ typedef enum
+ {
+ kNumSystems = 1,
+ } tIfaceConstants;
+
+
+
+
+
+
+ typedef enum
+ {
+ kNumMXPRegisters = 2,
+ } tMXP_IfaceConstants;
+
+ virtual void writeMXP(unsigned char reg_index, unsigned short value, tRioStatusCode *status) = 0;
+ virtual unsigned short readMXP(unsigned char reg_index, tRioStatusCode *status) = 0;
+
+
+private:
+ tAO(const tAO&);
+ void operator=(const tAO&);
+};
+
+}
+}
+
+#endif // __nFRC_2015_1_0_A_AO_h__
diff --git a/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRoboRIO_FPGANamespace/tAccel.h b/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRoboRIO_FPGANamespace/tAccel.h
new file mode 100644
index 0000000..02318de
--- /dev/null
+++ b/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRoboRIO_FPGANamespace/tAccel.h
@@ -0,0 +1,102 @@
+// Copyright (c) National Instruments 2008. All Rights Reserved.
+// Do Not Edit... this file is generated!
+
+#ifndef __nFRC_2015_1_0_A_Accel_h__
+#define __nFRC_2015_1_0_A_Accel_h__
+
+#include "tSystemInterface.h"
+
+namespace nFPGA
+{
+namespace nFRC_2015_1_0_A
+{
+
+class tAccel
+{
+public:
+ tAccel(){}
+ virtual ~tAccel(){}
+
+ virtual tSystemInterface* getSystemInterface() = 0;
+ static tAccel* create(tRioStatusCode *status);
+
+ typedef enum
+ {
+ kNumSystems = 1,
+ } tIfaceConstants;
+
+
+
+
+ typedef enum
+ {
+ } tSTAT_IfaceConstants;
+
+ virtual unsigned char readSTAT(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tCNTR_IfaceConstants;
+
+ virtual void writeCNTR(unsigned char value, tRioStatusCode *status) = 0;
+ virtual unsigned char readCNTR(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tDATO_IfaceConstants;
+
+ virtual void writeDATO(unsigned char value, tRioStatusCode *status) = 0;
+ virtual unsigned char readDATO(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tCNFG_IfaceConstants;
+
+ virtual void writeCNFG(unsigned char value, tRioStatusCode *status) = 0;
+ virtual unsigned char readCNFG(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tCNTL_IfaceConstants;
+
+ virtual void writeCNTL(unsigned char value, tRioStatusCode *status) = 0;
+ virtual unsigned char readCNTL(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tDATI_IfaceConstants;
+
+ virtual unsigned char readDATI(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tGO_IfaceConstants;
+
+ virtual void strobeGO(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tADDR_IfaceConstants;
+
+ virtual void writeADDR(unsigned char value, tRioStatusCode *status) = 0;
+ virtual unsigned char readADDR(tRioStatusCode *status) = 0;
+
+
+
+
+private:
+ tAccel(const tAccel&);
+ void operator=(const tAccel&);
+};
+
+}
+}
+
+#endif // __nFRC_2015_1_0_A_Accel_h__
diff --git a/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRoboRIO_FPGANamespace/tAccumulator.h b/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRoboRIO_FPGANamespace/tAccumulator.h
new file mode 100644
index 0000000..991c5fb
--- /dev/null
+++ b/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRoboRIO_FPGANamespace/tAccumulator.h
@@ -0,0 +1,87 @@
+// Copyright (c) National Instruments 2008. All Rights Reserved.
+// Do Not Edit... this file is generated!
+
+#ifndef __nFRC_2015_1_0_A_Accumulator_h__
+#define __nFRC_2015_1_0_A_Accumulator_h__
+
+#include "tSystemInterface.h"
+
+namespace nFPGA
+{
+namespace nFRC_2015_1_0_A
+{
+
+class tAccumulator
+{
+public:
+ tAccumulator(){}
+ virtual ~tAccumulator(){}
+
+ virtual tSystemInterface* getSystemInterface() = 0;
+ static tAccumulator* create(unsigned char sys_index, tRioStatusCode *status);
+ virtual unsigned char getSystemIndex() = 0;
+
+
+ typedef enum
+ {
+ kNumSystems = 2,
+ } tIfaceConstants;
+
+ typedef
+ union{
+ struct{
+ signed long long Value;
+ unsigned Count : 32;
+ };
+ struct{
+ unsigned value : 32;
+ unsigned value2 : 32;
+ unsigned value3 : 32;
+ };
+ } tOutput;
+
+
+ typedef enum
+ {
+ } tOutput_IfaceConstants;
+
+ virtual tOutput readOutput(tRioStatusCode *status) = 0;
+ virtual signed long long readOutput_Value(tRioStatusCode *status) = 0;
+ virtual unsigned int readOutput_Count(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tCenter_IfaceConstants;
+
+ virtual void writeCenter(signed int value, tRioStatusCode *status) = 0;
+ virtual signed int readCenter(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tDeadband_IfaceConstants;
+
+ virtual void writeDeadband(signed int value, tRioStatusCode *status) = 0;
+ virtual signed int readDeadband(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tReset_IfaceConstants;
+
+ virtual void strobeReset(tRioStatusCode *status) = 0;
+
+
+
+
+
+private:
+ tAccumulator(const tAccumulator&);
+ void operator=(const tAccumulator&);
+};
+
+}
+}
+
+#endif // __nFRC_2015_1_0_A_Accumulator_h__
diff --git a/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRoboRIO_FPGANamespace/tAlarm.h b/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRoboRIO_FPGANamespace/tAlarm.h
new file mode 100644
index 0000000..8739405
--- /dev/null
+++ b/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRoboRIO_FPGANamespace/tAlarm.h
@@ -0,0 +1,57 @@
+// Copyright (c) National Instruments 2008. All Rights Reserved.
+// Do Not Edit... this file is generated!
+
+#ifndef __nFRC_2015_1_0_A_Alarm_h__
+#define __nFRC_2015_1_0_A_Alarm_h__
+
+#include "tSystemInterface.h"
+
+namespace nFPGA
+{
+namespace nFRC_2015_1_0_A
+{
+
+class tAlarm
+{
+public:
+ tAlarm(){}
+ virtual ~tAlarm(){}
+
+ virtual tSystemInterface* getSystemInterface() = 0;
+ static tAlarm* create(tRioStatusCode *status);
+
+ typedef enum
+ {
+ kNumSystems = 1,
+ } tIfaceConstants;
+
+
+
+
+ typedef enum
+ {
+ } tEnable_IfaceConstants;
+
+ virtual void writeEnable(bool value, tRioStatusCode *status) = 0;
+ virtual bool readEnable(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tTriggerTime_IfaceConstants;
+
+ virtual void writeTriggerTime(unsigned int value, tRioStatusCode *status) = 0;
+ virtual unsigned int readTriggerTime(tRioStatusCode *status) = 0;
+
+
+
+
+private:
+ tAlarm(const tAlarm&);
+ void operator=(const tAlarm&);
+};
+
+}
+}
+
+#endif // __nFRC_2015_1_0_A_Alarm_h__
diff --git a/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRoboRIO_FPGANamespace/tAnalogTrigger.h b/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRoboRIO_FPGANamespace/tAnalogTrigger.h
new file mode 100644
index 0000000..6b89bc2
--- /dev/null
+++ b/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRoboRIO_FPGANamespace/tAnalogTrigger.h
@@ -0,0 +1,129 @@
+// Copyright (c) National Instruments 2008. All Rights Reserved.
+// Do Not Edit... this file is generated!
+
+#ifndef __nFRC_2015_1_0_A_AnalogTrigger_h__
+#define __nFRC_2015_1_0_A_AnalogTrigger_h__
+
+#include "tSystemInterface.h"
+
+namespace nFPGA
+{
+namespace nFRC_2015_1_0_A
+{
+
+class tAnalogTrigger
+{
+public:
+ tAnalogTrigger(){}
+ virtual ~tAnalogTrigger(){}
+
+ virtual tSystemInterface* getSystemInterface() = 0;
+ static tAnalogTrigger* create(unsigned char sys_index, tRioStatusCode *status);
+ virtual unsigned char getSystemIndex() = 0;
+
+
+ typedef enum
+ {
+ kNumSystems = 8,
+ } tIfaceConstants;
+
+ typedef
+ union{
+ struct{
+#ifdef __vxworks
+ unsigned InHysteresis : 1;
+ unsigned OverLimit : 1;
+ unsigned Rising : 1;
+ unsigned Falling : 1;
+#else
+ unsigned Falling : 1;
+ unsigned Rising : 1;
+ unsigned OverLimit : 1;
+ unsigned InHysteresis : 1;
+#endif
+ };
+ struct{
+ unsigned value : 4;
+ };
+ } tOutput;
+ typedef
+ union{
+ struct{
+#ifdef __vxworks
+ unsigned Channel : 3;
+ unsigned Averaged : 1;
+ unsigned Filter : 1;
+ unsigned FloatingRollover : 1;
+ signed RolloverLimit : 8;
+#else
+ signed RolloverLimit : 8;
+ unsigned FloatingRollover : 1;
+ unsigned Filter : 1;
+ unsigned Averaged : 1;
+ unsigned Channel : 3;
+#endif
+ };
+ struct{
+ unsigned value : 14;
+ };
+ } tSourceSelect;
+
+
+ typedef enum
+ {
+ } tSourceSelect_IfaceConstants;
+
+ virtual void writeSourceSelect(tSourceSelect value, tRioStatusCode *status) = 0;
+ virtual void writeSourceSelect_Channel(unsigned char value, tRioStatusCode *status) = 0;
+ virtual void writeSourceSelect_Averaged(bool value, tRioStatusCode *status) = 0;
+ virtual void writeSourceSelect_Filter(bool value, tRioStatusCode *status) = 0;
+ virtual void writeSourceSelect_FloatingRollover(bool value, tRioStatusCode *status) = 0;
+ virtual void writeSourceSelect_RolloverLimit(signed short value, tRioStatusCode *status) = 0;
+ virtual tSourceSelect readSourceSelect(tRioStatusCode *status) = 0;
+ virtual unsigned char readSourceSelect_Channel(tRioStatusCode *status) = 0;
+ virtual bool readSourceSelect_Averaged(tRioStatusCode *status) = 0;
+ virtual bool readSourceSelect_Filter(tRioStatusCode *status) = 0;
+ virtual bool readSourceSelect_FloatingRollover(tRioStatusCode *status) = 0;
+ virtual signed short readSourceSelect_RolloverLimit(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tUpperLimit_IfaceConstants;
+
+ virtual void writeUpperLimit(signed int value, tRioStatusCode *status) = 0;
+ virtual signed int readUpperLimit(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tLowerLimit_IfaceConstants;
+
+ virtual void writeLowerLimit(signed int value, tRioStatusCode *status) = 0;
+ virtual signed int readLowerLimit(tRioStatusCode *status) = 0;
+
+
+
+ typedef enum
+ {
+ kNumOutputElements = 8,
+ } tOutput_IfaceConstants;
+
+ virtual tOutput readOutput(unsigned char bitfield_index, tRioStatusCode *status) = 0;
+ virtual bool readOutput_InHysteresis(unsigned char bitfield_index, tRioStatusCode *status) = 0;
+ virtual bool readOutput_OverLimit(unsigned char bitfield_index, tRioStatusCode *status) = 0;
+ virtual bool readOutput_Rising(unsigned char bitfield_index, tRioStatusCode *status) = 0;
+ virtual bool readOutput_Falling(unsigned char bitfield_index, tRioStatusCode *status) = 0;
+
+
+
+
+private:
+ tAnalogTrigger(const tAnalogTrigger&);
+ void operator=(const tAnalogTrigger&);
+};
+
+}
+}
+
+#endif // __nFRC_2015_1_0_A_AnalogTrigger_h__
diff --git a/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRoboRIO_FPGANamespace/tBIST.h b/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRoboRIO_FPGANamespace/tBIST.h
new file mode 100644
index 0000000..41b8626
--- /dev/null
+++ b/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRoboRIO_FPGANamespace/tBIST.h
@@ -0,0 +1,90 @@
+// Copyright (c) National Instruments 2008. All Rights Reserved.
+// Do Not Edit... this file is generated!
+
+#ifndef __nFRC_2015_1_0_A_BIST_h__
+#define __nFRC_2015_1_0_A_BIST_h__
+
+#include "tSystemInterface.h"
+
+namespace nFPGA
+{
+namespace nFRC_2015_1_0_A
+{
+
+class tBIST
+{
+public:
+ tBIST(){}
+ virtual ~tBIST(){}
+
+ virtual tSystemInterface* getSystemInterface() = 0;
+ static tBIST* create(tRioStatusCode *status);
+
+ typedef enum
+ {
+ kNumSystems = 1,
+ } tIfaceConstants;
+
+
+
+
+ typedef enum
+ {
+ } tDO0SquareTicks_IfaceConstants;
+
+ virtual void writeDO0SquareTicks(unsigned int value, tRioStatusCode *status) = 0;
+ virtual unsigned int readDO0SquareTicks(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tEnable_IfaceConstants;
+
+ virtual void writeEnable(bool value, tRioStatusCode *status) = 0;
+ virtual bool readEnable(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tDO1SquareEnable_IfaceConstants;
+
+ virtual void writeDO1SquareEnable(bool value, tRioStatusCode *status) = 0;
+ virtual bool readDO1SquareEnable(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tDO0SquareEnable_IfaceConstants;
+
+ virtual void writeDO0SquareEnable(bool value, tRioStatusCode *status) = 0;
+ virtual bool readDO0SquareEnable(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tDO1SquareTicks_IfaceConstants;
+
+ virtual void writeDO1SquareTicks(unsigned int value, tRioStatusCode *status) = 0;
+ virtual unsigned int readDO1SquareTicks(tRioStatusCode *status) = 0;
+
+
+
+
+ typedef enum
+ {
+ kNumDORegisters = 2,
+ } tDO_IfaceConstants;
+
+ virtual void writeDO(unsigned char reg_index, bool value, tRioStatusCode *status) = 0;
+ virtual bool readDO(unsigned char reg_index, tRioStatusCode *status) = 0;
+
+
+private:
+ tBIST(const tBIST&);
+ void operator=(const tBIST&);
+};
+
+}
+}
+
+#endif // __nFRC_2015_1_0_A_BIST_h__
diff --git a/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRoboRIO_FPGANamespace/tCounter.h b/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRoboRIO_FPGANamespace/tCounter.h
new file mode 100644
index 0000000..6eff544
--- /dev/null
+++ b/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRoboRIO_FPGANamespace/tCounter.h
@@ -0,0 +1,219 @@
+// Copyright (c) National Instruments 2008. All Rights Reserved.
+// Do Not Edit... this file is generated!
+
+#ifndef __nFRC_2015_1_0_A_Counter_h__
+#define __nFRC_2015_1_0_A_Counter_h__
+
+#include "tSystemInterface.h"
+
+namespace nFPGA
+{
+namespace nFRC_2015_1_0_A
+{
+
+class tCounter
+{
+public:
+ tCounter(){}
+ virtual ~tCounter(){}
+
+ virtual tSystemInterface* getSystemInterface() = 0;
+ static tCounter* create(unsigned char sys_index, tRioStatusCode *status);
+ virtual unsigned char getSystemIndex() = 0;
+
+
+ typedef enum
+ {
+ kNumSystems = 8,
+ } tIfaceConstants;
+
+ typedef
+ union{
+ struct{
+#ifdef __vxworks
+ unsigned Direction : 1;
+ signed Value : 31;
+#else
+ signed Value : 31;
+ unsigned Direction : 1;
+#endif
+ };
+ struct{
+ unsigned value : 32;
+ };
+ } tOutput;
+ typedef
+ union{
+ struct{
+#ifdef __vxworks
+ unsigned UpSource_Channel : 4;
+ unsigned UpSource_Module : 1;
+ unsigned UpSource_AnalogTrigger : 1;
+ unsigned DownSource_Channel : 4;
+ unsigned DownSource_Module : 1;
+ unsigned DownSource_AnalogTrigger : 1;
+ unsigned IndexSource_Channel : 4;
+ unsigned IndexSource_Module : 1;
+ unsigned IndexSource_AnalogTrigger : 1;
+ unsigned IndexActiveHigh : 1;
+ unsigned IndexEdgeSensitive : 1;
+ unsigned UpRisingEdge : 1;
+ unsigned UpFallingEdge : 1;
+ unsigned DownRisingEdge : 1;
+ unsigned DownFallingEdge : 1;
+ unsigned Mode : 2;
+ unsigned PulseLengthThreshold : 6;
+#else
+ unsigned PulseLengthThreshold : 6;
+ unsigned Mode : 2;
+ unsigned DownFallingEdge : 1;
+ unsigned DownRisingEdge : 1;
+ unsigned UpFallingEdge : 1;
+ unsigned UpRisingEdge : 1;
+ unsigned IndexEdgeSensitive : 1;
+ unsigned IndexActiveHigh : 1;
+ unsigned IndexSource_AnalogTrigger : 1;
+ unsigned IndexSource_Module : 1;
+ unsigned IndexSource_Channel : 4;
+ unsigned DownSource_AnalogTrigger : 1;
+ unsigned DownSource_Module : 1;
+ unsigned DownSource_Channel : 4;
+ unsigned UpSource_AnalogTrigger : 1;
+ unsigned UpSource_Module : 1;
+ unsigned UpSource_Channel : 4;
+#endif
+ };
+ struct{
+ unsigned value : 32;
+ };
+ } tConfig;
+ typedef
+ union{
+ struct{
+#ifdef __vxworks
+ unsigned Period : 23;
+ signed Count : 8;
+ unsigned Stalled : 1;
+#else
+ unsigned Stalled : 1;
+ signed Count : 8;
+ unsigned Period : 23;
+#endif
+ };
+ struct{
+ unsigned value : 32;
+ };
+ } tTimerOutput;
+ typedef
+ union{
+ struct{
+#ifdef __vxworks
+ unsigned StallPeriod : 24;
+ unsigned AverageSize : 7;
+ unsigned UpdateWhenEmpty : 1;
+#else
+ unsigned UpdateWhenEmpty : 1;
+ unsigned AverageSize : 7;
+ unsigned StallPeriod : 24;
+#endif
+ };
+ struct{
+ unsigned value : 32;
+ };
+ } tTimerConfig;
+
+
+ typedef enum
+ {
+ } tOutput_IfaceConstants;
+
+ virtual tOutput readOutput(tRioStatusCode *status) = 0;
+ virtual bool readOutput_Direction(tRioStatusCode *status) = 0;
+ virtual signed int readOutput_Value(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tConfig_IfaceConstants;
+
+ virtual void writeConfig(tConfig value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_UpSource_Channel(unsigned char value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_UpSource_Module(unsigned char value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_UpSource_AnalogTrigger(bool value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_DownSource_Channel(unsigned char value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_DownSource_Module(unsigned char value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_DownSource_AnalogTrigger(bool value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_IndexSource_Channel(unsigned char value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_IndexSource_Module(unsigned char value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_IndexSource_AnalogTrigger(bool value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_IndexActiveHigh(bool value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_IndexEdgeSensitive(bool value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_UpRisingEdge(bool value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_UpFallingEdge(bool value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_DownRisingEdge(bool value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_DownFallingEdge(bool value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_Mode(unsigned char value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_PulseLengthThreshold(unsigned short value, tRioStatusCode *status) = 0;
+ virtual tConfig readConfig(tRioStatusCode *status) = 0;
+ virtual unsigned char readConfig_UpSource_Channel(tRioStatusCode *status) = 0;
+ virtual unsigned char readConfig_UpSource_Module(tRioStatusCode *status) = 0;
+ virtual bool readConfig_UpSource_AnalogTrigger(tRioStatusCode *status) = 0;
+ virtual unsigned char readConfig_DownSource_Channel(tRioStatusCode *status) = 0;
+ virtual unsigned char readConfig_DownSource_Module(tRioStatusCode *status) = 0;
+ virtual bool readConfig_DownSource_AnalogTrigger(tRioStatusCode *status) = 0;
+ virtual unsigned char readConfig_IndexSource_Channel(tRioStatusCode *status) = 0;
+ virtual unsigned char readConfig_IndexSource_Module(tRioStatusCode *status) = 0;
+ virtual bool readConfig_IndexSource_AnalogTrigger(tRioStatusCode *status) = 0;
+ virtual bool readConfig_IndexActiveHigh(tRioStatusCode *status) = 0;
+ virtual bool readConfig_IndexEdgeSensitive(tRioStatusCode *status) = 0;
+ virtual bool readConfig_UpRisingEdge(tRioStatusCode *status) = 0;
+ virtual bool readConfig_UpFallingEdge(tRioStatusCode *status) = 0;
+ virtual bool readConfig_DownRisingEdge(tRioStatusCode *status) = 0;
+ virtual bool readConfig_DownFallingEdge(tRioStatusCode *status) = 0;
+ virtual unsigned char readConfig_Mode(tRioStatusCode *status) = 0;
+ virtual unsigned short readConfig_PulseLengthThreshold(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tTimerOutput_IfaceConstants;
+
+ virtual tTimerOutput readTimerOutput(tRioStatusCode *status) = 0;
+ virtual unsigned int readTimerOutput_Period(tRioStatusCode *status) = 0;
+ virtual signed char readTimerOutput_Count(tRioStatusCode *status) = 0;
+ virtual bool readTimerOutput_Stalled(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tReset_IfaceConstants;
+
+ virtual void strobeReset(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tTimerConfig_IfaceConstants;
+
+ virtual void writeTimerConfig(tTimerConfig value, tRioStatusCode *status) = 0;
+ virtual void writeTimerConfig_StallPeriod(unsigned int value, tRioStatusCode *status) = 0;
+ virtual void writeTimerConfig_AverageSize(unsigned char value, tRioStatusCode *status) = 0;
+ virtual void writeTimerConfig_UpdateWhenEmpty(bool value, tRioStatusCode *status) = 0;
+ virtual tTimerConfig readTimerConfig(tRioStatusCode *status) = 0;
+ virtual unsigned int readTimerConfig_StallPeriod(tRioStatusCode *status) = 0;
+ virtual unsigned char readTimerConfig_AverageSize(tRioStatusCode *status) = 0;
+ virtual bool readTimerConfig_UpdateWhenEmpty(tRioStatusCode *status) = 0;
+
+
+
+
+
+private:
+ tCounter(const tCounter&);
+ void operator=(const tCounter&);
+};
+
+}
+}
+
+#endif // __nFRC_2015_1_0_A_Counter_h__
diff --git a/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRoboRIO_FPGANamespace/tDIO.h b/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRoboRIO_FPGANamespace/tDIO.h
new file mode 100644
index 0000000..0d9659b
--- /dev/null
+++ b/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRoboRIO_FPGANamespace/tDIO.h
@@ -0,0 +1,248 @@
+// Copyright (c) National Instruments 2008. All Rights Reserved.
+// Do Not Edit... this file is generated!
+
+#ifndef __nFRC_2015_1_0_A_DIO_h__
+#define __nFRC_2015_1_0_A_DIO_h__
+
+#include "tSystemInterface.h"
+
+namespace nFPGA
+{
+namespace nFRC_2015_1_0_A
+{
+
+class tDIO
+{
+public:
+ tDIO(){}
+ virtual ~tDIO(){}
+
+ virtual tSystemInterface* getSystemInterface() = 0;
+ static tDIO* create(tRioStatusCode *status);
+
+ typedef enum
+ {
+ kNumSystems = 1,
+ } tIfaceConstants;
+
+ typedef
+ union{
+ struct{
+#ifdef __vxworks
+ unsigned Headers : 10;
+ unsigned Reserved : 6;
+ unsigned MXP : 16;
+#else
+ unsigned MXP : 16;
+ unsigned Reserved : 6;
+ unsigned Headers : 10;
+#endif
+ };
+ struct{
+ unsigned value : 32;
+ };
+ } tDO;
+ typedef
+ union{
+ struct{
+#ifdef __vxworks
+ unsigned Headers : 10;
+ unsigned Reserved : 6;
+ unsigned MXP : 16;
+#else
+ unsigned MXP : 16;
+ unsigned Reserved : 6;
+ unsigned Headers : 10;
+#endif
+ };
+ struct{
+ unsigned value : 32;
+ };
+ } tOutputEnable;
+ typedef
+ union{
+ struct{
+#ifdef __vxworks
+ unsigned Headers : 10;
+ unsigned Reserved : 6;
+ unsigned MXP : 16;
+#else
+ unsigned MXP : 16;
+ unsigned Reserved : 6;
+ unsigned Headers : 10;
+#endif
+ };
+ struct{
+ unsigned value : 32;
+ };
+ } tPulse;
+ typedef
+ union{
+ struct{
+#ifdef __vxworks
+ unsigned Headers : 10;
+ unsigned Reserved : 6;
+ unsigned MXP : 16;
+#else
+ unsigned MXP : 16;
+ unsigned Reserved : 6;
+ unsigned Headers : 10;
+#endif
+ };
+ struct{
+ unsigned value : 32;
+ };
+ } tDI;
+
+
+
+ typedef enum
+ {
+ } tDO_IfaceConstants;
+
+ virtual void writeDO(tDO value, tRioStatusCode *status) = 0;
+ virtual void writeDO_Headers(unsigned short value, tRioStatusCode *status) = 0;
+ virtual void writeDO_Reserved(unsigned char value, tRioStatusCode *status) = 0;
+ virtual void writeDO_MXP(unsigned short value, tRioStatusCode *status) = 0;
+ virtual tDO readDO(tRioStatusCode *status) = 0;
+ virtual unsigned short readDO_Headers(tRioStatusCode *status) = 0;
+ virtual unsigned char readDO_Reserved(tRioStatusCode *status) = 0;
+ virtual unsigned short readDO_MXP(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ kNumPWMDutyCycleAElements = 4,
+ } tPWMDutyCycleA_IfaceConstants;
+
+ virtual void writePWMDutyCycleA(unsigned char bitfield_index, unsigned char value, tRioStatusCode *status) = 0;
+ virtual unsigned char readPWMDutyCycleA(unsigned char bitfield_index, tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ kNumPWMDutyCycleBElements = 2,
+ } tPWMDutyCycleB_IfaceConstants;
+
+ virtual void writePWMDutyCycleB(unsigned char bitfield_index, unsigned char value, tRioStatusCode *status) = 0;
+ virtual unsigned char readPWMDutyCycleB(unsigned char bitfield_index, tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ kNumFilterSelectHdrElements = 16,
+ } tFilterSelectHdr_IfaceConstants;
+
+ virtual void writeFilterSelectHdr(unsigned char bitfield_index, unsigned char value, tRioStatusCode *status) = 0;
+ virtual unsigned char readFilterSelectHdr(unsigned char bitfield_index, tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tOutputEnable_IfaceConstants;
+
+ virtual void writeOutputEnable(tOutputEnable value, tRioStatusCode *status) = 0;
+ virtual void writeOutputEnable_Headers(unsigned short value, tRioStatusCode *status) = 0;
+ virtual void writeOutputEnable_Reserved(unsigned char value, tRioStatusCode *status) = 0;
+ virtual void writeOutputEnable_MXP(unsigned short value, tRioStatusCode *status) = 0;
+ virtual tOutputEnable readOutputEnable(tRioStatusCode *status) = 0;
+ virtual unsigned short readOutputEnable_Headers(tRioStatusCode *status) = 0;
+ virtual unsigned char readOutputEnable_Reserved(tRioStatusCode *status) = 0;
+ virtual unsigned short readOutputEnable_MXP(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ kNumPWMOutputSelectElements = 6,
+ } tPWMOutputSelect_IfaceConstants;
+
+ virtual void writePWMOutputSelect(unsigned char bitfield_index, unsigned char value, tRioStatusCode *status) = 0;
+ virtual unsigned char readPWMOutputSelect(unsigned char bitfield_index, tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tPulse_IfaceConstants;
+
+ virtual void writePulse(tPulse value, tRioStatusCode *status) = 0;
+ virtual void writePulse_Headers(unsigned short value, tRioStatusCode *status) = 0;
+ virtual void writePulse_Reserved(unsigned char value, tRioStatusCode *status) = 0;
+ virtual void writePulse_MXP(unsigned short value, tRioStatusCode *status) = 0;
+ virtual tPulse readPulse(tRioStatusCode *status) = 0;
+ virtual unsigned short readPulse_Headers(tRioStatusCode *status) = 0;
+ virtual unsigned char readPulse_Reserved(tRioStatusCode *status) = 0;
+ virtual unsigned short readPulse_MXP(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tDI_IfaceConstants;
+
+ virtual tDI readDI(tRioStatusCode *status) = 0;
+ virtual unsigned short readDI_Headers(tRioStatusCode *status) = 0;
+ virtual unsigned char readDI_Reserved(tRioStatusCode *status) = 0;
+ virtual unsigned short readDI_MXP(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tEnableMXPSpecialFunction_IfaceConstants;
+
+ virtual void writeEnableMXPSpecialFunction(unsigned short value, tRioStatusCode *status) = 0;
+ virtual unsigned short readEnableMXPSpecialFunction(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ kNumFilterSelectMXPElements = 16,
+ } tFilterSelectMXP_IfaceConstants;
+
+ virtual void writeFilterSelectMXP(unsigned char bitfield_index, unsigned char value, tRioStatusCode *status) = 0;
+ virtual unsigned char readFilterSelectMXP(unsigned char bitfield_index, tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tPulseLength_IfaceConstants;
+
+ virtual void writePulseLength(unsigned char value, tRioStatusCode *status) = 0;
+ virtual unsigned char readPulseLength(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tPWMPeriodPower_IfaceConstants;
+
+ virtual void writePWMPeriodPower(unsigned short value, tRioStatusCode *status) = 0;
+ virtual unsigned short readPWMPeriodPower(tRioStatusCode *status) = 0;
+
+
+
+
+ typedef enum
+ {
+ kNumFilterPeriodMXPRegisters = 3,
+ } tFilterPeriodMXP_IfaceConstants;
+
+ virtual void writeFilterPeriodMXP(unsigned char reg_index, unsigned int value, tRioStatusCode *status) = 0;
+ virtual unsigned int readFilterPeriodMXP(unsigned char reg_index, tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ kNumFilterPeriodHdrRegisters = 3,
+ } tFilterPeriodHdr_IfaceConstants;
+
+ virtual void writeFilterPeriodHdr(unsigned char reg_index, unsigned int value, tRioStatusCode *status) = 0;
+ virtual unsigned int readFilterPeriodHdr(unsigned char reg_index, tRioStatusCode *status) = 0;
+
+
+private:
+ tDIO(const tDIO&);
+ void operator=(const tDIO&);
+};
+
+}
+}
+
+#endif // __nFRC_2015_1_0_A_DIO_h__
diff --git a/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRoboRIO_FPGANamespace/tDMA.h b/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRoboRIO_FPGANamespace/tDMA.h
new file mode 100644
index 0000000..4b57edf
--- /dev/null
+++ b/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRoboRIO_FPGANamespace/tDMA.h
@@ -0,0 +1,188 @@
+// Copyright (c) National Instruments 2008. All Rights Reserved.
+// Do Not Edit... this file is generated!
+
+#ifndef __nFRC_2015_1_0_A_DMA_h__
+#define __nFRC_2015_1_0_A_DMA_h__
+
+#include "tSystemInterface.h"
+
+namespace nFPGA
+{
+namespace nFRC_2015_1_0_A
+{
+
+class tDMA
+{
+public:
+ tDMA(){}
+ virtual ~tDMA(){}
+
+ virtual tSystemInterface* getSystemInterface() = 0;
+ static tDMA* create(tRioStatusCode *status);
+
+ typedef enum
+ {
+ kNumSystems = 1,
+ } tIfaceConstants;
+
+ typedef
+ union{
+ struct{
+#ifdef __vxworks
+ unsigned Pause : 1;
+ unsigned Enable_AI0_Low : 1;
+ unsigned Enable_AI0_High : 1;
+ unsigned Enable_AIAveraged0_Low : 1;
+ unsigned Enable_AIAveraged0_High : 1;
+ unsigned Enable_AI1_Low : 1;
+ unsigned Enable_AI1_High : 1;
+ unsigned Enable_AIAveraged1_Low : 1;
+ unsigned Enable_AIAveraged1_High : 1;
+ unsigned Enable_Accumulator0 : 1;
+ unsigned Enable_Accumulator1 : 1;
+ unsigned Enable_DI : 1;
+ unsigned Enable_AnalogTriggers : 1;
+ unsigned Enable_Counters_Low : 1;
+ unsigned Enable_Counters_High : 1;
+ unsigned Enable_CounterTimers_Low : 1;
+ unsigned Enable_CounterTimers_High : 1;
+ unsigned Enable_Encoders : 1;
+ unsigned Enable_EncoderTimers : 1;
+ unsigned ExternalClock : 1;
+#else
+ unsigned ExternalClock : 1;
+ unsigned Enable_EncoderTimers : 1;
+ unsigned Enable_Encoders : 1;
+ unsigned Enable_CounterTimers_High : 1;
+ unsigned Enable_CounterTimers_Low : 1;
+ unsigned Enable_Counters_High : 1;
+ unsigned Enable_Counters_Low : 1;
+ unsigned Enable_AnalogTriggers : 1;
+ unsigned Enable_DI : 1;
+ unsigned Enable_Accumulator1 : 1;
+ unsigned Enable_Accumulator0 : 1;
+ unsigned Enable_AIAveraged1_High : 1;
+ unsigned Enable_AIAveraged1_Low : 1;
+ unsigned Enable_AI1_High : 1;
+ unsigned Enable_AI1_Low : 1;
+ unsigned Enable_AIAveraged0_High : 1;
+ unsigned Enable_AIAveraged0_Low : 1;
+ unsigned Enable_AI0_High : 1;
+ unsigned Enable_AI0_Low : 1;
+ unsigned Pause : 1;
+#endif
+ };
+ struct{
+ unsigned value : 20;
+ };
+ } tConfig;
+ typedef
+ union{
+ struct{
+#ifdef __vxworks
+ unsigned ExternalClockSource_Channel : 4;
+ unsigned ExternalClockSource_Module : 1;
+ unsigned ExternalClockSource_AnalogTrigger : 1;
+ unsigned RisingEdge : 1;
+ unsigned FallingEdge : 1;
+#else
+ unsigned FallingEdge : 1;
+ unsigned RisingEdge : 1;
+ unsigned ExternalClockSource_AnalogTrigger : 1;
+ unsigned ExternalClockSource_Module : 1;
+ unsigned ExternalClockSource_Channel : 4;
+#endif
+ };
+ struct{
+ unsigned value : 8;
+ };
+ } tExternalTriggers;
+
+
+
+ typedef enum
+ {
+ } tRate_IfaceConstants;
+
+ virtual void writeRate(unsigned int value, tRioStatusCode *status) = 0;
+ virtual unsigned int readRate(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tConfig_IfaceConstants;
+
+ virtual void writeConfig(tConfig value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_Pause(bool value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_Enable_AI0_Low(bool value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_Enable_AI0_High(bool value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_Enable_AIAveraged0_Low(bool value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_Enable_AIAveraged0_High(bool value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_Enable_AI1_Low(bool value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_Enable_AI1_High(bool value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_Enable_AIAveraged1_Low(bool value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_Enable_AIAveraged1_High(bool value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_Enable_Accumulator0(bool value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_Enable_Accumulator1(bool value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_Enable_DI(bool value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_Enable_AnalogTriggers(bool value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_Enable_Counters_Low(bool value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_Enable_Counters_High(bool value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_Enable_CounterTimers_Low(bool value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_Enable_CounterTimers_High(bool value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_Enable_Encoders(bool value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_Enable_EncoderTimers(bool value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_ExternalClock(bool value, tRioStatusCode *status) = 0;
+ virtual tConfig readConfig(tRioStatusCode *status) = 0;
+ virtual bool readConfig_Pause(tRioStatusCode *status) = 0;
+ virtual bool readConfig_Enable_AI0_Low(tRioStatusCode *status) = 0;
+ virtual bool readConfig_Enable_AI0_High(tRioStatusCode *status) = 0;
+ virtual bool readConfig_Enable_AIAveraged0_Low(tRioStatusCode *status) = 0;
+ virtual bool readConfig_Enable_AIAveraged0_High(tRioStatusCode *status) = 0;
+ virtual bool readConfig_Enable_AI1_Low(tRioStatusCode *status) = 0;
+ virtual bool readConfig_Enable_AI1_High(tRioStatusCode *status) = 0;
+ virtual bool readConfig_Enable_AIAveraged1_Low(tRioStatusCode *status) = 0;
+ virtual bool readConfig_Enable_AIAveraged1_High(tRioStatusCode *status) = 0;
+ virtual bool readConfig_Enable_Accumulator0(tRioStatusCode *status) = 0;
+ virtual bool readConfig_Enable_Accumulator1(tRioStatusCode *status) = 0;
+ virtual bool readConfig_Enable_DI(tRioStatusCode *status) = 0;
+ virtual bool readConfig_Enable_AnalogTriggers(tRioStatusCode *status) = 0;
+ virtual bool readConfig_Enable_Counters_Low(tRioStatusCode *status) = 0;
+ virtual bool readConfig_Enable_Counters_High(tRioStatusCode *status) = 0;
+ virtual bool readConfig_Enable_CounterTimers_Low(tRioStatusCode *status) = 0;
+ virtual bool readConfig_Enable_CounterTimers_High(tRioStatusCode *status) = 0;
+ virtual bool readConfig_Enable_Encoders(tRioStatusCode *status) = 0;
+ virtual bool readConfig_Enable_EncoderTimers(tRioStatusCode *status) = 0;
+ virtual bool readConfig_ExternalClock(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ kNumExternalTriggersElements = 4,
+ } tExternalTriggers_IfaceConstants;
+
+ virtual void writeExternalTriggers(unsigned char bitfield_index, tExternalTriggers value, tRioStatusCode *status) = 0;
+ virtual void writeExternalTriggers_ExternalClockSource_Channel(unsigned char bitfield_index, unsigned char value, tRioStatusCode *status) = 0;
+ virtual void writeExternalTriggers_ExternalClockSource_Module(unsigned char bitfield_index, unsigned char value, tRioStatusCode *status) = 0;
+ virtual void writeExternalTriggers_ExternalClockSource_AnalogTrigger(unsigned char bitfield_index, bool value, tRioStatusCode *status) = 0;
+ virtual void writeExternalTriggers_RisingEdge(unsigned char bitfield_index, bool value, tRioStatusCode *status) = 0;
+ virtual void writeExternalTriggers_FallingEdge(unsigned char bitfield_index, bool value, tRioStatusCode *status) = 0;
+ virtual tExternalTriggers readExternalTriggers(unsigned char bitfield_index, tRioStatusCode *status) = 0;
+ virtual unsigned char readExternalTriggers_ExternalClockSource_Channel(unsigned char bitfield_index, tRioStatusCode *status) = 0;
+ virtual unsigned char readExternalTriggers_ExternalClockSource_Module(unsigned char bitfield_index, tRioStatusCode *status) = 0;
+ virtual bool readExternalTriggers_ExternalClockSource_AnalogTrigger(unsigned char bitfield_index, tRioStatusCode *status) = 0;
+ virtual bool readExternalTriggers_RisingEdge(unsigned char bitfield_index, tRioStatusCode *status) = 0;
+ virtual bool readExternalTriggers_FallingEdge(unsigned char bitfield_index, tRioStatusCode *status) = 0;
+
+
+
+
+private:
+ tDMA(const tDMA&);
+ void operator=(const tDMA&);
+};
+
+}
+}
+
+#endif // __nFRC_2015_1_0_A_DMA_h__
diff --git a/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRoboRIO_FPGANamespace/tEncoder.h b/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRoboRIO_FPGANamespace/tEncoder.h
new file mode 100644
index 0000000..466b9c1
--- /dev/null
+++ b/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRoboRIO_FPGANamespace/tEncoder.h
@@ -0,0 +1,199 @@
+// Copyright (c) National Instruments 2008. All Rights Reserved.
+// Do Not Edit... this file is generated!
+
+#ifndef __nFRC_2015_1_0_A_Encoder_h__
+#define __nFRC_2015_1_0_A_Encoder_h__
+
+#include "tSystemInterface.h"
+
+namespace nFPGA
+{
+namespace nFRC_2015_1_0_A
+{
+
+class tEncoder
+{
+public:
+ tEncoder(){}
+ virtual ~tEncoder(){}
+
+ virtual tSystemInterface* getSystemInterface() = 0;
+ static tEncoder* create(unsigned char sys_index, tRioStatusCode *status);
+ virtual unsigned char getSystemIndex() = 0;
+
+
+ typedef enum
+ {
+ kNumSystems = 8,
+ } tIfaceConstants;
+
+ typedef
+ union{
+ struct{
+#ifdef __vxworks
+ unsigned Direction : 1;
+ signed Value : 31;
+#else
+ signed Value : 31;
+ unsigned Direction : 1;
+#endif
+ };
+ struct{
+ unsigned value : 32;
+ };
+ } tOutput;
+ typedef
+ union{
+ struct{
+#ifdef __vxworks
+ unsigned ASource_Channel : 4;
+ unsigned ASource_Module : 1;
+ unsigned ASource_AnalogTrigger : 1;
+ unsigned BSource_Channel : 4;
+ unsigned BSource_Module : 1;
+ unsigned BSource_AnalogTrigger : 1;
+ unsigned IndexSource_Channel : 4;
+ unsigned IndexSource_Module : 1;
+ unsigned IndexSource_AnalogTrigger : 1;
+ unsigned IndexActiveHigh : 1;
+ unsigned IndexEdgeSensitive : 1;
+ unsigned Reverse : 1;
+#else
+ unsigned Reverse : 1;
+ unsigned IndexEdgeSensitive : 1;
+ unsigned IndexActiveHigh : 1;
+ unsigned IndexSource_AnalogTrigger : 1;
+ unsigned IndexSource_Module : 1;
+ unsigned IndexSource_Channel : 4;
+ unsigned BSource_AnalogTrigger : 1;
+ unsigned BSource_Module : 1;
+ unsigned BSource_Channel : 4;
+ unsigned ASource_AnalogTrigger : 1;
+ unsigned ASource_Module : 1;
+ unsigned ASource_Channel : 4;
+#endif
+ };
+ struct{
+ unsigned value : 21;
+ };
+ } tConfig;
+ typedef
+ union{
+ struct{
+#ifdef __vxworks
+ unsigned Period : 23;
+ signed Count : 8;
+ unsigned Stalled : 1;
+#else
+ unsigned Stalled : 1;
+ signed Count : 8;
+ unsigned Period : 23;
+#endif
+ };
+ struct{
+ unsigned value : 32;
+ };
+ } tTimerOutput;
+ typedef
+ union{
+ struct{
+#ifdef __vxworks
+ unsigned StallPeriod : 24;
+ unsigned AverageSize : 7;
+ unsigned UpdateWhenEmpty : 1;
+#else
+ unsigned UpdateWhenEmpty : 1;
+ unsigned AverageSize : 7;
+ unsigned StallPeriod : 24;
+#endif
+ };
+ struct{
+ unsigned value : 32;
+ };
+ } tTimerConfig;
+
+
+ typedef enum
+ {
+ } tOutput_IfaceConstants;
+
+ virtual tOutput readOutput(tRioStatusCode *status) = 0;
+ virtual bool readOutput_Direction(tRioStatusCode *status) = 0;
+ virtual signed int readOutput_Value(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tConfig_IfaceConstants;
+
+ virtual void writeConfig(tConfig value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_ASource_Channel(unsigned char value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_ASource_Module(unsigned char value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_ASource_AnalogTrigger(bool value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_BSource_Channel(unsigned char value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_BSource_Module(unsigned char value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_BSource_AnalogTrigger(bool value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_IndexSource_Channel(unsigned char value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_IndexSource_Module(unsigned char value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_IndexSource_AnalogTrigger(bool value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_IndexActiveHigh(bool value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_IndexEdgeSensitive(bool value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_Reverse(bool value, tRioStatusCode *status) = 0;
+ virtual tConfig readConfig(tRioStatusCode *status) = 0;
+ virtual unsigned char readConfig_ASource_Channel(tRioStatusCode *status) = 0;
+ virtual unsigned char readConfig_ASource_Module(tRioStatusCode *status) = 0;
+ virtual bool readConfig_ASource_AnalogTrigger(tRioStatusCode *status) = 0;
+ virtual unsigned char readConfig_BSource_Channel(tRioStatusCode *status) = 0;
+ virtual unsigned char readConfig_BSource_Module(tRioStatusCode *status) = 0;
+ virtual bool readConfig_BSource_AnalogTrigger(tRioStatusCode *status) = 0;
+ virtual unsigned char readConfig_IndexSource_Channel(tRioStatusCode *status) = 0;
+ virtual unsigned char readConfig_IndexSource_Module(tRioStatusCode *status) = 0;
+ virtual bool readConfig_IndexSource_AnalogTrigger(tRioStatusCode *status) = 0;
+ virtual bool readConfig_IndexActiveHigh(tRioStatusCode *status) = 0;
+ virtual bool readConfig_IndexEdgeSensitive(tRioStatusCode *status) = 0;
+ virtual bool readConfig_Reverse(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tTimerOutput_IfaceConstants;
+
+ virtual tTimerOutput readTimerOutput(tRioStatusCode *status) = 0;
+ virtual unsigned int readTimerOutput_Period(tRioStatusCode *status) = 0;
+ virtual signed char readTimerOutput_Count(tRioStatusCode *status) = 0;
+ virtual bool readTimerOutput_Stalled(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tReset_IfaceConstants;
+
+ virtual void strobeReset(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tTimerConfig_IfaceConstants;
+
+ virtual void writeTimerConfig(tTimerConfig value, tRioStatusCode *status) = 0;
+ virtual void writeTimerConfig_StallPeriod(unsigned int value, tRioStatusCode *status) = 0;
+ virtual void writeTimerConfig_AverageSize(unsigned char value, tRioStatusCode *status) = 0;
+ virtual void writeTimerConfig_UpdateWhenEmpty(bool value, tRioStatusCode *status) = 0;
+ virtual tTimerConfig readTimerConfig(tRioStatusCode *status) = 0;
+ virtual unsigned int readTimerConfig_StallPeriod(tRioStatusCode *status) = 0;
+ virtual unsigned char readTimerConfig_AverageSize(tRioStatusCode *status) = 0;
+ virtual bool readTimerConfig_UpdateWhenEmpty(tRioStatusCode *status) = 0;
+
+
+
+
+
+private:
+ tEncoder(const tEncoder&);
+ void operator=(const tEncoder&);
+};
+
+}
+}
+
+#endif // __nFRC_2015_1_0_A_Encoder_h__
diff --git a/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRoboRIO_FPGANamespace/tGlobal.h b/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRoboRIO_FPGANamespace/tGlobal.h
new file mode 100644
index 0000000..e2014de
--- /dev/null
+++ b/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRoboRIO_FPGANamespace/tGlobal.h
@@ -0,0 +1,104 @@
+// Copyright (c) National Instruments 2008. All Rights Reserved.
+// Do Not Edit... this file is generated!
+
+#ifndef __nFRC_2015_1_0_A_Global_h__
+#define __nFRC_2015_1_0_A_Global_h__
+
+#include "tSystemInterface.h"
+
+namespace nFPGA
+{
+namespace nFRC_2015_1_0_A
+{
+
+class tGlobal
+{
+public:
+ tGlobal(){}
+ virtual ~tGlobal(){}
+
+ virtual tSystemInterface* getSystemInterface() = 0;
+ static tGlobal* create(tRioStatusCode *status);
+
+ typedef enum
+ {
+ kNumSystems = 1,
+ } tIfaceConstants;
+
+ typedef
+ union{
+ struct{
+#ifdef __vxworks
+ unsigned Radio : 8;
+ unsigned Comm : 8;
+ unsigned Mode : 8;
+ unsigned RSL : 1;
+#else
+ unsigned RSL : 1;
+ unsigned Mode : 8;
+ unsigned Comm : 8;
+ unsigned Radio : 8;
+#endif
+ };
+ struct{
+ unsigned value : 25;
+ };
+ } tLEDs;
+
+
+
+ typedef enum
+ {
+ } tLEDs_IfaceConstants;
+
+ virtual void writeLEDs(tLEDs value, tRioStatusCode *status) = 0;
+ virtual void writeLEDs_Radio(unsigned char value, tRioStatusCode *status) = 0;
+ virtual void writeLEDs_Comm(unsigned char value, tRioStatusCode *status) = 0;
+ virtual void writeLEDs_Mode(unsigned char value, tRioStatusCode *status) = 0;
+ virtual void writeLEDs_RSL(bool value, tRioStatusCode *status) = 0;
+ virtual tLEDs readLEDs(tRioStatusCode *status) = 0;
+ virtual unsigned char readLEDs_Radio(tRioStatusCode *status) = 0;
+ virtual unsigned char readLEDs_Comm(tRioStatusCode *status) = 0;
+ virtual unsigned char readLEDs_Mode(tRioStatusCode *status) = 0;
+ virtual bool readLEDs_RSL(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tVersion_IfaceConstants;
+
+ virtual unsigned short readVersion(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tLocalTime_IfaceConstants;
+
+ virtual unsigned int readLocalTime(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tUserButton_IfaceConstants;
+
+ virtual bool readUserButton(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tRevision_IfaceConstants;
+
+ virtual unsigned int readRevision(tRioStatusCode *status) = 0;
+
+
+
+
+private:
+ tGlobal(const tGlobal&);
+ void operator=(const tGlobal&);
+};
+
+}
+}
+
+#endif // __nFRC_2015_1_0_A_Global_h__
diff --git a/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRoboRIO_FPGANamespace/tInterrupt.h b/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRoboRIO_FPGANamespace/tInterrupt.h
new file mode 100644
index 0000000..16233e1
--- /dev/null
+++ b/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRoboRIO_FPGANamespace/tInterrupt.h
@@ -0,0 +1,100 @@
+// Copyright (c) National Instruments 2008. All Rights Reserved.
+// Do Not Edit... this file is generated!
+
+#ifndef __nFRC_2015_1_0_A_Interrupt_h__
+#define __nFRC_2015_1_0_A_Interrupt_h__
+
+#include "tSystemInterface.h"
+
+namespace nFPGA
+{
+namespace nFRC_2015_1_0_A
+{
+
+class tInterrupt
+{
+public:
+ tInterrupt(){}
+ virtual ~tInterrupt(){}
+
+ virtual tSystemInterface* getSystemInterface() = 0;
+ static tInterrupt* create(unsigned char sys_index, tRioStatusCode *status);
+ virtual unsigned char getSystemIndex() = 0;
+
+
+ typedef enum
+ {
+ kNumSystems = 8,
+ } tIfaceConstants;
+
+ typedef
+ union{
+ struct{
+#ifdef __vxworks
+ unsigned Source_Channel : 4;
+ unsigned Source_Module : 1;
+ unsigned Source_AnalogTrigger : 1;
+ unsigned RisingEdge : 1;
+ unsigned FallingEdge : 1;
+ unsigned WaitForAck : 1;
+#else
+ unsigned WaitForAck : 1;
+ unsigned FallingEdge : 1;
+ unsigned RisingEdge : 1;
+ unsigned Source_AnalogTrigger : 1;
+ unsigned Source_Module : 1;
+ unsigned Source_Channel : 4;
+#endif
+ };
+ struct{
+ unsigned value : 9;
+ };
+ } tConfig;
+
+
+ typedef enum
+ {
+ } tFallingTimeStamp_IfaceConstants;
+
+ virtual unsigned int readFallingTimeStamp(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tConfig_IfaceConstants;
+
+ virtual void writeConfig(tConfig value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_Source_Channel(unsigned char value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_Source_Module(unsigned char value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_Source_AnalogTrigger(bool value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_RisingEdge(bool value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_FallingEdge(bool value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_WaitForAck(bool value, tRioStatusCode *status) = 0;
+ virtual tConfig readConfig(tRioStatusCode *status) = 0;
+ virtual unsigned char readConfig_Source_Channel(tRioStatusCode *status) = 0;
+ virtual unsigned char readConfig_Source_Module(tRioStatusCode *status) = 0;
+ virtual bool readConfig_Source_AnalogTrigger(tRioStatusCode *status) = 0;
+ virtual bool readConfig_RisingEdge(tRioStatusCode *status) = 0;
+ virtual bool readConfig_FallingEdge(tRioStatusCode *status) = 0;
+ virtual bool readConfig_WaitForAck(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tRisingTimeStamp_IfaceConstants;
+
+ virtual unsigned int readRisingTimeStamp(tRioStatusCode *status) = 0;
+
+
+
+
+
+private:
+ tInterrupt(const tInterrupt&);
+ void operator=(const tInterrupt&);
+};
+
+}
+}
+
+#endif // __nFRC_2015_1_0_A_Interrupt_h__
diff --git a/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRoboRIO_FPGANamespace/tPWM.h b/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRoboRIO_FPGANamespace/tPWM.h
new file mode 100644
index 0000000..5dcf5e2
--- /dev/null
+++ b/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRoboRIO_FPGANamespace/tPWM.h
@@ -0,0 +1,120 @@
+// Copyright (c) National Instruments 2008. All Rights Reserved.
+// Do Not Edit... this file is generated!
+
+#ifndef __nFRC_2015_1_0_A_PWM_h__
+#define __nFRC_2015_1_0_A_PWM_h__
+
+#include "tSystemInterface.h"
+
+namespace nFPGA
+{
+namespace nFRC_2015_1_0_A
+{
+
+class tPWM
+{
+public:
+ tPWM(){}
+ virtual ~tPWM(){}
+
+ virtual tSystemInterface* getSystemInterface() = 0;
+ static tPWM* create(tRioStatusCode *status);
+
+ typedef enum
+ {
+ kNumSystems = 1,
+ } tIfaceConstants;
+
+ typedef
+ union{
+ struct{
+#ifdef __vxworks
+ unsigned Period : 16;
+ unsigned MinHigh : 16;
+#else
+ unsigned MinHigh : 16;
+ unsigned Period : 16;
+#endif
+ };
+ struct{
+ unsigned value : 32;
+ };
+ } tConfig;
+
+
+
+ typedef enum
+ {
+ } tConfig_IfaceConstants;
+
+ virtual void writeConfig(tConfig value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_Period(unsigned short value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_MinHigh(unsigned short value, tRioStatusCode *status) = 0;
+ virtual tConfig readConfig(tRioStatusCode *status) = 0;
+ virtual unsigned short readConfig_Period(tRioStatusCode *status) = 0;
+ virtual unsigned short readConfig_MinHigh(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tLoopTiming_IfaceConstants;
+
+ virtual unsigned short readLoopTiming(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ kNumPeriodScaleMXPElements = 10,
+ } tPeriodScaleMXP_IfaceConstants;
+
+ virtual void writePeriodScaleMXP(unsigned char bitfield_index, unsigned char value, tRioStatusCode *status) = 0;
+ virtual unsigned char readPeriodScaleMXP(unsigned char bitfield_index, tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ kNumPeriodScaleHdrElements = 10,
+ } tPeriodScaleHdr_IfaceConstants;
+
+ virtual void writePeriodScaleHdr(unsigned char bitfield_index, unsigned char value, tRioStatusCode *status) = 0;
+ virtual unsigned char readPeriodScaleHdr(unsigned char bitfield_index, tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ kNumZeroLatchElements = 20,
+ } tZeroLatch_IfaceConstants;
+
+ virtual void writeZeroLatch(unsigned char bitfield_index, bool value, tRioStatusCode *status) = 0;
+ virtual bool readZeroLatch(unsigned char bitfield_index, tRioStatusCode *status) = 0;
+
+
+
+
+ typedef enum
+ {
+ kNumHdrRegisters = 10,
+ } tHdr_IfaceConstants;
+
+ virtual void writeHdr(unsigned char reg_index, unsigned short value, tRioStatusCode *status) = 0;
+ virtual unsigned short readHdr(unsigned char reg_index, tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ kNumMXPRegisters = 10,
+ } tMXP_IfaceConstants;
+
+ virtual void writeMXP(unsigned char reg_index, unsigned short value, tRioStatusCode *status) = 0;
+ virtual unsigned short readMXP(unsigned char reg_index, tRioStatusCode *status) = 0;
+
+
+private:
+ tPWM(const tPWM&);
+ void operator=(const tPWM&);
+};
+
+}
+}
+
+#endif // __nFRC_2015_1_0_A_PWM_h__
diff --git a/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRoboRIO_FPGANamespace/tPower.h b/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRoboRIO_FPGANamespace/tPower.h
new file mode 100644
index 0000000..6084c66
--- /dev/null
+++ b/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRoboRIO_FPGANamespace/tPower.h
@@ -0,0 +1,217 @@
+// Copyright (c) National Instruments 2008. All Rights Reserved.
+// Do Not Edit... this file is generated!
+
+#ifndef __nFRC_2015_1_0_A_Power_h__
+#define __nFRC_2015_1_0_A_Power_h__
+
+#include "tSystemInterface.h"
+
+namespace nFPGA
+{
+namespace nFRC_2015_1_0_A
+{
+
+class tPower
+{
+public:
+ tPower(){}
+ virtual ~tPower(){}
+
+ virtual tSystemInterface* getSystemInterface() = 0;
+ static tPower* create(tRioStatusCode *status);
+
+ typedef enum
+ {
+ kNumSystems = 1,
+ } tIfaceConstants;
+
+ typedef
+ union{
+ struct{
+#ifdef __vxworks
+ unsigned User3V3 : 8;
+ unsigned User5V : 8;
+ unsigned User6V : 8;
+#else
+ unsigned User6V : 8;
+ unsigned User5V : 8;
+ unsigned User3V3 : 8;
+#endif
+ };
+ struct{
+ unsigned value : 24;
+ };
+ } tStatus;
+ typedef
+ union{
+ struct{
+#ifdef __vxworks
+ unsigned OverCurrentFaultCount3V3 : 10;
+ unsigned OverCurrentFaultCount5V : 10;
+ unsigned OverCurrentFaultCount6V : 10;
+#else
+ unsigned OverCurrentFaultCount6V : 10;
+ unsigned OverCurrentFaultCount5V : 10;
+ unsigned OverCurrentFaultCount3V3 : 10;
+#endif
+ };
+ struct{
+ unsigned value : 30;
+ };
+ } tOverCurrentFaultCounts;
+ typedef
+ union{
+ struct{
+#ifdef __vxworks
+ unsigned User3V3 : 1;
+ unsigned User5V : 1;
+ unsigned User6V : 1;
+#else
+ unsigned User6V : 1;
+ unsigned User5V : 1;
+ unsigned User3V3 : 1;
+#endif
+ };
+ struct{
+ unsigned value : 3;
+ };
+ } tDisable;
+
+
+
+ typedef enum
+ {
+ } tUserVoltage3V3_IfaceConstants;
+
+ virtual unsigned short readUserVoltage3V3(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tStatus_IfaceConstants;
+
+ virtual tStatus readStatus(tRioStatusCode *status) = 0;
+ virtual unsigned char readStatus_User3V3(tRioStatusCode *status) = 0;
+ virtual unsigned char readStatus_User5V(tRioStatusCode *status) = 0;
+ virtual unsigned char readStatus_User6V(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tUserVoltage6V_IfaceConstants;
+
+ virtual unsigned short readUserVoltage6V(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tOnChipTemperature_IfaceConstants;
+
+ virtual unsigned short readOnChipTemperature(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tUserVoltage5V_IfaceConstants;
+
+ virtual unsigned short readUserVoltage5V(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tResetFaultCounts_IfaceConstants;
+
+ virtual void strobeResetFaultCounts(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tOverCurrentFaultCounts_IfaceConstants;
+
+ virtual tOverCurrentFaultCounts readOverCurrentFaultCounts(tRioStatusCode *status) = 0;
+ virtual unsigned short readOverCurrentFaultCounts_OverCurrentFaultCount3V3(tRioStatusCode *status) = 0;
+ virtual unsigned short readOverCurrentFaultCounts_OverCurrentFaultCount5V(tRioStatusCode *status) = 0;
+ virtual unsigned short readOverCurrentFaultCounts_OverCurrentFaultCount6V(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tIntegratedIO_IfaceConstants;
+
+ virtual unsigned short readIntegratedIO(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tMXP_DIOVoltage_IfaceConstants;
+
+ virtual unsigned short readMXP_DIOVoltage(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tUserCurrent3V3_IfaceConstants;
+
+ virtual unsigned short readUserCurrent3V3(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tVinVoltage_IfaceConstants;
+
+ virtual unsigned short readVinVoltage(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tUserCurrent6V_IfaceConstants;
+
+ virtual unsigned short readUserCurrent6V(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tUserCurrent5V_IfaceConstants;
+
+ virtual unsigned short readUserCurrent5V(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tAOVoltage_IfaceConstants;
+
+ virtual unsigned short readAOVoltage(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tVinCurrent_IfaceConstants;
+
+ virtual unsigned short readVinCurrent(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tDisable_IfaceConstants;
+
+ virtual void writeDisable(tDisable value, tRioStatusCode *status) = 0;
+ virtual void writeDisable_User3V3(bool value, tRioStatusCode *status) = 0;
+ virtual void writeDisable_User5V(bool value, tRioStatusCode *status) = 0;
+ virtual void writeDisable_User6V(bool value, tRioStatusCode *status) = 0;
+ virtual tDisable readDisable(tRioStatusCode *status) = 0;
+ virtual bool readDisable_User3V3(tRioStatusCode *status) = 0;
+ virtual bool readDisable_User5V(tRioStatusCode *status) = 0;
+ virtual bool readDisable_User6V(tRioStatusCode *status) = 0;
+
+
+
+
+private:
+ tPower(const tPower&);
+ void operator=(const tPower&);
+};
+
+}
+}
+
+#endif // __nFRC_2015_1_0_A_Power_h__
diff --git a/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRoboRIO_FPGANamespace/tRelay.h b/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRoboRIO_FPGANamespace/tRelay.h
new file mode 100644
index 0000000..e3f0286
--- /dev/null
+++ b/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRoboRIO_FPGANamespace/tRelay.h
@@ -0,0 +1,68 @@
+// Copyright (c) National Instruments 2008. All Rights Reserved.
+// Do Not Edit... this file is generated!
+
+#ifndef __nFRC_2015_1_0_A_Relay_h__
+#define __nFRC_2015_1_0_A_Relay_h__
+
+#include "tSystemInterface.h"
+
+namespace nFPGA
+{
+namespace nFRC_2015_1_0_A
+{
+
+class tRelay
+{
+public:
+ tRelay(){}
+ virtual ~tRelay(){}
+
+ virtual tSystemInterface* getSystemInterface() = 0;
+ static tRelay* create(tRioStatusCode *status);
+
+ typedef enum
+ {
+ kNumSystems = 1,
+ } tIfaceConstants;
+
+ typedef
+ union{
+ struct{
+#ifdef __vxworks
+ unsigned Forward : 4;
+ unsigned Reverse : 4;
+#else
+ unsigned Reverse : 4;
+ unsigned Forward : 4;
+#endif
+ };
+ struct{
+ unsigned value : 8;
+ };
+ } tValue;
+
+
+
+ typedef enum
+ {
+ } tValue_IfaceConstants;
+
+ virtual void writeValue(tValue value, tRioStatusCode *status) = 0;
+ virtual void writeValue_Forward(unsigned char value, tRioStatusCode *status) = 0;
+ virtual void writeValue_Reverse(unsigned char value, tRioStatusCode *status) = 0;
+ virtual tValue readValue(tRioStatusCode *status) = 0;
+ virtual unsigned char readValue_Forward(tRioStatusCode *status) = 0;
+ virtual unsigned char readValue_Reverse(tRioStatusCode *status) = 0;
+
+
+
+
+private:
+ tRelay(const tRelay&);
+ void operator=(const tRelay&);
+};
+
+}
+}
+
+#endif // __nFRC_2015_1_0_A_Relay_h__
diff --git a/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRoboRIO_FPGANamespace/tSPI.h b/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRoboRIO_FPGANamespace/tSPI.h
new file mode 100644
index 0000000..93a2b3e
--- /dev/null
+++ b/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRoboRIO_FPGANamespace/tSPI.h
@@ -0,0 +1,68 @@
+// Copyright (c) National Instruments 2008. All Rights Reserved.
+// Do Not Edit... this file is generated!
+
+#ifndef __nFRC_2015_1_0_A_SPI_h__
+#define __nFRC_2015_1_0_A_SPI_h__
+
+#include "tSystemInterface.h"
+
+namespace nFPGA
+{
+namespace nFRC_2015_1_0_A
+{
+
+class tSPI
+{
+public:
+ tSPI(){}
+ virtual ~tSPI(){}
+
+ virtual tSystemInterface* getSystemInterface() = 0;
+ static tSPI* create(tRioStatusCode *status);
+
+ typedef enum
+ {
+ kNumSystems = 1,
+ } tIfaceConstants;
+
+ typedef
+ union{
+ struct{
+#ifdef __vxworks
+ unsigned Hdr : 4;
+ unsigned MXP : 1;
+#else
+ unsigned MXP : 1;
+ unsigned Hdr : 4;
+#endif
+ };
+ struct{
+ unsigned value : 5;
+ };
+ } tChipSelectActiveHigh;
+
+
+
+ typedef enum
+ {
+ } tChipSelectActiveHigh_IfaceConstants;
+
+ virtual void writeChipSelectActiveHigh(tChipSelectActiveHigh value, tRioStatusCode *status) = 0;
+ virtual void writeChipSelectActiveHigh_Hdr(unsigned char value, tRioStatusCode *status) = 0;
+ virtual void writeChipSelectActiveHigh_MXP(unsigned char value, tRioStatusCode *status) = 0;
+ virtual tChipSelectActiveHigh readChipSelectActiveHigh(tRioStatusCode *status) = 0;
+ virtual unsigned char readChipSelectActiveHigh_Hdr(tRioStatusCode *status) = 0;
+ virtual unsigned char readChipSelectActiveHigh_MXP(tRioStatusCode *status) = 0;
+
+
+
+
+private:
+ tSPI(const tSPI&);
+ void operator=(const tSPI&);
+};
+
+}
+}
+
+#endif // __nFRC_2015_1_0_A_SPI_h__
diff --git a/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRoboRIO_FPGANamespace/tSysWatchdog.h b/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRoboRIO_FPGANamespace/tSysWatchdog.h
new file mode 100644
index 0000000..67dcd13
--- /dev/null
+++ b/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRoboRIO_FPGANamespace/tSysWatchdog.h
@@ -0,0 +1,108 @@
+// Copyright (c) National Instruments 2008. All Rights Reserved.
+// Do Not Edit... this file is generated!
+
+#ifndef __nFRC_2015_1_0_A_SysWatchdog_h__
+#define __nFRC_2015_1_0_A_SysWatchdog_h__
+
+#include "tSystemInterface.h"
+
+namespace nFPGA
+{
+namespace nFRC_2015_1_0_A
+{
+
+class tSysWatchdog
+{
+public:
+ tSysWatchdog(){}
+ virtual ~tSysWatchdog(){}
+
+ virtual tSystemInterface* getSystemInterface() = 0;
+ static tSysWatchdog* create(tRioStatusCode *status);
+
+ typedef enum
+ {
+ kNumSystems = 1,
+ } tIfaceConstants;
+
+ typedef
+ union{
+ struct{
+#ifdef __vxworks
+ unsigned SystemActive : 1;
+ unsigned PowerAlive : 1;
+ unsigned SysDisableCount : 15;
+ unsigned PowerDisableCount : 15;
+#else
+ unsigned PowerDisableCount : 15;
+ unsigned SysDisableCount : 15;
+ unsigned PowerAlive : 1;
+ unsigned SystemActive : 1;
+#endif
+ };
+ struct{
+ unsigned value : 32;
+ };
+ } tStatus;
+
+
+
+ typedef enum
+ {
+ } tStatus_IfaceConstants;
+
+ virtual tStatus readStatus(tRioStatusCode *status) = 0;
+ virtual bool readStatus_SystemActive(tRioStatusCode *status) = 0;
+ virtual bool readStatus_PowerAlive(tRioStatusCode *status) = 0;
+ virtual unsigned short readStatus_SysDisableCount(tRioStatusCode *status) = 0;
+ virtual unsigned short readStatus_PowerDisableCount(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tCommand_IfaceConstants;
+
+ virtual void writeCommand(unsigned short value, tRioStatusCode *status) = 0;
+ virtual unsigned short readCommand(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tChallenge_IfaceConstants;
+
+ virtual unsigned char readChallenge(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tActive_IfaceConstants;
+
+ virtual void writeActive(bool value, tRioStatusCode *status) = 0;
+ virtual bool readActive(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tTimer_IfaceConstants;
+
+ virtual unsigned int readTimer(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tForcedKills_IfaceConstants;
+
+ virtual unsigned short readForcedKills(tRioStatusCode *status) = 0;
+
+
+
+
+private:
+ tSysWatchdog(const tSysWatchdog&);
+ void operator=(const tSysWatchdog&);
+};
+
+}
+}
+
+#endif // __nFRC_2015_1_0_A_SysWatchdog_h__
diff --git a/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRuntimeFPGANamespace/nInterfaceGlobals.h b/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRuntimeFPGANamespace/nInterfaceGlobals.h
new file mode 100644
index 0000000..f34cc74
--- /dev/null
+++ b/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRuntimeFPGANamespace/nInterfaceGlobals.h
@@ -0,0 +1,15 @@
+// Copyright (c) National Instruments 2008. All Rights Reserved.
+// Do Not Edit... this file is generated!
+
+#ifndef __nFRC_2012_1_6_4_nInterfaceGlobals_h__
+#define __nFRC_2012_1_6_4_nInterfaceGlobals_h__
+
+namespace nFPGA
+{
+namespace nFRC_2012_1_6_4
+{
+ extern unsigned int g_currentTargetClass;
+}
+}
+
+#endif // __nFRC_2012_1_6_4_nInterfaceGlobals_h__
diff --git a/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRuntimeFPGANamespace/tAI.h b/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRuntimeFPGANamespace/tAI.h
new file mode 100644
index 0000000..78c423a
--- /dev/null
+++ b/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRuntimeFPGANamespace/tAI.h
@@ -0,0 +1,149 @@
+// Copyright (c) National Instruments 2008. All Rights Reserved.
+// Do Not Edit... this file is generated!
+
+#ifndef __nFRC_2012_1_6_4_AI_h__
+#define __nFRC_2012_1_6_4_AI_h__
+
+#include "tSystemInterface.h"
+
+namespace nFPGA
+{
+namespace nFRC_2012_1_6_4
+{
+
+class tAI
+{
+public:
+ tAI(){}
+ virtual ~tAI(){}
+
+ virtual tSystemInterface* getSystemInterface() = 0;
+ static tAI* create(unsigned char sys_index, tRioStatusCode *status);
+ virtual unsigned char getSystemIndex() = 0;
+
+
+ typedef enum
+ {
+ kNumSystems = 2,
+ } tIfaceConstants;
+
+ typedef
+ union{
+ struct{
+#ifdef __vxworks
+ unsigned Channel : 3;
+ unsigned Module : 1;
+ unsigned Averaged : 1;
+#else
+ unsigned Averaged : 1;
+ unsigned Module : 1;
+ unsigned Channel : 3;
+#endif
+ };
+ struct{
+ unsigned value : 5;
+ };
+ } tReadSelect;
+ typedef
+ union{
+ struct{
+#ifdef __vxworks
+ unsigned ScanSize : 3;
+ unsigned ConvertRate : 26;
+#else
+ unsigned ConvertRate : 26;
+ unsigned ScanSize : 3;
+#endif
+ };
+ struct{
+ unsigned value : 29;
+ };
+ } tConfig;
+
+
+ typedef enum
+ {
+ } tConfig_IfaceConstants;
+
+ virtual void writeConfig(tConfig value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_ScanSize(unsigned char value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_ConvertRate(unsigned int value, tRioStatusCode *status) = 0;
+ virtual tConfig readConfig(tRioStatusCode *status) = 0;
+ virtual unsigned char readConfig_ScanSize(tRioStatusCode *status) = 0;
+ virtual unsigned int readConfig_ConvertRate(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tLoopTiming_IfaceConstants;
+
+ virtual unsigned int readLoopTiming(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ kNumOversampleBitsElements = 8,
+ } tOversampleBits_IfaceConstants;
+
+ virtual void writeOversampleBits(unsigned char bitfield_index, unsigned char value, tRioStatusCode *status) = 0;
+ virtual unsigned char readOversampleBits(unsigned char bitfield_index, tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ kNumAverageBitsElements = 8,
+ } tAverageBits_IfaceConstants;
+
+ virtual void writeAverageBits(unsigned char bitfield_index, unsigned char value, tRioStatusCode *status) = 0;
+ virtual unsigned char readAverageBits(unsigned char bitfield_index, tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ kNumScanListElements = 8,
+ } tScanList_IfaceConstants;
+
+ virtual void writeScanList(unsigned char bitfield_index, unsigned char value, tRioStatusCode *status) = 0;
+ virtual unsigned char readScanList(unsigned char bitfield_index, tRioStatusCode *status) = 0;
+
+
+
+ typedef enum
+ {
+ } tOutput_IfaceConstants;
+
+ virtual signed int readOutput(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tLatchOutput_IfaceConstants;
+
+ virtual void strobeLatchOutput(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tReadSelect_IfaceConstants;
+
+ virtual void writeReadSelect(tReadSelect value, tRioStatusCode *status) = 0;
+ virtual void writeReadSelect_Channel(unsigned char value, tRioStatusCode *status) = 0;
+ virtual void writeReadSelect_Module(unsigned char value, tRioStatusCode *status) = 0;
+ virtual void writeReadSelect_Averaged(bool value, tRioStatusCode *status) = 0;
+ virtual tReadSelect readReadSelect(tRioStatusCode *status) = 0;
+ virtual unsigned char readReadSelect_Channel(tRioStatusCode *status) = 0;
+ virtual unsigned char readReadSelect_Module(tRioStatusCode *status) = 0;
+ virtual bool readReadSelect_Averaged(tRioStatusCode *status) = 0;
+
+
+
+
+private:
+ tAI(const tAI&);
+ void operator=(const tAI&);
+};
+
+}
+}
+
+#endif // __nFRC_2012_1_6_4_AI_h__
diff --git a/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRuntimeFPGANamespace/tAccumulator.h b/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRuntimeFPGANamespace/tAccumulator.h
new file mode 100644
index 0000000..1a0972a
--- /dev/null
+++ b/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRuntimeFPGANamespace/tAccumulator.h
@@ -0,0 +1,87 @@
+// Copyright (c) National Instruments 2008. All Rights Reserved.
+// Do Not Edit... this file is generated!
+
+#ifndef __nFRC_2012_1_6_4_Accumulator_h__
+#define __nFRC_2012_1_6_4_Accumulator_h__
+
+#include "tSystemInterface.h"
+
+namespace nFPGA
+{
+namespace nFRC_2012_1_6_4
+{
+
+class tAccumulator
+{
+public:
+ tAccumulator(){}
+ virtual ~tAccumulator(){}
+
+ virtual tSystemInterface* getSystemInterface() = 0;
+ static tAccumulator* create(unsigned char sys_index, tRioStatusCode *status);
+ virtual unsigned char getSystemIndex() = 0;
+
+
+ typedef enum
+ {
+ kNumSystems = 2,
+ } tIfaceConstants;
+
+ typedef
+ union{
+ struct{
+ signed long long Value;
+ unsigned Count : 32;
+ };
+ struct{
+ unsigned value : 32;
+ unsigned value2 : 32;
+ unsigned value3 : 32;
+ };
+ } tOutput;
+
+
+ typedef enum
+ {
+ } tOutput_IfaceConstants;
+
+ virtual tOutput readOutput(tRioStatusCode *status) = 0;
+ virtual signed long long readOutput_Value(tRioStatusCode *status) = 0;
+ virtual unsigned int readOutput_Count(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tCenter_IfaceConstants;
+
+ virtual void writeCenter(signed int value, tRioStatusCode *status) = 0;
+ virtual signed int readCenter(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tDeadband_IfaceConstants;
+
+ virtual void writeDeadband(signed int value, tRioStatusCode *status) = 0;
+ virtual signed int readDeadband(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tReset_IfaceConstants;
+
+ virtual void strobeReset(tRioStatusCode *status) = 0;
+
+
+
+
+
+private:
+ tAccumulator(const tAccumulator&);
+ void operator=(const tAccumulator&);
+};
+
+}
+}
+
+#endif // __nFRC_2012_1_6_4_Accumulator_h__
diff --git a/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRuntimeFPGANamespace/tAlarm.h b/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRuntimeFPGANamespace/tAlarm.h
new file mode 100644
index 0000000..f3eb33f
--- /dev/null
+++ b/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRuntimeFPGANamespace/tAlarm.h
@@ -0,0 +1,57 @@
+// Copyright (c) National Instruments 2008. All Rights Reserved.
+// Do Not Edit... this file is generated!
+
+#ifndef __nFRC_2012_1_6_4_Alarm_h__
+#define __nFRC_2012_1_6_4_Alarm_h__
+
+#include "tSystemInterface.h"
+
+namespace nFPGA
+{
+namespace nFRC_2012_1_6_4
+{
+
+class tAlarm
+{
+public:
+ tAlarm(){}
+ virtual ~tAlarm(){}
+
+ virtual tSystemInterface* getSystemInterface() = 0;
+ static tAlarm* create(tRioStatusCode *status);
+
+ typedef enum
+ {
+ kNumSystems = 1,
+ } tIfaceConstants;
+
+
+
+
+ typedef enum
+ {
+ } tEnable_IfaceConstants;
+
+ virtual void writeEnable(bool value, tRioStatusCode *status) = 0;
+ virtual bool readEnable(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tTriggerTime_IfaceConstants;
+
+ virtual void writeTriggerTime(unsigned int value, tRioStatusCode *status) = 0;
+ virtual unsigned int readTriggerTime(tRioStatusCode *status) = 0;
+
+
+
+
+private:
+ tAlarm(const tAlarm&);
+ void operator=(const tAlarm&);
+};
+
+}
+}
+
+#endif // __nFRC_2012_1_6_4_Alarm_h__
diff --git a/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRuntimeFPGANamespace/tAnalogTrigger.h b/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRuntimeFPGANamespace/tAnalogTrigger.h
new file mode 100644
index 0000000..43150f7
--- /dev/null
+++ b/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRuntimeFPGANamespace/tAnalogTrigger.h
@@ -0,0 +1,133 @@
+// Copyright (c) National Instruments 2008. All Rights Reserved.
+// Do Not Edit... this file is generated!
+
+#ifndef __nFRC_2012_1_6_4_AnalogTrigger_h__
+#define __nFRC_2012_1_6_4_AnalogTrigger_h__
+
+#include "tSystemInterface.h"
+
+namespace nFPGA
+{
+namespace nFRC_2012_1_6_4
+{
+
+class tAnalogTrigger
+{
+public:
+ tAnalogTrigger(){}
+ virtual ~tAnalogTrigger(){}
+
+ virtual tSystemInterface* getSystemInterface() = 0;
+ static tAnalogTrigger* create(unsigned char sys_index, tRioStatusCode *status);
+ virtual unsigned char getSystemIndex() = 0;
+
+
+ typedef enum
+ {
+ kNumSystems = 8,
+ } tIfaceConstants;
+
+ typedef
+ union{
+ struct{
+#ifdef __vxworks
+ unsigned InHysteresis : 1;
+ unsigned OverLimit : 1;
+ unsigned Rising : 1;
+ unsigned Falling : 1;
+#else
+ unsigned Falling : 1;
+ unsigned Rising : 1;
+ unsigned OverLimit : 1;
+ unsigned InHysteresis : 1;
+#endif
+ };
+ struct{
+ unsigned value : 4;
+ };
+ } tOutput;
+ typedef
+ union{
+ struct{
+#ifdef __vxworks
+ unsigned Channel : 3;
+ unsigned Module : 1;
+ unsigned Averaged : 1;
+ unsigned Filter : 1;
+ unsigned FloatingRollover : 1;
+ signed RolloverLimit : 8;
+#else
+ signed RolloverLimit : 8;
+ unsigned FloatingRollover : 1;
+ unsigned Filter : 1;
+ unsigned Averaged : 1;
+ unsigned Module : 1;
+ unsigned Channel : 3;
+#endif
+ };
+ struct{
+ unsigned value : 15;
+ };
+ } tSourceSelect;
+
+
+ typedef enum
+ {
+ } tSourceSelect_IfaceConstants;
+
+ virtual void writeSourceSelect(tSourceSelect value, tRioStatusCode *status) = 0;
+ virtual void writeSourceSelect_Channel(unsigned char value, tRioStatusCode *status) = 0;
+ virtual void writeSourceSelect_Module(unsigned char value, tRioStatusCode *status) = 0;
+ virtual void writeSourceSelect_Averaged(bool value, tRioStatusCode *status) = 0;
+ virtual void writeSourceSelect_Filter(bool value, tRioStatusCode *status) = 0;
+ virtual void writeSourceSelect_FloatingRollover(bool value, tRioStatusCode *status) = 0;
+ virtual void writeSourceSelect_RolloverLimit(signed short value, tRioStatusCode *status) = 0;
+ virtual tSourceSelect readSourceSelect(tRioStatusCode *status) = 0;
+ virtual unsigned char readSourceSelect_Channel(tRioStatusCode *status) = 0;
+ virtual unsigned char readSourceSelect_Module(tRioStatusCode *status) = 0;
+ virtual bool readSourceSelect_Averaged(tRioStatusCode *status) = 0;
+ virtual bool readSourceSelect_Filter(tRioStatusCode *status) = 0;
+ virtual bool readSourceSelect_FloatingRollover(tRioStatusCode *status) = 0;
+ virtual signed short readSourceSelect_RolloverLimit(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tUpperLimit_IfaceConstants;
+
+ virtual void writeUpperLimit(signed int value, tRioStatusCode *status) = 0;
+ virtual signed int readUpperLimit(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tLowerLimit_IfaceConstants;
+
+ virtual void writeLowerLimit(signed int value, tRioStatusCode *status) = 0;
+ virtual signed int readLowerLimit(tRioStatusCode *status) = 0;
+
+
+
+ typedef enum
+ {
+ kNumOutputElements = 8,
+ } tOutput_IfaceConstants;
+
+ virtual tOutput readOutput(unsigned char bitfield_index, tRioStatusCode *status) = 0;
+ virtual bool readOutput_InHysteresis(unsigned char bitfield_index, tRioStatusCode *status) = 0;
+ virtual bool readOutput_OverLimit(unsigned char bitfield_index, tRioStatusCode *status) = 0;
+ virtual bool readOutput_Rising(unsigned char bitfield_index, tRioStatusCode *status) = 0;
+ virtual bool readOutput_Falling(unsigned char bitfield_index, tRioStatusCode *status) = 0;
+
+
+
+
+private:
+ tAnalogTrigger(const tAnalogTrigger&);
+ void operator=(const tAnalogTrigger&);
+};
+
+}
+}
+
+#endif // __nFRC_2012_1_6_4_AnalogTrigger_h__
diff --git a/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRuntimeFPGANamespace/tCounter.h b/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRuntimeFPGANamespace/tCounter.h
new file mode 100644
index 0000000..b23a7f0
--- /dev/null
+++ b/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRuntimeFPGANamespace/tCounter.h
@@ -0,0 +1,219 @@
+// Copyright (c) National Instruments 2008. All Rights Reserved.
+// Do Not Edit... this file is generated!
+
+#ifndef __nFRC_2012_1_6_4_Counter_h__
+#define __nFRC_2012_1_6_4_Counter_h__
+
+#include "tSystemInterface.h"
+
+namespace nFPGA
+{
+namespace nFRC_2012_1_6_4
+{
+
+class tCounter
+{
+public:
+ tCounter(){}
+ virtual ~tCounter(){}
+
+ virtual tSystemInterface* getSystemInterface() = 0;
+ static tCounter* create(unsigned char sys_index, tRioStatusCode *status);
+ virtual unsigned char getSystemIndex() = 0;
+
+
+ typedef enum
+ {
+ kNumSystems = 8,
+ } tIfaceConstants;
+
+ typedef
+ union{
+ struct{
+#ifdef __vxworks
+ unsigned Direction : 1;
+ signed Value : 31;
+#else
+ signed Value : 31;
+ unsigned Direction : 1;
+#endif
+ };
+ struct{
+ unsigned value : 32;
+ };
+ } tOutput;
+ typedef
+ union{
+ struct{
+#ifdef __vxworks
+ unsigned UpSource_Channel : 4;
+ unsigned UpSource_Module : 1;
+ unsigned UpSource_AnalogTrigger : 1;
+ unsigned DownSource_Channel : 4;
+ unsigned DownSource_Module : 1;
+ unsigned DownSource_AnalogTrigger : 1;
+ unsigned IndexSource_Channel : 4;
+ unsigned IndexSource_Module : 1;
+ unsigned IndexSource_AnalogTrigger : 1;
+ unsigned IndexActiveHigh : 1;
+ unsigned UpRisingEdge : 1;
+ unsigned UpFallingEdge : 1;
+ unsigned DownRisingEdge : 1;
+ unsigned DownFallingEdge : 1;
+ unsigned Mode : 2;
+ unsigned PulseLengthThreshold : 6;
+ unsigned Enable : 1;
+#else
+ unsigned Enable : 1;
+ unsigned PulseLengthThreshold : 6;
+ unsigned Mode : 2;
+ unsigned DownFallingEdge : 1;
+ unsigned DownRisingEdge : 1;
+ unsigned UpFallingEdge : 1;
+ unsigned UpRisingEdge : 1;
+ unsigned IndexActiveHigh : 1;
+ unsigned IndexSource_AnalogTrigger : 1;
+ unsigned IndexSource_Module : 1;
+ unsigned IndexSource_Channel : 4;
+ unsigned DownSource_AnalogTrigger : 1;
+ unsigned DownSource_Module : 1;
+ unsigned DownSource_Channel : 4;
+ unsigned UpSource_AnalogTrigger : 1;
+ unsigned UpSource_Module : 1;
+ unsigned UpSource_Channel : 4;
+#endif
+ };
+ struct{
+ unsigned value : 32;
+ };
+ } tConfig;
+ typedef
+ union{
+ struct{
+#ifdef __vxworks
+ unsigned Period : 23;
+ signed Count : 8;
+ unsigned Stalled : 1;
+#else
+ unsigned Stalled : 1;
+ signed Count : 8;
+ unsigned Period : 23;
+#endif
+ };
+ struct{
+ unsigned value : 32;
+ };
+ } tTimerOutput;
+ typedef
+ union{
+ struct{
+#ifdef __vxworks
+ unsigned StallPeriod : 24;
+ unsigned AverageSize : 7;
+ unsigned UpdateWhenEmpty : 1;
+#else
+ unsigned UpdateWhenEmpty : 1;
+ unsigned AverageSize : 7;
+ unsigned StallPeriod : 24;
+#endif
+ };
+ struct{
+ unsigned value : 32;
+ };
+ } tTimerConfig;
+
+
+ typedef enum
+ {
+ } tOutput_IfaceConstants;
+
+ virtual tOutput readOutput(tRioStatusCode *status) = 0;
+ virtual bool readOutput_Direction(tRioStatusCode *status) = 0;
+ virtual signed int readOutput_Value(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tConfig_IfaceConstants;
+
+ virtual void writeConfig(tConfig value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_UpSource_Channel(unsigned char value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_UpSource_Module(unsigned char value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_UpSource_AnalogTrigger(bool value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_DownSource_Channel(unsigned char value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_DownSource_Module(unsigned char value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_DownSource_AnalogTrigger(bool value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_IndexSource_Channel(unsigned char value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_IndexSource_Module(unsigned char value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_IndexSource_AnalogTrigger(bool value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_IndexActiveHigh(bool value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_UpRisingEdge(bool value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_UpFallingEdge(bool value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_DownRisingEdge(bool value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_DownFallingEdge(bool value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_Mode(unsigned char value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_PulseLengthThreshold(unsigned short value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_Enable(bool value, tRioStatusCode *status) = 0;
+ virtual tConfig readConfig(tRioStatusCode *status) = 0;
+ virtual unsigned char readConfig_UpSource_Channel(tRioStatusCode *status) = 0;
+ virtual unsigned char readConfig_UpSource_Module(tRioStatusCode *status) = 0;
+ virtual bool readConfig_UpSource_AnalogTrigger(tRioStatusCode *status) = 0;
+ virtual unsigned char readConfig_DownSource_Channel(tRioStatusCode *status) = 0;
+ virtual unsigned char readConfig_DownSource_Module(tRioStatusCode *status) = 0;
+ virtual bool readConfig_DownSource_AnalogTrigger(tRioStatusCode *status) = 0;
+ virtual unsigned char readConfig_IndexSource_Channel(tRioStatusCode *status) = 0;
+ virtual unsigned char readConfig_IndexSource_Module(tRioStatusCode *status) = 0;
+ virtual bool readConfig_IndexSource_AnalogTrigger(tRioStatusCode *status) = 0;
+ virtual bool readConfig_IndexActiveHigh(tRioStatusCode *status) = 0;
+ virtual bool readConfig_UpRisingEdge(tRioStatusCode *status) = 0;
+ virtual bool readConfig_UpFallingEdge(tRioStatusCode *status) = 0;
+ virtual bool readConfig_DownRisingEdge(tRioStatusCode *status) = 0;
+ virtual bool readConfig_DownFallingEdge(tRioStatusCode *status) = 0;
+ virtual unsigned char readConfig_Mode(tRioStatusCode *status) = 0;
+ virtual unsigned short readConfig_PulseLengthThreshold(tRioStatusCode *status) = 0;
+ virtual bool readConfig_Enable(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tTimerOutput_IfaceConstants;
+
+ virtual tTimerOutput readTimerOutput(tRioStatusCode *status) = 0;
+ virtual unsigned int readTimerOutput_Period(tRioStatusCode *status) = 0;
+ virtual signed char readTimerOutput_Count(tRioStatusCode *status) = 0;
+ virtual bool readTimerOutput_Stalled(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tReset_IfaceConstants;
+
+ virtual void strobeReset(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tTimerConfig_IfaceConstants;
+
+ virtual void writeTimerConfig(tTimerConfig value, tRioStatusCode *status) = 0;
+ virtual void writeTimerConfig_StallPeriod(unsigned int value, tRioStatusCode *status) = 0;
+ virtual void writeTimerConfig_AverageSize(unsigned char value, tRioStatusCode *status) = 0;
+ virtual void writeTimerConfig_UpdateWhenEmpty(bool value, tRioStatusCode *status) = 0;
+ virtual tTimerConfig readTimerConfig(tRioStatusCode *status) = 0;
+ virtual unsigned int readTimerConfig_StallPeriod(tRioStatusCode *status) = 0;
+ virtual unsigned char readTimerConfig_AverageSize(tRioStatusCode *status) = 0;
+ virtual bool readTimerConfig_UpdateWhenEmpty(tRioStatusCode *status) = 0;
+
+
+
+
+
+private:
+ tCounter(const tCounter&);
+ void operator=(const tCounter&);
+};
+
+}
+}
+
+#endif // __nFRC_2012_1_6_4_Counter_h__
diff --git a/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRuntimeFPGANamespace/tDIO.h b/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRuntimeFPGANamespace/tDIO.h
new file mode 100644
index 0000000..babb691
--- /dev/null
+++ b/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRuntimeFPGANamespace/tDIO.h
@@ -0,0 +1,330 @@
+// Copyright (c) National Instruments 2008. All Rights Reserved.
+// Do Not Edit... this file is generated!
+
+#ifndef __nFRC_2012_1_6_4_DIO_h__
+#define __nFRC_2012_1_6_4_DIO_h__
+
+#include "tSystemInterface.h"
+
+namespace nFPGA
+{
+namespace nFRC_2012_1_6_4
+{
+
+class tDIO
+{
+public:
+ tDIO(){}
+ virtual ~tDIO(){}
+
+ virtual tSystemInterface* getSystemInterface() = 0;
+ static tDIO* create(unsigned char sys_index, tRioStatusCode *status);
+ virtual unsigned char getSystemIndex() = 0;
+
+
+ typedef enum
+ {
+ kNumSystems = 2,
+ } tIfaceConstants;
+
+ typedef
+ union{
+ struct{
+#ifdef __vxworks
+ unsigned Period : 16;
+ unsigned MinHigh : 16;
+#else
+ unsigned MinHigh : 16;
+ unsigned Period : 16;
+#endif
+ };
+ struct{
+ unsigned value : 32;
+ };
+ } tPWMConfig;
+ typedef
+ union{
+ struct{
+#ifdef __vxworks
+ unsigned RelayFwd : 8;
+ unsigned RelayRev : 8;
+ unsigned I2CHeader : 4;
+#else
+ unsigned I2CHeader : 4;
+ unsigned RelayRev : 8;
+ unsigned RelayFwd : 8;
+#endif
+ };
+ struct{
+ unsigned value : 20;
+ };
+ } tSlowValue;
+ typedef
+ union{
+ struct{
+#ifdef __vxworks
+ unsigned Transaction : 1;
+ unsigned Done : 1;
+ unsigned Aborted : 1;
+ unsigned DataReceivedHigh : 24;
+#else
+ unsigned DataReceivedHigh : 24;
+ unsigned Aborted : 1;
+ unsigned Done : 1;
+ unsigned Transaction : 1;
+#endif
+ };
+ struct{
+ unsigned value : 27;
+ };
+ } tI2CStatus;
+ typedef
+ union{
+ struct{
+#ifdef __vxworks
+ unsigned Address : 8;
+ unsigned BytesToRead : 3;
+ unsigned BytesToWrite : 3;
+ unsigned DataToSendHigh : 16;
+ unsigned BitwiseHandshake : 1;
+#else
+ unsigned BitwiseHandshake : 1;
+ unsigned DataToSendHigh : 16;
+ unsigned BytesToWrite : 3;
+ unsigned BytesToRead : 3;
+ unsigned Address : 8;
+#endif
+ };
+ struct{
+ unsigned value : 31;
+ };
+ } tI2CConfig;
+ typedef
+ union{
+ struct{
+#ifdef __vxworks
+ unsigned PeriodPower : 4;
+ unsigned OutputSelect_0 : 4;
+ unsigned OutputSelect_1 : 4;
+ unsigned OutputSelect_2 : 4;
+ unsigned OutputSelect_3 : 4;
+#else
+ unsigned OutputSelect_3 : 4;
+ unsigned OutputSelect_2 : 4;
+ unsigned OutputSelect_1 : 4;
+ unsigned OutputSelect_0 : 4;
+ unsigned PeriodPower : 4;
+#endif
+ };
+ struct{
+ unsigned value : 20;
+ };
+ } tDO_PWMConfig;
+
+
+ typedef enum
+ {
+ kNumFilterSelectElements = 16,
+ } tFilterSelect_IfaceConstants;
+
+ virtual void writeFilterSelect(unsigned char bitfield_index, unsigned char value, tRioStatusCode *status) = 0;
+ virtual unsigned char readFilterSelect(unsigned char bitfield_index, tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tI2CDataToSend_IfaceConstants;
+
+ virtual void writeI2CDataToSend(unsigned int value, tRioStatusCode *status) = 0;
+ virtual unsigned int readI2CDataToSend(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tDO_IfaceConstants;
+
+ virtual void writeDO(unsigned short value, tRioStatusCode *status) = 0;
+ virtual unsigned short readDO(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ kNumFilterPeriodElements = 3,
+ } tFilterPeriod_IfaceConstants;
+
+ virtual void writeFilterPeriod(unsigned char bitfield_index, unsigned char value, tRioStatusCode *status) = 0;
+ virtual unsigned char readFilterPeriod(unsigned char bitfield_index, tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tOutputEnable_IfaceConstants;
+
+ virtual void writeOutputEnable(unsigned short value, tRioStatusCode *status) = 0;
+ virtual unsigned short readOutputEnable(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tPulse_IfaceConstants;
+
+ virtual void writePulse(unsigned short value, tRioStatusCode *status) = 0;
+ virtual unsigned short readPulse(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tSlowValue_IfaceConstants;
+
+ virtual void writeSlowValue(tSlowValue value, tRioStatusCode *status) = 0;
+ virtual void writeSlowValue_RelayFwd(unsigned char value, tRioStatusCode *status) = 0;
+ virtual void writeSlowValue_RelayRev(unsigned char value, tRioStatusCode *status) = 0;
+ virtual void writeSlowValue_I2CHeader(unsigned char value, tRioStatusCode *status) = 0;
+ virtual tSlowValue readSlowValue(tRioStatusCode *status) = 0;
+ virtual unsigned char readSlowValue_RelayFwd(tRioStatusCode *status) = 0;
+ virtual unsigned char readSlowValue_RelayRev(tRioStatusCode *status) = 0;
+ virtual unsigned char readSlowValue_I2CHeader(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tI2CStatus_IfaceConstants;
+
+ virtual tI2CStatus readI2CStatus(tRioStatusCode *status) = 0;
+ virtual unsigned char readI2CStatus_Transaction(tRioStatusCode *status) = 0;
+ virtual bool readI2CStatus_Done(tRioStatusCode *status) = 0;
+ virtual bool readI2CStatus_Aborted(tRioStatusCode *status) = 0;
+ virtual unsigned int readI2CStatus_DataReceivedHigh(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tI2CDataReceived_IfaceConstants;
+
+ virtual unsigned int readI2CDataReceived(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tDI_IfaceConstants;
+
+ virtual unsigned short readDI(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tPulseLength_IfaceConstants;
+
+ virtual void writePulseLength(unsigned char value, tRioStatusCode *status) = 0;
+ virtual unsigned char readPulseLength(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ kNumPWMPeriodScaleElements = 10,
+ } tPWMPeriodScale_IfaceConstants;
+
+ virtual void writePWMPeriodScale(unsigned char bitfield_index, unsigned char value, tRioStatusCode *status) = 0;
+ virtual unsigned char readPWMPeriodScale(unsigned char bitfield_index, tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ kNumDO_PWMDutyCycleElements = 4,
+ } tDO_PWMDutyCycle_IfaceConstants;
+
+ virtual void writeDO_PWMDutyCycle(unsigned char bitfield_index, unsigned char value, tRioStatusCode *status) = 0;
+ virtual unsigned char readDO_PWMDutyCycle(unsigned char bitfield_index, tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tBFL_IfaceConstants;
+
+ virtual void writeBFL(bool value, tRioStatusCode *status) = 0;
+ virtual bool readBFL(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tI2CConfig_IfaceConstants;
+
+ virtual void writeI2CConfig(tI2CConfig value, tRioStatusCode *status) = 0;
+ virtual void writeI2CConfig_Address(unsigned char value, tRioStatusCode *status) = 0;
+ virtual void writeI2CConfig_BytesToRead(unsigned char value, tRioStatusCode *status) = 0;
+ virtual void writeI2CConfig_BytesToWrite(unsigned char value, tRioStatusCode *status) = 0;
+ virtual void writeI2CConfig_DataToSendHigh(unsigned short value, tRioStatusCode *status) = 0;
+ virtual void writeI2CConfig_BitwiseHandshake(bool value, tRioStatusCode *status) = 0;
+ virtual tI2CConfig readI2CConfig(tRioStatusCode *status) = 0;
+ virtual unsigned char readI2CConfig_Address(tRioStatusCode *status) = 0;
+ virtual unsigned char readI2CConfig_BytesToRead(tRioStatusCode *status) = 0;
+ virtual unsigned char readI2CConfig_BytesToWrite(tRioStatusCode *status) = 0;
+ virtual unsigned short readI2CConfig_DataToSendHigh(tRioStatusCode *status) = 0;
+ virtual bool readI2CConfig_BitwiseHandshake(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tDO_PWMConfig_IfaceConstants;
+
+ virtual void writeDO_PWMConfig(tDO_PWMConfig value, tRioStatusCode *status) = 0;
+ virtual void writeDO_PWMConfig_PeriodPower(unsigned char value, tRioStatusCode *status) = 0;
+ virtual void writeDO_PWMConfig_OutputSelect_0(unsigned char value, tRioStatusCode *status) = 0;
+ virtual void writeDO_PWMConfig_OutputSelect_1(unsigned char value, tRioStatusCode *status) = 0;
+ virtual void writeDO_PWMConfig_OutputSelect_2(unsigned char value, tRioStatusCode *status) = 0;
+ virtual void writeDO_PWMConfig_OutputSelect_3(unsigned char value, tRioStatusCode *status) = 0;
+ virtual tDO_PWMConfig readDO_PWMConfig(tRioStatusCode *status) = 0;
+ virtual unsigned char readDO_PWMConfig_PeriodPower(tRioStatusCode *status) = 0;
+ virtual unsigned char readDO_PWMConfig_OutputSelect_0(tRioStatusCode *status) = 0;
+ virtual unsigned char readDO_PWMConfig_OutputSelect_1(tRioStatusCode *status) = 0;
+ virtual unsigned char readDO_PWMConfig_OutputSelect_2(tRioStatusCode *status) = 0;
+ virtual unsigned char readDO_PWMConfig_OutputSelect_3(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tI2CStart_IfaceConstants;
+
+ virtual void strobeI2CStart(tRioStatusCode *status) = 0;
+
+
+
+ typedef enum
+ {
+ } tLoopTiming_IfaceConstants;
+
+ virtual unsigned short readLoopTiming(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tPWMConfig_IfaceConstants;
+
+ virtual void writePWMConfig(tPWMConfig value, tRioStatusCode *status) = 0;
+ virtual void writePWMConfig_Period(unsigned short value, tRioStatusCode *status) = 0;
+ virtual void writePWMConfig_MinHigh(unsigned short value, tRioStatusCode *status) = 0;
+ virtual tPWMConfig readPWMConfig(tRioStatusCode *status) = 0;
+ virtual unsigned short readPWMConfig_Period(tRioStatusCode *status) = 0;
+ virtual unsigned short readPWMConfig_MinHigh(tRioStatusCode *status) = 0;
+
+
+
+ typedef enum
+ {
+ kNumPWMValueRegisters = 10,
+ } tPWMValue_IfaceConstants;
+
+ virtual void writePWMValue(unsigned char reg_index, unsigned char value, tRioStatusCode *status) = 0;
+ virtual unsigned char readPWMValue(unsigned char reg_index, tRioStatusCode *status) = 0;
+
+
+
+private:
+ tDIO(const tDIO&);
+ void operator=(const tDIO&);
+};
+
+}
+}
+
+#endif // __nFRC_2012_1_6_4_DIO_h__
diff --git a/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRuntimeFPGANamespace/tDMA.h b/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRuntimeFPGANamespace/tDMA.h
new file mode 100644
index 0000000..006ec60
--- /dev/null
+++ b/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRuntimeFPGANamespace/tDMA.h
@@ -0,0 +1,188 @@
+// Copyright (c) National Instruments 2008. All Rights Reserved.
+// Do Not Edit... this file is generated!
+
+#ifndef __nFRC_2012_1_6_4_DMA_h__
+#define __nFRC_2012_1_6_4_DMA_h__
+
+#include "tSystemInterface.h"
+
+namespace nFPGA
+{
+namespace nFRC_2012_1_6_4
+{
+
+class tDMA
+{
+public:
+ tDMA(){}
+ virtual ~tDMA(){}
+
+ virtual tSystemInterface* getSystemInterface() = 0;
+ static tDMA* create(tRioStatusCode *status);
+
+ typedef enum
+ {
+ kNumSystems = 1,
+ } tIfaceConstants;
+
+ typedef
+ union{
+ struct{
+#ifdef __vxworks
+ unsigned Pause : 1;
+ unsigned Enable_AI0_Low : 1;
+ unsigned Enable_AI0_High : 1;
+ unsigned Enable_AIAveraged0_Low : 1;
+ unsigned Enable_AIAveraged0_High : 1;
+ unsigned Enable_AI1_Low : 1;
+ unsigned Enable_AI1_High : 1;
+ unsigned Enable_AIAveraged1_Low : 1;
+ unsigned Enable_AIAveraged1_High : 1;
+ unsigned Enable_Accumulator0 : 1;
+ unsigned Enable_Accumulator1 : 1;
+ unsigned Enable_DI : 1;
+ unsigned Enable_AnalogTriggers : 1;
+ unsigned Enable_Counters_Low : 1;
+ unsigned Enable_Counters_High : 1;
+ unsigned Enable_CounterTimers_Low : 1;
+ unsigned Enable_CounterTimers_High : 1;
+ unsigned Enable_Encoders : 1;
+ unsigned Enable_EncoderTimers : 1;
+ unsigned ExternalClock : 1;
+#else
+ unsigned ExternalClock : 1;
+ unsigned Enable_EncoderTimers : 1;
+ unsigned Enable_Encoders : 1;
+ unsigned Enable_CounterTimers_High : 1;
+ unsigned Enable_CounterTimers_Low : 1;
+ unsigned Enable_Counters_High : 1;
+ unsigned Enable_Counters_Low : 1;
+ unsigned Enable_AnalogTriggers : 1;
+ unsigned Enable_DI : 1;
+ unsigned Enable_Accumulator1 : 1;
+ unsigned Enable_Accumulator0 : 1;
+ unsigned Enable_AIAveraged1_High : 1;
+ unsigned Enable_AIAveraged1_Low : 1;
+ unsigned Enable_AI1_High : 1;
+ unsigned Enable_AI1_Low : 1;
+ unsigned Enable_AIAveraged0_High : 1;
+ unsigned Enable_AIAveraged0_Low : 1;
+ unsigned Enable_AI0_High : 1;
+ unsigned Enable_AI0_Low : 1;
+ unsigned Pause : 1;
+#endif
+ };
+ struct{
+ unsigned value : 20;
+ };
+ } tConfig;
+ typedef
+ union{
+ struct{
+#ifdef __vxworks
+ unsigned ExternalClockSource_Channel : 4;
+ unsigned ExternalClockSource_Module : 1;
+ unsigned ExternalClockSource_AnalogTrigger : 1;
+ unsigned RisingEdge : 1;
+ unsigned FallingEdge : 1;
+#else
+ unsigned FallingEdge : 1;
+ unsigned RisingEdge : 1;
+ unsigned ExternalClockSource_AnalogTrigger : 1;
+ unsigned ExternalClockSource_Module : 1;
+ unsigned ExternalClockSource_Channel : 4;
+#endif
+ };
+ struct{
+ unsigned value : 8;
+ };
+ } tExternalTriggers;
+
+
+
+ typedef enum
+ {
+ } tRate_IfaceConstants;
+
+ virtual void writeRate(unsigned int value, tRioStatusCode *status) = 0;
+ virtual unsigned int readRate(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tConfig_IfaceConstants;
+
+ virtual void writeConfig(tConfig value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_Pause(bool value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_Enable_AI0_Low(bool value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_Enable_AI0_High(bool value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_Enable_AIAveraged0_Low(bool value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_Enable_AIAveraged0_High(bool value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_Enable_AI1_Low(bool value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_Enable_AI1_High(bool value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_Enable_AIAveraged1_Low(bool value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_Enable_AIAveraged1_High(bool value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_Enable_Accumulator0(bool value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_Enable_Accumulator1(bool value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_Enable_DI(bool value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_Enable_AnalogTriggers(bool value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_Enable_Counters_Low(bool value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_Enable_Counters_High(bool value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_Enable_CounterTimers_Low(bool value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_Enable_CounterTimers_High(bool value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_Enable_Encoders(bool value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_Enable_EncoderTimers(bool value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_ExternalClock(bool value, tRioStatusCode *status) = 0;
+ virtual tConfig readConfig(tRioStatusCode *status) = 0;
+ virtual bool readConfig_Pause(tRioStatusCode *status) = 0;
+ virtual bool readConfig_Enable_AI0_Low(tRioStatusCode *status) = 0;
+ virtual bool readConfig_Enable_AI0_High(tRioStatusCode *status) = 0;
+ virtual bool readConfig_Enable_AIAveraged0_Low(tRioStatusCode *status) = 0;
+ virtual bool readConfig_Enable_AIAveraged0_High(tRioStatusCode *status) = 0;
+ virtual bool readConfig_Enable_AI1_Low(tRioStatusCode *status) = 0;
+ virtual bool readConfig_Enable_AI1_High(tRioStatusCode *status) = 0;
+ virtual bool readConfig_Enable_AIAveraged1_Low(tRioStatusCode *status) = 0;
+ virtual bool readConfig_Enable_AIAveraged1_High(tRioStatusCode *status) = 0;
+ virtual bool readConfig_Enable_Accumulator0(tRioStatusCode *status) = 0;
+ virtual bool readConfig_Enable_Accumulator1(tRioStatusCode *status) = 0;
+ virtual bool readConfig_Enable_DI(tRioStatusCode *status) = 0;
+ virtual bool readConfig_Enable_AnalogTriggers(tRioStatusCode *status) = 0;
+ virtual bool readConfig_Enable_Counters_Low(tRioStatusCode *status) = 0;
+ virtual bool readConfig_Enable_Counters_High(tRioStatusCode *status) = 0;
+ virtual bool readConfig_Enable_CounterTimers_Low(tRioStatusCode *status) = 0;
+ virtual bool readConfig_Enable_CounterTimers_High(tRioStatusCode *status) = 0;
+ virtual bool readConfig_Enable_Encoders(tRioStatusCode *status) = 0;
+ virtual bool readConfig_Enable_EncoderTimers(tRioStatusCode *status) = 0;
+ virtual bool readConfig_ExternalClock(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ kNumExternalTriggersElements = 4,
+ } tExternalTriggers_IfaceConstants;
+
+ virtual void writeExternalTriggers(unsigned char bitfield_index, tExternalTriggers value, tRioStatusCode *status) = 0;
+ virtual void writeExternalTriggers_ExternalClockSource_Channel(unsigned char bitfield_index, unsigned char value, tRioStatusCode *status) = 0;
+ virtual void writeExternalTriggers_ExternalClockSource_Module(unsigned char bitfield_index, unsigned char value, tRioStatusCode *status) = 0;
+ virtual void writeExternalTriggers_ExternalClockSource_AnalogTrigger(unsigned char bitfield_index, bool value, tRioStatusCode *status) = 0;
+ virtual void writeExternalTriggers_RisingEdge(unsigned char bitfield_index, bool value, tRioStatusCode *status) = 0;
+ virtual void writeExternalTriggers_FallingEdge(unsigned char bitfield_index, bool value, tRioStatusCode *status) = 0;
+ virtual tExternalTriggers readExternalTriggers(unsigned char bitfield_index, tRioStatusCode *status) = 0;
+ virtual unsigned char readExternalTriggers_ExternalClockSource_Channel(unsigned char bitfield_index, tRioStatusCode *status) = 0;
+ virtual unsigned char readExternalTriggers_ExternalClockSource_Module(unsigned char bitfield_index, tRioStatusCode *status) = 0;
+ virtual bool readExternalTriggers_ExternalClockSource_AnalogTrigger(unsigned char bitfield_index, tRioStatusCode *status) = 0;
+ virtual bool readExternalTriggers_RisingEdge(unsigned char bitfield_index, tRioStatusCode *status) = 0;
+ virtual bool readExternalTriggers_FallingEdge(unsigned char bitfield_index, tRioStatusCode *status) = 0;
+
+
+
+
+private:
+ tDMA(const tDMA&);
+ void operator=(const tDMA&);
+};
+
+}
+}
+
+#endif // __nFRC_2012_1_6_4_DMA_h__
diff --git a/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRuntimeFPGANamespace/tEncoder.h b/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRuntimeFPGANamespace/tEncoder.h
new file mode 100644
index 0000000..7255920
--- /dev/null
+++ b/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRuntimeFPGANamespace/tEncoder.h
@@ -0,0 +1,199 @@
+// Copyright (c) National Instruments 2008. All Rights Reserved.
+// Do Not Edit... this file is generated!
+
+#ifndef __nFRC_2012_1_6_4_Encoder_h__
+#define __nFRC_2012_1_6_4_Encoder_h__
+
+#include "tSystemInterface.h"
+
+namespace nFPGA
+{
+namespace nFRC_2012_1_6_4
+{
+
+class tEncoder
+{
+public:
+ tEncoder(){}
+ virtual ~tEncoder(){}
+
+ virtual tSystemInterface* getSystemInterface() = 0;
+ static tEncoder* create(unsigned char sys_index, tRioStatusCode *status);
+ virtual unsigned char getSystemIndex() = 0;
+
+
+ typedef enum
+ {
+ kNumSystems = 4,
+ } tIfaceConstants;
+
+ typedef
+ union{
+ struct{
+#ifdef __vxworks
+ unsigned Direction : 1;
+ signed Value : 31;
+#else
+ signed Value : 31;
+ unsigned Direction : 1;
+#endif
+ };
+ struct{
+ unsigned value : 32;
+ };
+ } tOutput;
+ typedef
+ union{
+ struct{
+#ifdef __vxworks
+ unsigned ASource_Channel : 4;
+ unsigned ASource_Module : 1;
+ unsigned ASource_AnalogTrigger : 1;
+ unsigned BSource_Channel : 4;
+ unsigned BSource_Module : 1;
+ unsigned BSource_AnalogTrigger : 1;
+ unsigned IndexSource_Channel : 4;
+ unsigned IndexSource_Module : 1;
+ unsigned IndexSource_AnalogTrigger : 1;
+ unsigned IndexActiveHigh : 1;
+ unsigned Reverse : 1;
+ unsigned Enable : 1;
+#else
+ unsigned Enable : 1;
+ unsigned Reverse : 1;
+ unsigned IndexActiveHigh : 1;
+ unsigned IndexSource_AnalogTrigger : 1;
+ unsigned IndexSource_Module : 1;
+ unsigned IndexSource_Channel : 4;
+ unsigned BSource_AnalogTrigger : 1;
+ unsigned BSource_Module : 1;
+ unsigned BSource_Channel : 4;
+ unsigned ASource_AnalogTrigger : 1;
+ unsigned ASource_Module : 1;
+ unsigned ASource_Channel : 4;
+#endif
+ };
+ struct{
+ unsigned value : 21;
+ };
+ } tConfig;
+ typedef
+ union{
+ struct{
+#ifdef __vxworks
+ unsigned Period : 23;
+ signed Count : 8;
+ unsigned Stalled : 1;
+#else
+ unsigned Stalled : 1;
+ signed Count : 8;
+ unsigned Period : 23;
+#endif
+ };
+ struct{
+ unsigned value : 32;
+ };
+ } tTimerOutput;
+ typedef
+ union{
+ struct{
+#ifdef __vxworks
+ unsigned StallPeriod : 24;
+ unsigned AverageSize : 7;
+ unsigned UpdateWhenEmpty : 1;
+#else
+ unsigned UpdateWhenEmpty : 1;
+ unsigned AverageSize : 7;
+ unsigned StallPeriod : 24;
+#endif
+ };
+ struct{
+ unsigned value : 32;
+ };
+ } tTimerConfig;
+
+
+ typedef enum
+ {
+ } tOutput_IfaceConstants;
+
+ virtual tOutput readOutput(tRioStatusCode *status) = 0;
+ virtual bool readOutput_Direction(tRioStatusCode *status) = 0;
+ virtual signed int readOutput_Value(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tConfig_IfaceConstants;
+
+ virtual void writeConfig(tConfig value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_ASource_Channel(unsigned char value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_ASource_Module(unsigned char value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_ASource_AnalogTrigger(bool value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_BSource_Channel(unsigned char value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_BSource_Module(unsigned char value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_BSource_AnalogTrigger(bool value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_IndexSource_Channel(unsigned char value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_IndexSource_Module(unsigned char value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_IndexSource_AnalogTrigger(bool value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_IndexActiveHigh(bool value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_Reverse(bool value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_Enable(bool value, tRioStatusCode *status) = 0;
+ virtual tConfig readConfig(tRioStatusCode *status) = 0;
+ virtual unsigned char readConfig_ASource_Channel(tRioStatusCode *status) = 0;
+ virtual unsigned char readConfig_ASource_Module(tRioStatusCode *status) = 0;
+ virtual bool readConfig_ASource_AnalogTrigger(tRioStatusCode *status) = 0;
+ virtual unsigned char readConfig_BSource_Channel(tRioStatusCode *status) = 0;
+ virtual unsigned char readConfig_BSource_Module(tRioStatusCode *status) = 0;
+ virtual bool readConfig_BSource_AnalogTrigger(tRioStatusCode *status) = 0;
+ virtual unsigned char readConfig_IndexSource_Channel(tRioStatusCode *status) = 0;
+ virtual unsigned char readConfig_IndexSource_Module(tRioStatusCode *status) = 0;
+ virtual bool readConfig_IndexSource_AnalogTrigger(tRioStatusCode *status) = 0;
+ virtual bool readConfig_IndexActiveHigh(tRioStatusCode *status) = 0;
+ virtual bool readConfig_Reverse(tRioStatusCode *status) = 0;
+ virtual bool readConfig_Enable(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tTimerOutput_IfaceConstants;
+
+ virtual tTimerOutput readTimerOutput(tRioStatusCode *status) = 0;
+ virtual unsigned int readTimerOutput_Period(tRioStatusCode *status) = 0;
+ virtual signed char readTimerOutput_Count(tRioStatusCode *status) = 0;
+ virtual bool readTimerOutput_Stalled(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tReset_IfaceConstants;
+
+ virtual void strobeReset(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tTimerConfig_IfaceConstants;
+
+ virtual void writeTimerConfig(tTimerConfig value, tRioStatusCode *status) = 0;
+ virtual void writeTimerConfig_StallPeriod(unsigned int value, tRioStatusCode *status) = 0;
+ virtual void writeTimerConfig_AverageSize(unsigned char value, tRioStatusCode *status) = 0;
+ virtual void writeTimerConfig_UpdateWhenEmpty(bool value, tRioStatusCode *status) = 0;
+ virtual tTimerConfig readTimerConfig(tRioStatusCode *status) = 0;
+ virtual unsigned int readTimerConfig_StallPeriod(tRioStatusCode *status) = 0;
+ virtual unsigned char readTimerConfig_AverageSize(tRioStatusCode *status) = 0;
+ virtual bool readTimerConfig_UpdateWhenEmpty(tRioStatusCode *status) = 0;
+
+
+
+
+
+private:
+ tEncoder(const tEncoder&);
+ void operator=(const tEncoder&);
+};
+
+}
+}
+
+#endif // __nFRC_2012_1_6_4_Encoder_h__
diff --git a/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRuntimeFPGANamespace/tGlobal.h b/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRuntimeFPGANamespace/tGlobal.h
new file mode 100644
index 0000000..0782f35
--- /dev/null
+++ b/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRuntimeFPGANamespace/tGlobal.h
@@ -0,0 +1,70 @@
+// Copyright (c) National Instruments 2008. All Rights Reserved.
+// Do Not Edit... this file is generated!
+
+#ifndef __nFRC_2012_1_6_4_Global_h__
+#define __nFRC_2012_1_6_4_Global_h__
+
+#include "tSystemInterface.h"
+
+namespace nFPGA
+{
+namespace nFRC_2012_1_6_4
+{
+
+class tGlobal
+{
+public:
+ tGlobal(){}
+ virtual ~tGlobal(){}
+
+ virtual tSystemInterface* getSystemInterface() = 0;
+ static tGlobal* create(tRioStatusCode *status);
+
+ typedef enum
+ {
+ kNumSystems = 1,
+ } tIfaceConstants;
+
+
+
+
+ typedef enum
+ {
+ } tVersion_IfaceConstants;
+
+ virtual unsigned short readVersion(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tLocalTime_IfaceConstants;
+
+ virtual unsigned int readLocalTime(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tFPGA_LED_IfaceConstants;
+
+ virtual void writeFPGA_LED(bool value, tRioStatusCode *status) = 0;
+ virtual bool readFPGA_LED(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tRevision_IfaceConstants;
+
+ virtual unsigned int readRevision(tRioStatusCode *status) = 0;
+
+
+
+
+private:
+ tGlobal(const tGlobal&);
+ void operator=(const tGlobal&);
+};
+
+}
+}
+
+#endif // __nFRC_2012_1_6_4_Global_h__
diff --git a/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRuntimeFPGANamespace/tInterrupt.h b/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRuntimeFPGANamespace/tInterrupt.h
new file mode 100644
index 0000000..40186e5
--- /dev/null
+++ b/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRuntimeFPGANamespace/tInterrupt.h
@@ -0,0 +1,93 @@
+// Copyright (c) National Instruments 2008. All Rights Reserved.
+// Do Not Edit... this file is generated!
+
+#ifndef __nFRC_2012_1_6_4_Interrupt_h__
+#define __nFRC_2012_1_6_4_Interrupt_h__
+
+#include "tSystemInterface.h"
+
+namespace nFPGA
+{
+namespace nFRC_2012_1_6_4
+{
+
+class tInterrupt
+{
+public:
+ tInterrupt(){}
+ virtual ~tInterrupt(){}
+
+ virtual tSystemInterface* getSystemInterface() = 0;
+ static tInterrupt* create(unsigned char sys_index, tRioStatusCode *status);
+ virtual unsigned char getSystemIndex() = 0;
+
+
+ typedef enum
+ {
+ kNumSystems = 8,
+ } tIfaceConstants;
+
+ typedef
+ union{
+ struct{
+#ifdef __vxworks
+ unsigned Source_Channel : 4;
+ unsigned Source_Module : 1;
+ unsigned Source_AnalogTrigger : 1;
+ unsigned RisingEdge : 1;
+ unsigned FallingEdge : 1;
+ unsigned WaitForAck : 1;
+#else
+ unsigned WaitForAck : 1;
+ unsigned FallingEdge : 1;
+ unsigned RisingEdge : 1;
+ unsigned Source_AnalogTrigger : 1;
+ unsigned Source_Module : 1;
+ unsigned Source_Channel : 4;
+#endif
+ };
+ struct{
+ unsigned value : 9;
+ };
+ } tConfig;
+
+
+ typedef enum
+ {
+ } tTimeStamp_IfaceConstants;
+
+ virtual unsigned int readTimeStamp(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tConfig_IfaceConstants;
+
+ virtual void writeConfig(tConfig value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_Source_Channel(unsigned char value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_Source_Module(unsigned char value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_Source_AnalogTrigger(bool value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_RisingEdge(bool value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_FallingEdge(bool value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_WaitForAck(bool value, tRioStatusCode *status) = 0;
+ virtual tConfig readConfig(tRioStatusCode *status) = 0;
+ virtual unsigned char readConfig_Source_Channel(tRioStatusCode *status) = 0;
+ virtual unsigned char readConfig_Source_Module(tRioStatusCode *status) = 0;
+ virtual bool readConfig_Source_AnalogTrigger(tRioStatusCode *status) = 0;
+ virtual bool readConfig_RisingEdge(tRioStatusCode *status) = 0;
+ virtual bool readConfig_FallingEdge(tRioStatusCode *status) = 0;
+ virtual bool readConfig_WaitForAck(tRioStatusCode *status) = 0;
+
+
+
+
+
+private:
+ tInterrupt(const tInterrupt&);
+ void operator=(const tInterrupt&);
+};
+
+}
+}
+
+#endif // __nFRC_2012_1_6_4_Interrupt_h__
diff --git a/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRuntimeFPGANamespace/tSPI.h b/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRuntimeFPGANamespace/tSPI.h
new file mode 100644
index 0000000..45c208c
--- /dev/null
+++ b/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRuntimeFPGANamespace/tSPI.h
@@ -0,0 +1,228 @@
+// Copyright (c) National Instruments 2008. All Rights Reserved.
+// Do Not Edit... this file is generated!
+
+#ifndef __nFRC_2012_1_6_4_SPI_h__
+#define __nFRC_2012_1_6_4_SPI_h__
+
+#include "tSystemInterface.h"
+
+namespace nFPGA
+{
+namespace nFRC_2012_1_6_4
+{
+
+class tSPI
+{
+public:
+ tSPI(){}
+ virtual ~tSPI(){}
+
+ virtual tSystemInterface* getSystemInterface() = 0;
+ static tSPI* create(tRioStatusCode *status);
+
+ typedef enum
+ {
+ kNumSystems = 1,
+ } tIfaceConstants;
+
+ typedef
+ union{
+ struct{
+#ifdef __vxworks
+ unsigned ReceivedDataOverflow : 1;
+ unsigned Idle : 1;
+#else
+ unsigned Idle : 1;
+ unsigned ReceivedDataOverflow : 1;
+#endif
+ };
+ struct{
+ unsigned value : 2;
+ };
+ } tStatus;
+ typedef
+ union{
+ struct{
+#ifdef __vxworks
+ unsigned BusBitWidth : 8;
+ unsigned ClockHalfPeriodDelay : 8;
+ unsigned MSBfirst : 1;
+ unsigned DataOnFalling : 1;
+ unsigned LatchFirst : 1;
+ unsigned LatchLast : 1;
+ unsigned FramePolarity : 1;
+ unsigned WriteOnly : 1;
+ unsigned ClockPolarity : 1;
+#else
+ unsigned ClockPolarity : 1;
+ unsigned WriteOnly : 1;
+ unsigned FramePolarity : 1;
+ unsigned LatchLast : 1;
+ unsigned LatchFirst : 1;
+ unsigned DataOnFalling : 1;
+ unsigned MSBfirst : 1;
+ unsigned ClockHalfPeriodDelay : 8;
+ unsigned BusBitWidth : 8;
+#endif
+ };
+ struct{
+ unsigned value : 23;
+ };
+ } tConfig;
+ typedef
+ union{
+ struct{
+#ifdef __vxworks
+ unsigned SCLK_Channel : 4;
+ unsigned SCLK_Module : 1;
+ unsigned MOSI_Channel : 4;
+ unsigned MOSI_Module : 1;
+ unsigned MISO_Channel : 4;
+ unsigned MISO_Module : 1;
+ unsigned SS_Channel : 4;
+ unsigned SS_Module : 1;
+#else
+ unsigned SS_Module : 1;
+ unsigned SS_Channel : 4;
+ unsigned MISO_Module : 1;
+ unsigned MISO_Channel : 4;
+ unsigned MOSI_Module : 1;
+ unsigned MOSI_Channel : 4;
+ unsigned SCLK_Module : 1;
+ unsigned SCLK_Channel : 4;
+#endif
+ };
+ struct{
+ unsigned value : 20;
+ };
+ } tChannels;
+
+
+
+ typedef enum
+ {
+ } tStatus_IfaceConstants;
+
+ virtual tStatus readStatus(tRioStatusCode *status) = 0;
+ virtual bool readStatus_ReceivedDataOverflow(tRioStatusCode *status) = 0;
+ virtual bool readStatus_Idle(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tReceivedData_IfaceConstants;
+
+ virtual unsigned int readReceivedData(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tDataToLoad_IfaceConstants;
+
+ virtual void writeDataToLoad(unsigned int value, tRioStatusCode *status) = 0;
+ virtual unsigned int readDataToLoad(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tConfig_IfaceConstants;
+
+ virtual void writeConfig(tConfig value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_BusBitWidth(unsigned char value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_ClockHalfPeriodDelay(unsigned char value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_MSBfirst(bool value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_DataOnFalling(bool value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_LatchFirst(bool value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_LatchLast(bool value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_FramePolarity(bool value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_WriteOnly(bool value, tRioStatusCode *status) = 0;
+ virtual void writeConfig_ClockPolarity(bool value, tRioStatusCode *status) = 0;
+ virtual tConfig readConfig(tRioStatusCode *status) = 0;
+ virtual unsigned char readConfig_BusBitWidth(tRioStatusCode *status) = 0;
+ virtual unsigned char readConfig_ClockHalfPeriodDelay(tRioStatusCode *status) = 0;
+ virtual bool readConfig_MSBfirst(tRioStatusCode *status) = 0;
+ virtual bool readConfig_DataOnFalling(tRioStatusCode *status) = 0;
+ virtual bool readConfig_LatchFirst(tRioStatusCode *status) = 0;
+ virtual bool readConfig_LatchLast(tRioStatusCode *status) = 0;
+ virtual bool readConfig_FramePolarity(tRioStatusCode *status) = 0;
+ virtual bool readConfig_WriteOnly(tRioStatusCode *status) = 0;
+ virtual bool readConfig_ClockPolarity(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tClearReceivedData_IfaceConstants;
+
+ virtual void strobeClearReceivedData(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tReceivedElements_IfaceConstants;
+
+ virtual unsigned short readReceivedElements(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tLoad_IfaceConstants;
+
+ virtual void strobeLoad(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tReset_IfaceConstants;
+
+ virtual void strobeReset(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tChannels_IfaceConstants;
+
+ virtual void writeChannels(tChannels value, tRioStatusCode *status) = 0;
+ virtual void writeChannels_SCLK_Channel(unsigned char value, tRioStatusCode *status) = 0;
+ virtual void writeChannels_SCLK_Module(unsigned char value, tRioStatusCode *status) = 0;
+ virtual void writeChannels_MOSI_Channel(unsigned char value, tRioStatusCode *status) = 0;
+ virtual void writeChannels_MOSI_Module(unsigned char value, tRioStatusCode *status) = 0;
+ virtual void writeChannels_MISO_Channel(unsigned char value, tRioStatusCode *status) = 0;
+ virtual void writeChannels_MISO_Module(unsigned char value, tRioStatusCode *status) = 0;
+ virtual void writeChannels_SS_Channel(unsigned char value, tRioStatusCode *status) = 0;
+ virtual void writeChannels_SS_Module(unsigned char value, tRioStatusCode *status) = 0;
+ virtual tChannels readChannels(tRioStatusCode *status) = 0;
+ virtual unsigned char readChannels_SCLK_Channel(tRioStatusCode *status) = 0;
+ virtual unsigned char readChannels_SCLK_Module(tRioStatusCode *status) = 0;
+ virtual unsigned char readChannels_MOSI_Channel(tRioStatusCode *status) = 0;
+ virtual unsigned char readChannels_MOSI_Module(tRioStatusCode *status) = 0;
+ virtual unsigned char readChannels_MISO_Channel(tRioStatusCode *status) = 0;
+ virtual unsigned char readChannels_MISO_Module(tRioStatusCode *status) = 0;
+ virtual unsigned char readChannels_SS_Channel(tRioStatusCode *status) = 0;
+ virtual unsigned char readChannels_SS_Module(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tAvailableToLoad_IfaceConstants;
+
+ virtual unsigned short readAvailableToLoad(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tReadReceivedData_IfaceConstants;
+
+ virtual void strobeReadReceivedData(tRioStatusCode *status) = 0;
+
+
+
+
+private:
+ tSPI(const tSPI&);
+ void operator=(const tSPI&);
+};
+
+}
+}
+
+#endif // __nFRC_2012_1_6_4_SPI_h__
diff --git a/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRuntimeFPGANamespace/tSolenoid.h b/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRuntimeFPGANamespace/tSolenoid.h
new file mode 100644
index 0000000..8627eea
--- /dev/null
+++ b/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRuntimeFPGANamespace/tSolenoid.h
@@ -0,0 +1,50 @@
+// Copyright (c) National Instruments 2008. All Rights Reserved.
+// Do Not Edit... this file is generated!
+
+#ifndef __nFRC_2012_1_6_4_Solenoid_h__
+#define __nFRC_2012_1_6_4_Solenoid_h__
+
+#include "tSystemInterface.h"
+
+namespace nFPGA
+{
+namespace nFRC_2012_1_6_4
+{
+
+class tSolenoid
+{
+public:
+ tSolenoid(){}
+ virtual ~tSolenoid(){}
+
+ virtual tSystemInterface* getSystemInterface() = 0;
+ static tSolenoid* create(tRioStatusCode *status);
+
+ typedef enum
+ {
+ kNumSystems = 1,
+ } tIfaceConstants;
+
+
+
+
+ typedef enum
+ {
+ kNumDO7_0Elements = 2,
+ } tDO7_0_IfaceConstants;
+
+ virtual void writeDO7_0(unsigned char bitfield_index, unsigned char value, tRioStatusCode *status) = 0;
+ virtual unsigned char readDO7_0(unsigned char bitfield_index, tRioStatusCode *status) = 0;
+
+
+
+
+private:
+ tSolenoid(const tSolenoid&);
+ void operator=(const tSolenoid&);
+};
+
+}
+}
+
+#endif // __nFRC_2012_1_6_4_Solenoid_h__
diff --git a/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRuntimeFPGANamespace/tSysWatchdog.h b/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRuntimeFPGANamespace/tSysWatchdog.h
new file mode 100644
index 0000000..2ef01ff
--- /dev/null
+++ b/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRuntimeFPGANamespace/tSysWatchdog.h
@@ -0,0 +1,71 @@
+// Copyright (c) National Instruments 2008. All Rights Reserved.
+// Do Not Edit... this file is generated!
+
+#ifndef __nFRC_2012_1_6_4_SysWatchdog_h__
+#define __nFRC_2012_1_6_4_SysWatchdog_h__
+
+#include "tSystemInterface.h"
+
+namespace nFPGA
+{
+namespace nFRC_2012_1_6_4
+{
+
+class tSysWatchdog
+{
+public:
+ tSysWatchdog(){}
+ virtual ~tSysWatchdog(){}
+
+ virtual tSystemInterface* getSystemInterface() = 0;
+ static tSysWatchdog* create(tRioStatusCode *status);
+
+ typedef enum
+ {
+ kNumSystems = 1,
+ } tIfaceConstants;
+
+
+
+
+ typedef enum
+ {
+ } tCommand_IfaceConstants;
+
+ virtual void writeCommand(unsigned short value, tRioStatusCode *status) = 0;
+ virtual unsigned short readCommand(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tChallenge_IfaceConstants;
+
+ virtual unsigned char readChallenge(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tActive_IfaceConstants;
+
+ virtual void writeActive(bool value, tRioStatusCode *status) = 0;
+ virtual bool readActive(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tTimer_IfaceConstants;
+
+ virtual unsigned int readTimer(tRioStatusCode *status) = 0;
+
+
+
+
+private:
+ tSysWatchdog(const tSysWatchdog&);
+ void operator=(const tSysWatchdog&);
+};
+
+}
+}
+
+#endif // __nFRC_2012_1_6_4_SysWatchdog_h__
diff --git a/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRuntimeFPGANamespace/tWatchdog.h b/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRuntimeFPGANamespace/tWatchdog.h
new file mode 100644
index 0000000..a589eda
--- /dev/null
+++ b/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/nRuntimeFPGANamespace/tWatchdog.h
@@ -0,0 +1,108 @@
+// Copyright (c) National Instruments 2008. All Rights Reserved.
+// Do Not Edit... this file is generated!
+
+#ifndef __nFRC_2012_1_6_4_Watchdog_h__
+#define __nFRC_2012_1_6_4_Watchdog_h__
+
+#include "tSystemInterface.h"
+
+namespace nFPGA
+{
+namespace nFRC_2012_1_6_4
+{
+
+class tWatchdog
+{
+public:
+ tWatchdog(){}
+ virtual ~tWatchdog(){}
+
+ virtual tSystemInterface* getSystemInterface() = 0;
+ static tWatchdog* create(tRioStatusCode *status);
+
+ typedef enum
+ {
+ kNumSystems = 1,
+ } tIfaceConstants;
+
+ typedef
+ union{
+ struct{
+#ifdef __vxworks
+ unsigned SystemActive : 1;
+ unsigned Alive : 1;
+ unsigned SysDisableCount : 15;
+ unsigned DisableCount : 15;
+#else
+ unsigned DisableCount : 15;
+ unsigned SysDisableCount : 15;
+ unsigned Alive : 1;
+ unsigned SystemActive : 1;
+#endif
+ };
+ struct{
+ unsigned value : 32;
+ };
+ } tStatus;
+
+
+
+ typedef enum
+ {
+ } tStatus_IfaceConstants;
+
+ virtual tStatus readStatus(tRioStatusCode *status) = 0;
+ virtual bool readStatus_SystemActive(tRioStatusCode *status) = 0;
+ virtual bool readStatus_Alive(tRioStatusCode *status) = 0;
+ virtual unsigned short readStatus_SysDisableCount(tRioStatusCode *status) = 0;
+ virtual unsigned short readStatus_DisableCount(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tKill_IfaceConstants;
+
+ virtual void strobeKill(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tFeed_IfaceConstants;
+
+ virtual void strobeFeed(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tTimer_IfaceConstants;
+
+ virtual unsigned int readTimer(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tExpiration_IfaceConstants;
+
+ virtual void writeExpiration(unsigned int value, tRioStatusCode *status) = 0;
+ virtual unsigned int readExpiration(tRioStatusCode *status) = 0;
+
+
+ typedef enum
+ {
+ } tImmortal_IfaceConstants;
+
+ virtual void writeImmortal(bool value, tRioStatusCode *status) = 0;
+ virtual bool readImmortal(tRioStatusCode *status) = 0;
+
+
+
+
+private:
+ tWatchdog(const tWatchdog&);
+ void operator=(const tWatchdog&);
+};
+
+}
+}
+
+#endif // __nFRC_2012_1_6_4_Watchdog_h__
diff --git a/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/printFpgaVersion.h b/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/printFpgaVersion.h
new file mode 100644
index 0000000..788f1df
--- /dev/null
+++ b/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/printFpgaVersion.h
@@ -0,0 +1,42 @@
+// Copyright (c) National Instruments 2008. All Rights Reserved.
+
+#ifndef __printFPGAVersion_h__
+#define __printFPGAVersion_h__
+
+namespace nFPGA
+{
+
+template<typename ttGlobal>
+inline void printFPGAVersion(ttGlobal &global)
+{
+ tRioStatusCode cleanStatus=0;
+ uint32_t hardwareGuid[4];
+ tSystemInterface &system = *global.getSystemInterface();
+ system.getHardwareFpgaSignature(hardwareGuid, &cleanStatus);
+ const uint32_t *softwareGuid = system.getExpectedFPGASignature();
+ printf("FPGA Hardware GUID: 0x");
+ for(int i=0; i<4; i++)
+ {
+ printf("%08X", hardwareGuid[i]);
+ }
+ printf("\n");
+ printf("FPGA Software GUID: 0x");
+ for(int i=0; i<4; i++)
+ {
+ printf("%08X", softwareGuid[i]);
+ }
+ printf("\n");
+ uint16_t fpgaHardwareVersion = global.readVersion(&cleanStatus);
+ uint16_t fpgaSoftwareVersion = system.getExpectedFPGAVersion();
+ printf("FPGA Hardware Version: %X\n", fpgaHardwareVersion);
+ printf("FPGA Software Version: %X\n", fpgaSoftwareVersion);
+ uint32_t fpgaHardwareRevision = global.readRevision(&cleanStatus);
+ uint32_t fpgaSoftwareRevision = system.getExpectedFPGARevision();
+ printf("FPGA Hardware Revision: %X.%X.%X\n", (fpgaHardwareRevision >> 20) & 0xFFF, (fpgaHardwareRevision >> 12) & 0xFF, fpgaHardwareRevision & 0xFFF);
+ printf("FPGA Software Revision: %X.%X.%X\n", (fpgaSoftwareRevision >> 20) & 0xFFF, (fpgaSoftwareRevision >> 12) & 0xFF, fpgaSoftwareRevision & 0xFFF);
+}
+
+}
+
+#endif // __printFPGAVersion_h__
+
diff --git a/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/tDMAChannelDescriptor.h b/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/tDMAChannelDescriptor.h
new file mode 100644
index 0000000..8fa5935
--- /dev/null
+++ b/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/tDMAChannelDescriptor.h
@@ -0,0 +1,17 @@
+// Describes the information needed to configure a DMA channel.
+// Copyright (c) National Instruments 2008. All Rights Reserved.
+
+#include <stdint.h>
+
+#ifndef __tDMAChannelDescriptor_h__
+#define __tDMAChannelDescriptor_h__
+
+struct tDMAChannelDescriptor
+{
+ uint32_t channel;
+ uint32_t baseAddress;
+ uint32_t depth;
+ bool targetToHost;
+};
+
+#endif // __tDMAChannelDescriptor_h__
diff --git a/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/tDMAManager.h b/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/tDMAManager.h
new file mode 100644
index 0000000..cb95203
--- /dev/null
+++ b/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/tDMAManager.h
@@ -0,0 +1,41 @@
+// Class for handling DMA transfers.
+// Copyright (c) National Instruments 2008. All Rights Reserved.
+
+#ifndef __tDMAManager_h__
+#define __tDMAManager_h__
+
+#include "tSystem.h"
+#include <stdint.h>
+
+namespace nFPGA
+{
+class tDMAManager : public tSystem
+{
+public:
+ tDMAManager(uint32_t dmaChannel, uint32_t hostBufferSize, tRioStatusCode *status);
+ ~tDMAManager();
+ void start(tRioStatusCode *status);
+ void stop(tRioStatusCode *status);
+ bool isStarted() {return _started;}
+ void read(
+ uint32_t* buf,
+ size_t num,
+ uint32_t timeout,
+ size_t* remaining,
+ tRioStatusCode *status);
+ void write(
+ uint32_t* buf,
+ size_t num,
+ uint32_t timeout,
+ size_t* remaining,
+ tRioStatusCode *status);
+private:
+ bool _started;
+ uint32_t _dmaChannel;
+ uint32_t _hostBufferSize;
+
+};
+
+}
+
+#endif // __tDMAManager_h__
diff --git a/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/tInterruptManager.h b/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/tInterruptManager.h
new file mode 100644
index 0000000..cb6783d
--- /dev/null
+++ b/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/tInterruptManager.h
@@ -0,0 +1,61 @@
+// Class for handling interrupts.
+// Copyright (c) National Instruments 2008. All Rights Reserved.
+
+#ifndef __tInterruptManager_h__
+#define __tInterruptManager_h__
+
+#include "tSystem.h"
+
+namespace ni
+{
+ namespace dsc
+ {
+ namespace osdep
+ {
+ class CriticalSection;
+ }
+ }
+}
+
+namespace nFPGA
+{
+
+typedef void (*tInterruptHandler)(uint32_t interruptAssertedMask, void *param);
+
+class tInterruptManager : public tSystem
+{
+public:
+ tInterruptManager(uint32_t interruptMask, bool watcher, tRioStatusCode *status);
+ ~tInterruptManager();
+ void registerHandler(tInterruptHandler handler, void *param, tRioStatusCode *status);
+ uint32_t watch(int32_t timeoutInMs, bool ignorePrevious, tRioStatusCode *status);
+ void enable(tRioStatusCode *status);
+ void disable(tRioStatusCode *status);
+ bool isEnabled(tRioStatusCode *status);
+private:
+ class tInterruptThread;
+ friend class tInterruptThread;
+ void handler();
+ static int handlerWrapper(tInterruptManager *pInterrupt);
+
+ void acknowledge(tRioStatusCode *status);
+ void reserve(tRioStatusCode *status);
+ void unreserve(tRioStatusCode *status);
+ tInterruptHandler _handler;
+ uint32_t _interruptMask;
+ tInterruptThread *_thread;
+ NiFpga_IrqContext _rioContext;
+ bool _watcher;
+ bool _enabled;
+ void *_userParam;
+
+ // maintain the interrupts that are already dealt with.
+ static uint32_t _globalInterruptMask;
+ static ni::dsc::osdep::CriticalSection *_globalInterruptMaskSemaphore;
+};
+
+}
+
+
+#endif // __tInterruptManager_h__
+
diff --git a/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/tSystem.h b/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/tSystem.h
new file mode 100644
index 0000000..b059e51
--- /dev/null
+++ b/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/tSystem.h
@@ -0,0 +1,48 @@
+// Base class for generated chip objects
+// Copyright (c) National Instruments 2008. All Rights Reserved.
+
+#ifndef __tSystem_h__
+#define __tSystem_h__
+
+#include "fpgainterfacecapi/NiFpga.h"
+typedef NiFpga_Status tRioStatusCode;
+
+#define FRC_FPGA_PRELOAD_BITFILE
+
+typedef uint32_t NiFpga_Session;
+
+namespace nFPGA
+{
+
+class tSystem
+{
+public:
+ tSystem(tRioStatusCode *status);
+ ~tSystem();
+ void getFpgaGuid(uint32_t *guid_ptr, tRioStatusCode *status);
+ void reset(tRioStatusCode *status);
+
+protected:
+ static NiFpga_Session _DeviceHandle;
+
+#ifdef FRC_FPGA_PRELOAD_BITFILE
+ void NiFpga_SharedOpen_common(const char* bitfile);
+ NiFpga_Status NiFpga_SharedOpen(const char* bitfile,
+ const char* signature,
+ const char* resource,
+ uint32_t attribute,
+ NiFpga_Session* session);
+ NiFpga_Status NiFpgaLv_SharedOpen(const char* const bitfile,
+ const char* const apiSignature,
+ const char* const resource,
+ const uint32_t attribute,
+ NiFpga_Session* const session);
+private:
+ static char *_FileName;
+ static char *_Bitfile;
+#endif
+};
+
+}
+
+#endif // __tSystem_h__
diff --git a/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/tSystemInterface.h b/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/tSystemInterface.h
new file mode 100644
index 0000000..8594187
--- /dev/null
+++ b/aos/externals/allwpilib/hal/lib/Athena/FRC_FPGA_ChipObject/tSystemInterface.h
@@ -0,0 +1,27 @@
+// Copyright (c) National Instruments 2008. All Rights Reserved.
+
+#ifndef __tSystemInterface_h__
+#define __tSystemInterface_h__
+
+namespace nFPGA
+{
+
+class tSystemInterface
+{
+public:
+ tSystemInterface(){}
+ virtual ~tSystemInterface(){}
+
+ virtual const uint16_t getExpectedFPGAVersion()=0;
+ virtual const uint32_t getExpectedFPGARevision()=0;
+ virtual const uint32_t * const getExpectedFPGASignature()=0;
+ virtual void getHardwareFpgaSignature(uint32_t *guid_ptr, tRioStatusCode *status)=0;
+ virtual uint32_t getLVHandle(tRioStatusCode *status)=0;
+ virtual uint32_t getHandle()=0;
+ virtual void reset(tRioStatusCode *status)=0;
+};
+
+}
+
+#endif // __tSystemInterface_h__
+
diff --git a/aos/externals/allwpilib/hal/lib/Athena/HAL.cpp b/aos/externals/allwpilib/hal/lib/Athena/HAL.cpp
new file mode 100644
index 0000000..7a9f0d5
--- /dev/null
+++ b/aos/externals/allwpilib/hal/lib/Athena/HAL.cpp
@@ -0,0 +1,373 @@
+#include "HAL/HAL.hpp"
+
+#include "Port.h"
+#include "HAL/Errors.hpp"
+#include "ctre/ctre.h"
+#include "visa/visa.h"
+#include "ChipObject.h"
+#include "NetworkCommunication/FRCComm.h"
+#include "NetworkCommunication/UsageReporting.h"
+#include "NetworkCommunication/LoadOut.h"
+#include "NetworkCommunication/CANSessionMux.h"
+#include <fstream>
+#include <iostream>
+#include <unistd.h>
+#include <sys/prctl.h>
+#include <signal.h> // linux for kill
+const uint32_t solenoid_kNumDO7_0Elements = 8;
+const uint32_t dio_kNumSystems = tDIO::kNumSystems;
+const uint32_t interrupt_kNumSystems = tInterrupt::kNumSystems;
+const uint32_t kSystemClockTicksPerMicrosecond = 40;
+
+static tGlobal *global;
+static tSysWatchdog *watchdog;
+
+void* getPort(uint8_t pin)
+{
+ Port* port = new Port();
+ port->pin = pin;
+ port->module = 1;
+ return port;
+}
+
+/**
+ * @deprecated Uses module numbers
+ */
+void* getPortWithModule(uint8_t module, uint8_t pin)
+{
+ Port* port = new Port();
+ port->pin = pin;
+ port->module = module;
+ return port;
+}
+
+const char* getHALErrorMessage(int32_t code)
+{
+ switch(code) {
+ case 0:
+ return "";
+ case CTR_RxTimeout:
+ return CTR_RxTimeout_MESSAGE;
+ case CTR_TxTimeout:
+ return CTR_TxTimeout_MESSAGE;
+ case CTR_InvalidParamValue:
+ return CTR_InvalidParamValue_MESSAGE;
+ case CTR_UnexpectedArbId:
+ return CTR_UnexpectedArbId_MESSAGE;
+ case CTR_TxFailed:
+ return CTR_TxFailed_MESSAGE;
+ case CTR_SigNotUpdated:
+ return CTR_SigNotUpdated_MESSAGE;
+ case NiFpga_Status_FifoTimeout:
+ return NiFpga_Status_FifoTimeout_MESSAGE;
+ case NiFpga_Status_TransferAborted:
+ return NiFpga_Status_TransferAborted_MESSAGE;
+ case NiFpga_Status_MemoryFull:
+ return NiFpga_Status_MemoryFull_MESSAGE;
+ case NiFpga_Status_SoftwareFault:
+ return NiFpga_Status_SoftwareFault_MESSAGE;
+ case NiFpga_Status_InvalidParameter:
+ return NiFpga_Status_InvalidParameter_MESSAGE;
+ case NiFpga_Status_ResourceNotFound:
+ return NiFpga_Status_ResourceNotFound_MESSAGE;
+ case NiFpga_Status_ResourceNotInitialized:
+ return NiFpga_Status_ResourceNotInitialized_MESSAGE;
+ case NiFpga_Status_HardwareFault:
+ return NiFpga_Status_HardwareFault_MESSAGE;
+ case NiFpga_Status_IrqTimeout:
+ return NiFpga_Status_IrqTimeout_MESSAGE;
+ case NiFpga_Status_VersionMismatch:
+ return NiFpga_Status_VersionMismatch_MESSAGE;
+ case SAMPLE_RATE_TOO_HIGH:
+ return SAMPLE_RATE_TOO_HIGH_MESSAGE;
+ case VOLTAGE_OUT_OF_RANGE:
+ return VOLTAGE_OUT_OF_RANGE_MESSAGE;
+ case LOOP_TIMING_ERROR:
+ return LOOP_TIMING_ERROR_MESSAGE;
+ case SPI_WRITE_NO_MOSI:
+ return SPI_WRITE_NO_MOSI_MESSAGE;
+ case SPI_READ_NO_MISO:
+ return SPI_READ_NO_MISO_MESSAGE;
+ case SPI_READ_NO_DATA:
+ return SPI_READ_NO_DATA_MESSAGE;
+ case INCOMPATIBLE_STATE:
+ return INCOMPATIBLE_STATE_MESSAGE;
+ case NO_AVAILABLE_RESOURCES:
+ return NO_AVAILABLE_RESOURCES_MESSAGE;
+ case NULL_PARAMETER:
+ return NULL_PARAMETER_MESSAGE;
+ case ANALOG_TRIGGER_LIMIT_ORDER_ERROR:
+ return ANALOG_TRIGGER_LIMIT_ORDER_ERROR_MESSAGE;
+ case ANALOG_TRIGGER_PULSE_OUTPUT_ERROR:
+ return ANALOG_TRIGGER_PULSE_OUTPUT_ERROR_MESSAGE;
+ case PARAMETER_OUT_OF_RANGE:
+ return PARAMETER_OUT_OF_RANGE_MESSAGE;
+ case ERR_CANSessionMux_InvalidBuffer:
+ return ERR_CANSessionMux_InvalidBuffer_MESSAGE;
+ case ERR_CANSessionMux_MessageNotFound:
+ return ERR_CANSessionMux_MessageNotFound_MESSAGE;
+ case WARN_CANSessionMux_NoToken:
+ return WARN_CANSessionMux_NoToken_MESSAGE;
+ case ERR_CANSessionMux_NotAllowed:
+ return ERR_CANSessionMux_NotAllowed_MESSAGE;
+ case ERR_CANSessionMux_NotInitialized:
+ return ERR_CANSessionMux_NotInitialized_MESSAGE;
+ case VI_ERROR_SYSTEM_ERROR:
+ return VI_ERROR_SYSTEM_ERROR_MESSAGE;
+ case VI_ERROR_INV_OBJECT:
+ return VI_ERROR_INV_OBJECT_MESSAGE;
+ case VI_ERROR_RSRC_LOCKED:
+ return VI_ERROR_RSRC_LOCKED_MESSAGE;
+ case VI_ERROR_RSRC_NFOUND:
+ return VI_ERROR_RSRC_NFOUND_MESSAGE;
+ case VI_ERROR_INV_RSRC_NAME:
+ return VI_ERROR_INV_RSRC_NAME_MESSAGE;
+ case VI_ERROR_QUEUE_OVERFLOW:
+ return VI_ERROR_QUEUE_OVERFLOW_MESSAGE;
+ case VI_ERROR_IO:
+ return VI_ERROR_IO_MESSAGE;
+ case VI_ERROR_ASRL_PARITY:
+ return VI_ERROR_ASRL_PARITY_MESSAGE;
+ case VI_ERROR_ASRL_FRAMING:
+ return VI_ERROR_ASRL_FRAMING_MESSAGE;
+ case VI_ERROR_ASRL_OVERRUN:
+ return VI_ERROR_ASRL_OVERRUN_MESSAGE;
+ case VI_ERROR_RSRC_BUSY:
+ return VI_ERROR_RSRC_BUSY_MESSAGE;
+ case VI_ERROR_INV_PARAMETER:
+ return VI_ERROR_INV_PARAMETER_MESSAGE;
+ default:
+ return "Unknown error status";
+ }
+}
+
+/**
+ * Return the FPGA Version number.
+ * For now, expect this to be competition year.
+ * @return FPGA Version number.
+ */
+uint16_t getFPGAVersion(int32_t *status)
+{
+ return global->readVersion(status);
+}
+
+/**
+ * Return the FPGA Revision number.
+ * The format of the revision is 3 numbers.
+ * The 12 most significant bits are the Major Revision.
+ * the next 8 bits are the Minor Revision.
+ * The 12 least significant bits are the Build Number.
+ * @return FPGA Revision number.
+ */
+uint32_t getFPGARevision(int32_t *status)
+{
+ return global->readRevision(status);
+}
+
+/**
+ * Read the microsecond-resolution timer on the FPGA.
+ *
+ * @return The current time in microseconds according to the FPGA (since FPGA reset).
+ */
+uint32_t getFPGATime(int32_t *status)
+{
+ return global->readLocalTime(status);
+}
+
+/**
+ * Get the state of the "USER" button on the RoboRIO
+ * @return true if the button is currently pressed down
+ */
+bool getFPGAButton(int32_t *status)
+{
+ return global->readUserButton(status);
+}
+
+int HALSetErrorData(const char *errors, int errorsLength, int wait_ms)
+{
+ return setErrorData(errors, errorsLength, wait_ms);
+}
+
+int HALGetControlWord(HALControlWord *data)
+{
+ return FRC_NetworkCommunication_getControlWord((ControlWord_t*) data);
+}
+
+int HALGetAllianceStation(enum HALAllianceStationID *allianceStation)
+{
+ return FRC_NetworkCommunication_getAllianceStation((AllianceStationID_t*) allianceStation);
+}
+
+int HALGetJoystickAxes(uint8_t joystickNum, HALJoystickAxes *axes)
+{
+ return FRC_NetworkCommunication_getJoystickAxes(joystickNum, (JoystickAxes_t*) axes, kMaxJoystickAxes);
+}
+
+int HALGetJoystickPOVs(uint8_t joystickNum, HALJoystickPOVs *povs)
+{
+ return FRC_NetworkCommunication_getJoystickPOVs(joystickNum, (JoystickPOV_t*) povs, kMaxJoystickPOVs);
+}
+
+int HALGetJoystickButtons(uint8_t joystickNum, HALJoystickButtons *buttons)
+{
+ return FRC_NetworkCommunication_getJoystickButtons(joystickNum, &buttons->buttons, &buttons->count);
+}
+
+int HALGetJoystickDescriptor(uint8_t joystickNum, HALJoystickDescriptor *desc)
+{
+ return FRC_NetworkCommunication_getJoystickDesc(joystickNum, &desc->isXbox, &desc->type, (char *)(&desc->name),
+ &desc->axisCount, &desc->axisTypes, &desc->buttonCount, &desc->povCount);
+}
+
+int HALSetJoystickOutputs(uint8_t joystickNum, uint32_t outputs, uint16_t leftRumble, uint16_t rightRumble)
+{
+ return FRC_NetworkCommunication_setJoystickOutputs(joystickNum, outputs, leftRumble, rightRumble);
+}
+
+int HALGetMatchTime(float *matchTime)
+{
+ return FRC_NetworkCommunication_getMatchTime(matchTime);
+}
+
+void HALSetNewDataSem(MULTIWAIT_ID sem)
+{
+ setNewDataSem(sem);
+}
+
+bool HALGetSystemActive(int32_t *status)
+{
+ return watchdog->readStatus_SystemActive(status);
+}
+
+bool HALGetBrownedOut(int32_t *status)
+{
+ return !(watchdog->readStatus_PowerAlive(status));
+}
+
+/**
+ * Call this to start up HAL. This is required for robot programs.
+ */
+int HALInitialize(int mode)
+{
+ setlinebuf(stdin);
+ setlinebuf(stdout);
+
+ prctl(PR_SET_PDEATHSIG, SIGTERM);
+
+ FRC_NetworkCommunication_Reserve(nullptr);
+ // image 4; Fixes errors caused by multiple processes. Talk to NI about this
+ nFPGA::nRoboRIO_FPGANamespace::g_currentTargetClass =
+ nLoadOut::kTargetClass_RoboRIO;
+
+ int32_t status;
+ global = tGlobal::create(&status);
+ watchdog = tSysWatchdog::create(&status);
+
+ // Kill any previous robot programs
+ std::fstream fs;
+ // By making this both in/out, it won't give us an error if it doesnt exist
+ fs.open("/var/lock/frc.pid", std::fstream::in | std::fstream::out);
+ if (fs.bad())
+ return 0;
+
+ pid_t pid = 0;
+ if (!fs.eof() && !fs.fail())
+ {
+ fs >> pid;
+ //see if the pid is around, but we don't want to mess with init id=1, or ourselves
+ if (pid >= 2 && kill(pid, 0) == 0 && pid != getpid())
+ {
+ std::cout << "Killing previously running FRC program..."
+ << std::endl;
+ kill(pid, SIGTERM); // try to kill it
+ delayMillis(100);
+ if (kill(pid, 0) == 0)
+ {
+ // still not successfull
+ if (mode == 0)
+ {
+ std::cout << "FRC pid " << pid
+ << " did not die within 110ms. Aborting"
+ << std::endl;
+ return 0; // just fail
+ }
+ else if (mode == 1) // kill -9 it
+ kill(pid, SIGKILL);
+ else
+ {
+ std::cout << "WARNING: FRC pid " << pid
+ << " did not die within 110ms." << std::endl;
+ }
+ }
+
+ }
+ }
+ fs.close();
+ // we will re-open it write only to truncate the file
+ fs.open("/var/lock/frc.pid", std::fstream::out | std::fstream::trunc);
+ fs.seekp(0);
+ pid = getpid();
+ fs << pid << std::endl;
+ fs.close();
+ return 1;
+}
+
+void HALNetworkCommunicationObserveUserProgramStarting(void)
+{
+ FRC_NetworkCommunication_observeUserProgramStarting();
+}
+
+void HALNetworkCommunicationObserveUserProgramDisabled(void)
+{
+ FRC_NetworkCommunication_observeUserProgramDisabled();
+}
+
+void HALNetworkCommunicationObserveUserProgramAutonomous(void)
+{
+ FRC_NetworkCommunication_observeUserProgramAutonomous();
+}
+
+void HALNetworkCommunicationObserveUserProgramTeleop(void)
+{
+ FRC_NetworkCommunication_observeUserProgramTeleop();
+}
+
+void HALNetworkCommunicationObserveUserProgramTest(void)
+{
+ FRC_NetworkCommunication_observeUserProgramTest();
+}
+
+uint32_t HALReport(uint8_t resource, uint8_t instanceNumber, uint8_t context,
+ const char *feature)
+{
+ if(feature == NULL)
+ {
+ feature = "";
+ }
+
+ return FRC_NetworkCommunication_nUsageReporting_report(resource, instanceNumber, context, feature);
+}
+
+// TODO: HACKS
+void NumericArrayResize()
+{
+}
+void RTSetCleanupProc()
+{
+}
+void EDVR_CreateReference()
+{
+}
+void Occur()
+{
+}
+
+void imaqGetErrorText()
+{
+}
+void imaqGetLastError()
+{
+}
+void niTimestamp64()
+{
+}
diff --git a/aos/externals/allwpilib/hal/lib/Athena/Interrupts.cpp b/aos/externals/allwpilib/hal/lib/Athena/Interrupts.cpp
new file mode 100644
index 0000000..49bd2c9
--- /dev/null
+++ b/aos/externals/allwpilib/hal/lib/Athena/Interrupts.cpp
@@ -0,0 +1,120 @@
+#include "HAL/Interrupts.hpp"
+#include "ChipObject.h"
+
+struct Interrupt // FIXME: why is this internal?
+{
+ tInterrupt *anInterrupt;
+ tInterruptManager *manager;
+};
+
+void* initializeInterrupts(uint32_t interruptIndex, bool watcher, int32_t *status)
+{
+ Interrupt* anInterrupt = new Interrupt();
+ // Expects the calling leaf class to allocate an interrupt index.
+ anInterrupt->anInterrupt = tInterrupt::create(interruptIndex, status);
+ anInterrupt->anInterrupt->writeConfig_WaitForAck(false, status);
+ anInterrupt->manager = new tInterruptManager(
+ (1 << interruptIndex) | (1 << (interruptIndex + 8)), watcher, status);
+ return anInterrupt;
+}
+
+void cleanInterrupts(void* interrupt_pointer, int32_t *status)
+{
+ Interrupt* anInterrupt = (Interrupt*)interrupt_pointer;
+ delete anInterrupt->anInterrupt;
+ delete anInterrupt->manager;
+ anInterrupt->anInterrupt = NULL;
+ anInterrupt->manager = NULL;
+}
+
+/**
+ * In synchronous mode, wait for the defined interrupt to occur.
+ * @param timeout Timeout in seconds
+ * @param ignorePrevious If true, ignore interrupts that happened before
+ * waitForInterrupt was called.
+ * @return The mask of interrupts that fired.
+ */
+uint32_t waitForInterrupt(void* interrupt_pointer, double timeout, bool ignorePrevious, int32_t *status)
+{
+ uint32_t result;
+ Interrupt* anInterrupt = (Interrupt*)interrupt_pointer;
+
+ result = anInterrupt->manager->watch((int32_t)(timeout * 1e3), ignorePrevious, status);
+
+ // Don't report a timeout as an error - the return code is enough to tell
+ // that a timeout happened.
+ if(*status == -NiFpga_Status_IrqTimeout) {
+ *status = NiFpga_Status_Success;
+ }
+
+ return result;
+}
+
+/**
+ * Enable interrupts to occur on this input.
+ * Interrupts are disabled when the RequestInterrupt call is made. This gives time to do the
+ * setup of the other options before starting to field interrupts.
+ */
+void enableInterrupts(void* interrupt_pointer, int32_t *status)
+{
+ Interrupt* anInterrupt = (Interrupt*)interrupt_pointer;
+ anInterrupt->manager->enable(status);
+}
+
+/**
+ * Disable Interrupts without without deallocating structures.
+ */
+void disableInterrupts(void* interrupt_pointer, int32_t *status)
+{
+ Interrupt* anInterrupt = (Interrupt*)interrupt_pointer;
+ anInterrupt->manager->disable(status);
+}
+
+/**
+ * Return the timestamp for the rising interrupt that occurred most recently.
+ * This is in the same time domain as GetClock().
+ * @return Timestamp in seconds since boot.
+ */
+double readRisingTimestamp(void* interrupt_pointer, int32_t *status)
+{
+ Interrupt* anInterrupt = (Interrupt*)interrupt_pointer;
+ uint32_t timestamp = anInterrupt->anInterrupt->readRisingTimeStamp(status);
+ return timestamp * 1e-6;
+}
+
+/**
+* Return the timestamp for the falling interrupt that occurred most recently.
+* This is in the same time domain as GetClock().
+* @return Timestamp in seconds since boot.
+*/
+double readFallingTimestamp(void* interrupt_pointer, int32_t *status)
+{
+ Interrupt* anInterrupt = (Interrupt*)interrupt_pointer;
+ uint32_t timestamp = anInterrupt->anInterrupt->readFallingTimeStamp(status);
+ return timestamp * 1e-6;
+}
+
+void requestInterrupts(void* interrupt_pointer, uint8_t routing_module, uint32_t routing_pin,
+ bool routing_analog_trigger, int32_t *status)
+{
+ Interrupt* anInterrupt = (Interrupt*)interrupt_pointer;
+ anInterrupt->anInterrupt->writeConfig_WaitForAck(false, status);
+ anInterrupt->anInterrupt->writeConfig_Source_AnalogTrigger(routing_analog_trigger, status);
+ anInterrupt->anInterrupt->writeConfig_Source_Channel(routing_pin, status);
+ anInterrupt->anInterrupt->writeConfig_Source_Module(routing_module, status);
+}
+
+void attachInterruptHandler(void* interrupt_pointer, InterruptHandlerFunction handler, void* param,
+ int32_t *status)
+{
+ Interrupt* anInterrupt = (Interrupt*)interrupt_pointer;
+ anInterrupt->manager->registerHandler(handler, param, status);
+}
+
+void setInterruptUpSourceEdge(void* interrupt_pointer, bool risingEdge, bool fallingEdge,
+ int32_t *status)
+{
+ Interrupt* anInterrupt = (Interrupt*)interrupt_pointer;
+ anInterrupt->anInterrupt->writeConfig_RisingEdge(risingEdge, status);
+ anInterrupt->anInterrupt->writeConfig_FallingEdge(fallingEdge, status);
+}
diff --git a/aos/externals/allwpilib/hal/lib/Athena/NetworkCommunication/AICalibration.h b/aos/externals/allwpilib/hal/lib/Athena/NetworkCommunication/AICalibration.h
new file mode 100644
index 0000000..b2f366c
--- /dev/null
+++ b/aos/externals/allwpilib/hal/lib/Athena/NetworkCommunication/AICalibration.h
@@ -0,0 +1,19 @@
+
+#ifndef __AICalibration_h__
+#define __AICalibration_h__
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+ uint32_t FRC_NetworkCommunication_nAICalibration_getLSBWeight(const uint32_t aiSystemIndex, const uint32_t channel, int32_t *status);
+ int32_t FRC_NetworkCommunication_nAICalibration_getOffset(const uint32_t aiSystemIndex, const uint32_t channel, int32_t *status);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // __AICalibration_h__
diff --git a/aos/externals/allwpilib/hal/lib/Athena/NetworkCommunication/CANInterfacePlugin.h b/aos/externals/allwpilib/hal/lib/Athena/NetworkCommunication/CANInterfacePlugin.h
new file mode 100644
index 0000000..77d992c
--- /dev/null
+++ b/aos/externals/allwpilib/hal/lib/Athena/NetworkCommunication/CANInterfacePlugin.h
@@ -0,0 +1,82 @@
+// CANInterfacePlugin.h
+//
+// Defines the API for building a CAN Interface Plugin to support
+// PWM-cable-free CAN motor control on FRC robots. This allows you
+// to connect any CAN interface to the secure Jaguar CAN driver.
+//
+
+#ifndef __CANInterfacePlugin_h__
+#define __CANInterfacePlugin_h__
+
+#include <stdint.h>
+
+#define CAN_IS_FRAME_REMOTE 0x80000000
+#define CAN_IS_FRAME_11BIT 0x40000000
+#define CAN_29BIT_MESSAGE_ID_MASK 0x1FFFFFFF
+#define CAN_11BIT_MESSAGE_ID_MASK 0x000007FF
+
+class CANInterfacePlugin
+{
+public:
+ CANInterfacePlugin() {}
+ virtual ~CANInterfacePlugin() {}
+
+ /**
+ * This entry-point of the CANInterfacePlugin is passed a message that the driver needs to send to
+ * a device on the CAN bus.
+ *
+ * This function may be called from multiple contexts and must therefore be reentrant.
+ *
+ * @param messageID The 29-bit CAN message ID in the lsbs. The msb can indicate a remote frame.
+ * @param data A pointer to a buffer containing between 0 and 8 bytes to send with the message. May be NULL if dataSize is 0.
+ * @param dataSize The number of bytes to send with the message.
+ * @return Return any error code. On success return 0.
+ */
+ virtual int32_t sendMessage(uint32_t messageID, const uint8_t *data, uint8_t dataSize) = 0;
+
+ /**
+ * This entry-point of the CANInterfacePlugin is passed buffers which should be populated with
+ * any received messages from devices on the CAN bus.
+ *
+ * This function is always called by a single task in the Jaguar driver, so it need not be reentrant.
+ *
+ * This function is expected to block for some period of time waiting for a message from the CAN bus.
+ * It may timeout periodically (returning non-zero to indicate no message was populated) to allow for
+ * shutdown and unloading of the plugin.
+ *
+ * @param messageID A reference to be populated with a received 29-bit CAN message ID in the lsbs.
+ * @param data A pointer to a buffer of 8 bytes to be populated with data received with the message.
+ * @param dataSize A reference to be populated with the size of the data received (0 - 8 bytes).
+ * @return This should return 0 if a message was populated, non-0 if no message was not populated.
+ */
+ virtual int32_t receiveMessage(uint32_t &messageID, uint8_t *data, uint8_t &dataSize) = 0;
+
+#if defined(__linux)
+ /**
+ * This entry-point of the CANInterfacePlugin returns status of the CAN bus.
+ *
+ * This function may be called from multiple contexts and must therefore be reentrant.
+ *
+ * This function will return detailed hardware status if available for diagnostics of the CAN interface.
+ *
+ * @param busOffCount The number of times that sendMessage failed with a busOff error indicating that messages
+ * are not successfully transmitted on the bus.
+ * @param txFullCount The number of times that sendMessage failed with a txFifoFull error indicating that messages
+ * are not successfully received by any CAN device.
+ * @param receiveErrorCount The count of receive errors as reported by the CAN driver.
+ * @param transmitErrorCount The count of transmit errors as reported by the CAN driver.
+ * @return This should return 0 if all status was retrieved successfully or an error code if not.
+ */
+ virtual int32_t getStatus(uint32_t &busOffCount, uint32_t &txFullCount, uint32_t &receiveErrorCount, uint32_t &transmitErrorCount) {return 0;}
+#endif
+};
+
+/**
+ * This function allows you to register a CANInterfacePlugin to provide access a CAN bus.
+ *
+ * @param interface A pointer to an object that inherits from CANInterfacePlugin and implements
+ * the pure virtual interface. If NULL, unregister the current plugin.
+ */
+void FRC_NetworkCommunication_CANSessionMux_registerInterface(CANInterfacePlugin* interface);
+
+#endif // __CANInterfacePlugin_h__
diff --git a/aos/externals/allwpilib/hal/lib/Athena/NetworkCommunication/CANSessionMux.h b/aos/externals/allwpilib/hal/lib/Athena/NetworkCommunication/CANSessionMux.h
new file mode 100644
index 0000000..3f8a6f1
--- /dev/null
+++ b/aos/externals/allwpilib/hal/lib/Athena/NetworkCommunication/CANSessionMux.h
@@ -0,0 +1,64 @@
+// CANSessionMux.h
+//
+// Defines the API for building a CAN Interface Plugin to support
+// PWM-cable-free CAN motor control on FRC robots. This allows you
+// to connect any CAN interface to the secure Jaguar CAN driver.
+//
+
+#ifndef __CANSessionMux_h__
+#define __CANSessionMux_h__
+
+#if defined(__vxworks)
+#include <vxWorks.h>
+#else
+#include <stdint.h>
+#endif
+
+#define CAN_SEND_PERIOD_NO_REPEAT 0
+#define CAN_SEND_PERIOD_STOP_REPEATING -1
+
+/* Flags in the upper bits of the messageID */
+#define CAN_IS_FRAME_REMOTE 0x80000000
+#define CAN_IS_FRAME_11BIT 0x40000000
+
+#define ERR_CANSessionMux_InvalidBuffer -44086
+#define ERR_CANSessionMux_MessageNotFound -44087
+#define WARN_CANSessionMux_NoToken 44087
+#define ERR_CANSessionMux_NotAllowed -44088
+#define ERR_CANSessionMux_NotInitialized -44089
+#define ERR_CANSessionMux_SessionOverrun 44050
+
+struct tCANStreamMessage{
+ uint32_t messageID;
+ uint32_t timeStamp;
+ uint8_t data[8];
+ uint8_t dataSize;
+};
+
+namespace nCANSessionMux
+{
+ void sendMessage_wrapper(uint32_t messageID, const uint8_t *data, uint8_t dataSize, int32_t periodMs, int32_t *status);
+ void receiveMessage_wrapper(uint32_t *messageID, uint32_t messageIDMask, uint8_t *data, uint8_t *dataSize, uint32_t *timeStamp, int32_t *status);
+ void openStreamSession(uint32_t *sessionHandle, uint32_t messageID, uint32_t messageIDMask, uint32_t maxMessages, int32_t *status);
+ void closeStreamSession(uint32_t sessionHandle);
+ void readStreamSession(uint32_t sessionHandle, struct tCANStreamMessage *messages, uint32_t messagesToRead, uint32_t *messagesRead, int32_t *status);
+ void getCANStatus(float *percentBusUtilization, uint32_t *busOffCount, uint32_t *txFullCount, uint32_t *receiveErrorCount, uint32_t *transmitErrorCount, int32_t *status);
+}
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+ void FRC_NetworkCommunication_CANSessionMux_sendMessage(uint32_t messageID, const uint8_t *data, uint8_t dataSize, int32_t periodMs, int32_t *status);
+ void FRC_NetworkCommunication_CANSessionMux_receiveMessage(uint32_t *messageID, uint32_t messageIDMask, uint8_t *data, uint8_t *dataSize, uint32_t *timeStamp, int32_t *status);
+ void FRC_NetworkCommunication_CANSessionMux_openStreamSession(uint32_t *sessionHandle, uint32_t messageID, uint32_t messageIDMask, uint32_t maxMessages, int32_t *status);
+ void FRC_NetworkCommunication_CANSessionMux_closeStreamSession(uint32_t sessionHandle);
+ void FRC_NetworkCommunication_CANSessionMux_readStreamSession(uint32_t sessionHandle, struct tCANStreamMessage *messages, uint32_t messagesToRead, uint32_t *messagesRead, int32_t *status);
+ void FRC_NetworkCommunication_CANSessionMux_getCANStatus(float *percentBusUtilization, uint32_t *busOffCount, uint32_t *txFullCount, uint32_t *receiveErrorCount, uint32_t *transmitErrorCount, int32_t *status);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // __CANSessionMux_h__
diff --git a/aos/externals/allwpilib/hal/lib/Athena/NetworkCommunication/FRCComm.h b/aos/externals/allwpilib/hal/lib/Athena/NetworkCommunication/FRCComm.h
new file mode 100644
index 0000000..d708136
--- /dev/null
+++ b/aos/externals/allwpilib/hal/lib/Athena/NetworkCommunication/FRCComm.h
@@ -0,0 +1,129 @@
+/*************************************************************
+ * NOTICE
+ *
+ * These are the only externally exposed functions to the
+ * NetworkCommunication library
+ *
+ * This is an implementation of FRC Spec for Comm Protocol
+ * Revision 4.5, June 30, 2008
+ *
+ * Copyright (c) National Instruments 2008. All Rights Reserved.
+ *
+ *************************************************************/
+
+#ifndef __FRC_COMM_H__
+#define __FRC_COMM_H__
+
+#ifdef SIMULATION
+#include <vxWorks_compat.h>
+#ifdef USE_THRIFT
+#define EXPORT_FUNC
+#else
+#define EXPORT_FUNC __declspec(dllexport) __cdecl
+#endif
+#else
+#if defined(__vxworks)
+#include <vxWorks.h>
+#define EXPORT_FUNC
+#else
+#include <stdint.h>
+#include <pthread.h>
+#define EXPORT_FUNC
+#endif
+#endif
+
+#define ERR_FRCSystem_NetCommNotResponding -44049
+#define ERR_FRCSystem_NoDSConnection -44018
+
+enum AllianceStationID_t {
+ kAllianceStationID_red1,
+ kAllianceStationID_red2,
+ kAllianceStationID_red3,
+ kAllianceStationID_blue1,
+ kAllianceStationID_blue2,
+ kAllianceStationID_blue3,
+};
+
+enum MatchType_t {
+ kMatchType_none,
+ kMatchType_practice,
+ kMatchType_qualification,
+ kMatchType_elimination,
+};
+
+struct ControlWord_t {
+#ifndef __vxworks
+ uint32_t enabled : 1;
+ uint32_t autonomous : 1;
+ uint32_t test :1;
+ uint32_t eStop : 1;
+ uint32_t fmsAttached:1;
+ uint32_t dsAttached:1;
+ uint32_t control_reserved : 26;
+#else
+ uint32_t control_reserved : 26;
+ uint32_t dsAttached:1;
+ uint32_t fmsAttached:1;
+ uint32_t eStop : 1;
+ uint32_t test :1;
+ uint32_t autonomous : 1;
+ uint32_t enabled : 1;
+#endif
+};
+
+struct JoystickAxes_t {
+ uint16_t count;
+ int16_t axes[1];
+};
+
+struct JoystickPOV_t {
+ uint16_t count;
+ int16_t povs[1];
+};
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+ int EXPORT_FUNC FRC_NetworkCommunication_Reserve(void *instance);
+#ifndef SIMULATION
+ void EXPORT_FUNC getFPGAHardwareVersion(uint16_t *fpgaVersion, uint32_t *fpgaRevision);
+#endif
+ int EXPORT_FUNC setStatusData(float battery, uint8_t dsDigitalOut, uint8_t updateNumber,
+ const char *userDataHigh, int userDataHighLength,
+ const char *userDataLow, int userDataLowLength, int wait_ms);
+ int EXPORT_FUNC setErrorData(const char *errors, int errorsLength, int wait_ms);
+
+#ifdef SIMULATION
+ void EXPORT_FUNC setNewDataSem(HANDLE);
+#else
+# if defined (__vxworks)
+ void EXPORT_FUNC setNewDataSem(SEM_ID);
+# else
+ void EXPORT_FUNC setNewDataSem(pthread_cond_t *);
+# endif
+#endif
+
+ // this uint32_t is really a LVRefNum
+ int EXPORT_FUNC setNewDataOccurRef(uint32_t refnum);
+
+ int EXPORT_FUNC FRC_NetworkCommunication_getControlWord(struct ControlWord_t *controlWord);
+ int EXPORT_FUNC FRC_NetworkCommunication_getAllianceStation(enum AllianceStationID_t *allianceStation);
+ int EXPORT_FUNC FRC_NetworkCommunication_getMatchTime(float *matchTime);
+ int EXPORT_FUNC FRC_NetworkCommunication_getJoystickAxes(uint8_t joystickNum, struct JoystickAxes_t *axes, uint8_t maxAxes);
+ int EXPORT_FUNC FRC_NetworkCommunication_getJoystickButtons(uint8_t joystickNum, uint32_t *buttons, uint8_t *count);
+ int EXPORT_FUNC FRC_NetworkCommunication_getJoystickPOVs(uint8_t joystickNum, struct JoystickPOV_t *povs, uint8_t maxPOVs);
+ int EXPORT_FUNC FRC_NetworkCommunication_setJoystickOutputs(uint8_t joystickNum, uint32_t hidOutputs, uint16_t leftRumble, uint16_t rightRumble);
+ int EXPORT_FUNC FRC_NetworkCommunication_getJoystickDesc(uint8_t joystickNum, uint8_t *isXBox, uint8_t *type, char *name,
+ uint8_t *axisCount, uint8_t *axisTypes, uint8_t *buttonCount, uint8_t *povCount);
+
+ void EXPORT_FUNC FRC_NetworkCommunication_getVersionString(char *version);
+ int EXPORT_FUNC FRC_NetworkCommunication_observeUserProgramStarting(void);
+ void EXPORT_FUNC FRC_NetworkCommunication_observeUserProgramDisabled(void);
+ void EXPORT_FUNC FRC_NetworkCommunication_observeUserProgramAutonomous(void);
+ void EXPORT_FUNC FRC_NetworkCommunication_observeUserProgramTeleop(void);
+ void EXPORT_FUNC FRC_NetworkCommunication_observeUserProgramTest(void);
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/aos/externals/allwpilib/hal/lib/Athena/NetworkCommunication/LoadOut.h b/aos/externals/allwpilib/hal/lib/Athena/NetworkCommunication/LoadOut.h
new file mode 100644
index 0000000..fce88a2
--- /dev/null
+++ b/aos/externals/allwpilib/hal/lib/Athena/NetworkCommunication/LoadOut.h
@@ -0,0 +1,58 @@
+
+#ifndef __LoadOut_h__
+#define __LoadOut_h__
+
+#ifdef SIMULATION
+#include <vxWorks_compat.h>
+#define EXPORT_FUNC __declspec(dllexport) __cdecl
+#elif defined (__vxworks)
+#include <vxWorks.h>
+#define EXPORT_FUNC
+#else
+#include <stdint.h>
+#define EXPORT_FUNC
+#endif
+
+#define kMaxModuleNumber 2
+namespace nLoadOut
+{
+#if defined(__vxworks) || defined(SIMULATION)
+ typedef enum {
+ kModuleType_Unknown = 0x00,
+ kModuleType_Analog = 0x01,
+ kModuleType_Digital = 0x02,
+ kModuleType_Solenoid = 0x03,
+ } tModuleType;
+ bool EXPORT_FUNC getModulePresence(tModuleType moduleType, uint8_t moduleNumber);
+#endif
+ typedef enum {
+ kTargetClass_Unknown = 0x00,
+ kTargetClass_FRC1 = 0x10,
+ kTargetClass_FRC2 = 0x20,
+ kTargetClass_FRC3 = 0x30,
+ kTargetClass_RoboRIO = 0x40,
+#if defined(__vxworks) || defined(SIMULATION)
+ kTargetClass_FRC2_Analog = kTargetClass_FRC2 | kModuleType_Analog,
+ kTargetClass_FRC2_Digital = kTargetClass_FRC2 | kModuleType_Digital,
+ kTargetClass_FRC2_Solenoid = kTargetClass_FRC2 | kModuleType_Solenoid,
+#endif
+ kTargetClass_FamilyMask = 0xF0,
+ kTargetClass_ModuleMask = 0x0F,
+ } tTargetClass;
+ tTargetClass EXPORT_FUNC getTargetClass();
+}
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if defined(__vxworks) || defined(SIMULATION)
+ uint32_t EXPORT_FUNC FRC_NetworkCommunication_nLoadOut_getModulePresence(uint32_t moduleType, uint8_t moduleNumber);
+#endif
+ uint32_t EXPORT_FUNC FRC_NetworkCommunication_nLoadOut_getTargetClass();
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // __LoadOut_h__
diff --git a/aos/externals/allwpilib/hal/lib/Athena/NetworkCommunication/UsageReporting.h b/aos/externals/allwpilib/hal/lib/Athena/NetworkCommunication/UsageReporting.h
new file mode 100644
index 0000000..0ec2a3a
--- /dev/null
+++ b/aos/externals/allwpilib/hal/lib/Athena/NetworkCommunication/UsageReporting.h
@@ -0,0 +1,143 @@
+
+#ifndef __UsageReporting_h__
+#define __UsageReporting_h__
+
+#ifdef SIMULATION
+#include <vxWorks_compat.h>
+#define EXPORT_FUNC __declspec(dllexport) __cdecl
+#elif defined (__vxworks)
+#include <vxWorks.h>
+#define EXPORT_FUNC
+#else
+#include <stdint.h>
+#include <stdlib.h>
+#define EXPORT_FUNC
+#endif
+
+#define kUsageReporting_version 1
+
+namespace nUsageReporting
+{
+ typedef enum
+ {
+ kResourceType_Controller,
+ kResourceType_Module,
+ kResourceType_Language,
+ kResourceType_CANPlugin,
+ kResourceType_Accelerometer,
+ kResourceType_ADXL345,
+ kResourceType_AnalogChannel,
+ kResourceType_AnalogTrigger,
+ kResourceType_AnalogTriggerOutput,
+ kResourceType_CANJaguar,
+ kResourceType_Compressor,
+ kResourceType_Counter,
+ kResourceType_Dashboard,
+ kResourceType_DigitalInput,
+ kResourceType_DigitalOutput,
+ kResourceType_DriverStationCIO,
+ kResourceType_DriverStationEIO,
+ kResourceType_DriverStationLCD,
+ kResourceType_Encoder,
+ kResourceType_GearTooth,
+ kResourceType_Gyro,
+ kResourceType_I2C,
+ kResourceType_Framework,
+ kResourceType_Jaguar,
+ kResourceType_Joystick,
+ kResourceType_Kinect,
+ kResourceType_KinectStick,
+ kResourceType_PIDController,
+ kResourceType_Preferences,
+ kResourceType_PWM,
+ kResourceType_Relay,
+ kResourceType_RobotDrive,
+ kResourceType_SerialPort,
+ kResourceType_Servo,
+ kResourceType_Solenoid,
+ kResourceType_SPI,
+ kResourceType_Task,
+ kResourceType_Ultrasonic,
+ kResourceType_Victor,
+ kResourceType_Button,
+ kResourceType_Command,
+ kResourceType_AxisCamera,
+ kResourceType_PCVideoServer,
+ kResourceType_SmartDashboard,
+ kResourceType_Talon,
+ kResourceType_HiTechnicColorSensor,
+ kResourceType_HiTechnicAccel,
+ kResourceType_HiTechnicCompass,
+ kResourceType_SRF08,
+ kResourceType_AnalogOutput,
+ kResourceType_VictorSP,
+ kResourceType_TalonSRX,
+ kResourceType_CANTalonSRX,
+ } tResourceType;
+
+ typedef enum
+ {
+ kLanguage_LabVIEW = 1,
+ kLanguage_CPlusPlus = 2,
+ kLanguage_Java = 3,
+ kLanguage_Python = 4,
+
+ kCANPlugin_BlackJagBridge = 1,
+ kCANPlugin_2CAN = 2,
+
+ kFramework_Iterative = 1,
+ kFramework_Simple = 2,
+
+ kRobotDrive_ArcadeStandard = 1,
+ kRobotDrive_ArcadeButtonSpin = 2,
+ kRobotDrive_ArcadeRatioCurve = 3,
+ kRobotDrive_Tank = 4,
+ kRobotDrive_MecanumPolar = 5,
+ kRobotDrive_MecanumCartesian = 6,
+
+ kDriverStationCIO_Analog = 1,
+ kDriverStationCIO_DigitalIn = 2,
+ kDriverStationCIO_DigitalOut = 3,
+
+ kDriverStationEIO_Acceleration = 1,
+ kDriverStationEIO_AnalogIn = 2,
+ kDriverStationEIO_AnalogOut = 3,
+ kDriverStationEIO_Button = 4,
+ kDriverStationEIO_LED = 5,
+ kDriverStationEIO_DigitalIn = 6,
+ kDriverStationEIO_DigitalOut = 7,
+ kDriverStationEIO_FixedDigitalOut = 8,
+ kDriverStationEIO_PWM = 9,
+ kDriverStationEIO_Encoder = 10,
+ kDriverStationEIO_TouchSlider = 11,
+
+ kADXL345_SPI = 1,
+ kADXL345_I2C = 2,
+
+ kCommand_Scheduler = 1,
+
+ kSmartDashboard_Instance = 1,
+ } tInstances;
+
+ /**
+ * Report the usage of a resource of interest.
+ *
+ * @param resource one of the values in the tResourceType above (max value 51).
+ * @param instanceNumber an index that identifies the resource instance.
+ * @param context an optional additional context number for some cases (such as module number). Set to 0 to omit.
+ * @param feature a string to be included describing features in use on a specific resource. Setting the same resource more than once allows you to change the feature string.
+ */
+ uint32_t EXPORT_FUNC report(tResourceType resource, uint8_t instanceNumber, uint8_t context = 0, const char *feature = NULL);
+}
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ uint32_t EXPORT_FUNC FRC_NetworkCommunication_nUsageReporting_report(uint8_t resource, uint8_t instanceNumber, uint8_t context, const char *feature);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // __UsageReporting_h__
diff --git a/aos/externals/allwpilib/hal/lib/Athena/Notifier.cpp b/aos/externals/allwpilib/hal/lib/Athena/Notifier.cpp
new file mode 100644
index 0000000..c532512
--- /dev/null
+++ b/aos/externals/allwpilib/hal/lib/Athena/Notifier.cpp
@@ -0,0 +1,40 @@
+#include "HAL/Notifier.hpp"
+#include "ChipObject.h"
+
+static const uint32_t kTimerInterruptNumber = 28;
+
+struct Notifier
+{
+ tAlarm *alarm;
+ tInterruptManager *manager;
+};
+
+void* initializeNotifier(void (*ProcessQueue)(uint32_t, void*), int32_t *status)
+{
+ Notifier* notifier = new Notifier();
+ notifier->manager = new tInterruptManager(1 << kTimerInterruptNumber, false, status);
+ notifier->manager->registerHandler(ProcessQueue, NULL, status);
+ notifier->manager->enable(status);
+ notifier->alarm = tAlarm::create(status);
+ return notifier;
+}
+
+void cleanNotifier(void* notifier_pointer, int32_t *status)
+{
+ Notifier* notifier = (Notifier*)notifier_pointer;
+ notifier->alarm->writeEnable(false, status);
+ delete notifier->alarm;
+ notifier->alarm = NULL;
+ notifier->manager->disable(status);
+ delete notifier->manager;
+ notifier->manager = NULL;
+}
+
+void updateNotifierAlarm(void* notifier_pointer, uint32_t triggerTime, int32_t *status)
+{
+ Notifier* notifier = (Notifier*)notifier_pointer;
+ // write the first item in the queue into the trigger time
+ notifier->alarm->writeTriggerTime(triggerTime, status);
+ // Enable the alarm. The hardware disables itself after each alarm.
+ notifier->alarm->writeEnable(true, status);
+}
diff --git a/aos/externals/allwpilib/hal/lib/Athena/PDP.cpp b/aos/externals/allwpilib/hal/lib/Athena/PDP.cpp
new file mode 100644
index 0000000..6129c06
--- /dev/null
+++ b/aos/externals/allwpilib/hal/lib/Athena/PDP.cpp
@@ -0,0 +1,61 @@
+#include "HAL/PDP.hpp"
+#include "ctre/PDP.h"
+static PDP pdp;
+
+double getPDPTemperature(int32_t *status) {
+ double temperature;
+
+ *status = pdp.GetTemperature(temperature);
+
+ return temperature;
+}
+
+double getPDPVoltage(int32_t *status) {
+ double voltage;
+
+ *status = pdp.GetVoltage(voltage);
+
+ return voltage;
+}
+
+double getPDPChannelCurrent(uint8_t channel, int32_t *status) {
+ double current;
+
+ *status = pdp.GetChannelCurrent(channel, current);
+
+ return current;
+}
+
+double getPDPTotalCurrent(int32_t *status) {
+ double current;
+
+ *status = pdp.GetTotalCurrent(current);
+
+ return current;
+}
+
+double getPDPTotalPower(int32_t *status) {
+ double power;
+
+ *status = pdp.GetTotalPower(power);
+
+ return power;
+}
+
+double getPDPTotalEnergy(int32_t *status) {
+ double energy;
+
+ *status = pdp.GetTotalEnergy(energy);
+
+ return energy;
+}
+
+void resetPDPTotalEnergy(int32_t *status) {
+ *status = pdp.ResetEnergy();
+}
+
+void clearPDPStickyFaults(int32_t *status) {
+ *status = pdp.ClearStickyFaults();
+}
+
+
diff --git a/aos/externals/allwpilib/hal/lib/Athena/Port.h b/aos/externals/allwpilib/hal/lib/Athena/Port.h
new file mode 100644
index 0000000..8679c26
--- /dev/null
+++ b/aos/externals/allwpilib/hal/lib/Athena/Port.h
@@ -0,0 +1,7 @@
+#pragma once
+
+typedef struct port_t
+{
+ uint8_t pin;
+ uint8_t module;
+} Port;
diff --git a/aos/externals/allwpilib/hal/lib/Athena/Power.cpp b/aos/externals/allwpilib/hal/lib/Athena/Power.cpp
new file mode 100644
index 0000000..34db043
--- /dev/null
+++ b/aos/externals/allwpilib/hal/lib/Athena/Power.cpp
@@ -0,0 +1,127 @@
+#include "HAL/Power.hpp"
+#include "ChipObject.h"
+
+static tPower *power = NULL;
+
+static void initializePower(int32_t *status) {
+ if(power == NULL) {
+ power = tPower::create(status);
+ }
+}
+
+/**
+ * Get the roboRIO input voltage
+ */
+float getVinVoltage(int32_t *status) {
+ initializePower(status);
+ return power->readVinVoltage(status) / 4.096f * 0.025733f - 0.029f;
+}
+
+/**
+ * Get the roboRIO input current
+ */
+float getVinCurrent(int32_t *status) {
+ initializePower(status);
+ return power->readVinCurrent(status) / 4.096f * 0.017042 - 0.071f;
+}
+
+/**
+ * Get the 6V rail voltage
+ */
+float getUserVoltage6V(int32_t *status) {
+ initializePower(status);
+ return power->readUserVoltage6V(status) / 4.096f * 0.007019f - 0.014f;
+}
+
+/**
+ * Get the 6V rail current
+ */
+float getUserCurrent6V(int32_t *status) {
+ initializePower(status);
+ return power->readUserCurrent6V(status) / 4.096f * 0.005566f - 0.009f;
+}
+
+/**
+ * Get the active state of the 6V rail
+ */
+bool getUserActive6V(int32_t *status) {
+ initializePower(status);
+ return power->readStatus_User6V(status) == 4;
+}
+
+/**
+ * Get the fault count for the 6V rail
+ */
+int getUserCurrentFaults6V(int32_t *status) {
+ initializePower(status);
+ return (int)power->readOverCurrentFaultCounts_OverCurrentFaultCount6V(status);
+}
+
+/**
+ * Get the 5V rail voltage
+ */
+float getUserVoltage5V(int32_t *status) {
+ initializePower(status);
+ return power->readUserVoltage5V(status) / 4.096f * 0.005962f - 0.013f;
+}
+
+/**
+ * Get the 5V rail current
+ */
+float getUserCurrent5V(int32_t *status) {
+ initializePower(status);
+ return power->readUserCurrent5V(status) / 4.096f * 0.001996f - 0.002f;
+}
+
+/**
+ * Get the active state of the 5V rail
+ */
+bool getUserActive5V(int32_t *status) {
+ initializePower(status);
+ return power->readStatus_User5V(status) == 4;
+}
+
+/**
+ * Get the fault count for the 5V rail
+ */
+int getUserCurrentFaults5V(int32_t *status) {
+ initializePower(status);
+ return (int)power->readOverCurrentFaultCounts_OverCurrentFaultCount5V(status);
+}
+
+unsigned char getUserStatus5V(int32_t *status) {
+ initializePower(status);
+ return power->readStatus_User5V(status);
+}
+
+/**
+ * Get the 3.3V rail voltage
+ */
+float getUserVoltage3V3(int32_t *status) {
+ initializePower(status);
+ return power->readUserVoltage3V3(status) / 4.096f * 0.004902f - 0.01f;
+}
+
+/**
+ * Get the 3.3V rail current
+ */
+float getUserCurrent3V3(int32_t *status) {
+ initializePower(status);
+ return power->readUserCurrent3V3(status) / 4.096f * 0.002486f - 0.003f;
+}
+
+/**
+ * Get the active state of the 3.3V rail
+ */
+bool getUserActive3V3(int32_t *status) {
+ initializePower(status);
+ return power->readStatus_User3V3(status) == 4;
+}
+
+/**
+ * Get the fault count for the 3.3V rail
+ */
+int getUserCurrentFaults3V3(int32_t *status) {
+ initializePower(status);
+ return (int)power->readOverCurrentFaultCounts_OverCurrentFaultCount3V3(status);
+}
diff --git a/aos/externals/allwpilib/hal/lib/Athena/Semaphore.cpp b/aos/externals/allwpilib/hal/lib/Athena/Semaphore.cpp
new file mode 100644
index 0000000..df30f3c
--- /dev/null
+++ b/aos/externals/allwpilib/hal/lib/Athena/Semaphore.cpp
@@ -0,0 +1,150 @@
+#include "HAL/Semaphore.hpp"
+#include "HAL/cpp/Synchronized.hpp"
+
+#include "Log.hpp"
+
+// set the logging level
+TLogLevel semaphoreLogLevel = logDEBUG;
+
+#define SEMAPHORE_LOG(level) \
+ if (level > semaphoreLogLevel) ; \
+ else Log().Get(level)
+
+// See: http://www.vxdev.com/docs/vx55man/vxworks/ref/semMLib.html
+const uint32_t SEMAPHORE_Q_FIFO= 0x01; // TODO: Support
+const uint32_t SEMAPHORE_Q_PRIORITY = 0x01; // TODO: Support
+const uint32_t SEMAPHORE_DELETE_SAFE = 0x04; // TODO: Support
+const uint32_t SEMAPHORE_INVERSION_SAFE = 0x08; // TODO: Support
+
+const int32_t SEMAPHORE_NO_WAIT = 0;
+const int32_t SEMAPHORE_WAIT_FOREVER = -1;
+
+const uint32_t SEMAPHORE_EMPTY = 0;
+const uint32_t SEMAPHORE_FULL = 1;
+
+class MutexInterface {
+ public:
+ virtual void lock() = 0;
+ virtual void unlock() = 0;
+ virtual bool try_lock() = 0;
+ virtual pthread_mutex_t *native_handle() = 0;
+ virtual ~MutexInterface() {}
+};
+
+template <typename MutexType>
+class TemplatedMutexWrapper : public MutexInterface {
+ public:
+ virtual void lock() { m_.lock(); }
+ virtual void unlock() { m_.unlock(); }
+ virtual bool try_lock() { return m_.try_lock(); }
+ pthread_mutex_t *native_handle() { return m_.native_handle(); }
+
+ private:
+ MutexType m_;
+};
+
+MUTEX_ID initializeMutexRecursive()
+{
+ return new TemplatedMutexWrapper<ReentrantMutex>();
+}
+
+MUTEX_ID initializeMutexNormal()
+{
+ return new TemplatedMutexWrapper<Mutex>();
+}
+
+void deleteMutex(MUTEX_ID mutex)
+{
+ delete mutex;
+}
+
+/**
+ * Lock the semaphore, blocking until it's available.
+ * @return 0 for success, -1 for error. If -1, the error will be in errno.
+ */
+int8_t lockMutex(MUTEX_ID mutex)
+{
+ mutex->lock();
+ return 0;
+}
+
+/**
+ * Tries to lock the mutex.
+ * @return 1 if we got the lock, 0 otherwise.
+ */
+int8_t tryLockMutex(MUTEX_ID mutex)
+{
+ return mutex->try_lock();
+}
+
+/**
+ * Unlock the semaphore.
+ * @return 0 for success, -1 for error. If -1, the error will be in errno.
+ */
+int8_t unlockMutex(MUTEX_ID mutex)
+{
+ mutex->unlock();
+ return 0;
+}
+
+SEMAPHORE_ID initializeSemaphore(uint32_t initial_value) {
+ SEMAPHORE_ID sem = new sem_t;
+ sem_init(sem, 0, initial_value);
+ return sem;
+}
+
+void deleteSemaphore(SEMAPHORE_ID sem) {
+ sem_destroy(sem);
+ delete sem;
+}
+
+/**
+ * Lock the semaphore, blocking until it's available.
+ * @return 0 for success, -1 for error. If -1, the error will be in errno.
+ */
+int8_t takeSemaphore(SEMAPHORE_ID sem)
+{
+ return sem_wait(sem);
+}
+
+int8_t tryTakeSemaphore(SEMAPHORE_ID sem)
+{
+ return sem_trywait(sem);
+}
+
+/**
+ * Unlock the semaphore.
+ * @return 0 for success, -1 for error. If -1, the error will be in errno.
+ */
+int8_t giveSemaphore(SEMAPHORE_ID sem)
+{
+ return sem_post(sem);
+}
+
+
+MULTIWAIT_ID initializeMultiWait() {
+ pthread_condattr_t attr;
+ pthread_condattr_init(&attr);
+ MULTIWAIT_ID cond = new pthread_cond_t();
+ pthread_cond_init(cond, &attr);
+ pthread_condattr_destroy(&attr);
+ return cond;
+}
+
+void deleteMultiWait(MULTIWAIT_ID sem) {
+ pthread_cond_destroy(sem);
+ delete sem;
+}
+
+int8_t takeMultiWait(MULTIWAIT_ID sem, MUTEX_ID m, int32_t timeout) {
+ lockMutex(m);
+ int8_t val = pthread_cond_wait(sem, m->native_handle());
+ unlockMutex(m);
+ return val;
+}
+
+int8_t giveMultiWait(MULTIWAIT_ID sem) {
+ return pthread_cond_broadcast(sem);
+}
+
+
diff --git a/aos/externals/allwpilib/hal/lib/Athena/Solenoid.cpp b/aos/externals/allwpilib/hal/lib/Athena/Solenoid.cpp
new file mode 100644
index 0000000..3b8bfaf
--- /dev/null
+++ b/aos/externals/allwpilib/hal/lib/Athena/Solenoid.cpp
@@ -0,0 +1,90 @@
+
+#include "HAL/Solenoid.hpp"
+
+#include "Port.h"
+#include "HAL/Errors.hpp"
+#include "ChipObject.h"
+#include "HAL/cpp/Synchronized.hpp"
+#include "NetworkCommunication/LoadOut.h"
+#include "ctre/PCM.h"
+
+static const int NUM_MODULE_NUMBERS = 63;
+
+PCM *modules[NUM_MODULE_NUMBERS] = { NULL };
+
+struct solenoid_port_t {
+ PCM *module;
+ uint32_t pin;
+};
+
+void initializePCM(int module) {
+ if(!modules[module]) {
+ modules[module] = new PCM(module);
+ }
+}
+
+void* initializeSolenoidPort(void *port_pointer, int32_t *status) {
+ Port* port = (Port*) port_pointer;
+ initializePCM(port->module);
+
+ solenoid_port_t *solenoid_port = new solenoid_port_t;
+ solenoid_port->module = modules[port->module];
+ solenoid_port->pin = port->pin;
+
+ return solenoid_port;
+}
+
+bool checkSolenoidModule(uint8_t module) {
+ return module < NUM_MODULE_NUMBERS;
+}
+
+bool getSolenoid(void* solenoid_port_pointer, int32_t *status) {
+ solenoid_port_t* port = (solenoid_port_t*) solenoid_port_pointer;
+ bool value;
+
+ *status = port->module->GetSolenoid(port->pin, value);
+
+ return value;
+}
+
+void setSolenoid(void* solenoid_port_pointer, bool value, int32_t *status) {
+ solenoid_port_t* port = (solenoid_port_t*) solenoid_port_pointer;
+
+ port->module->SetSolenoids(1 << port->pin, value ? 0xFF : 0x00);
+}
+
+void setSolenoids(void* solenoid_port_pointer, uint8_t value, uint8_t mask, int32_t *status) {
+ solenoid_port_t* port = (solenoid_port_t*) solenoid_port_pointer;
+
+ port->module->SetSolenoids(value, mask);
+}
+
+int getPCMSolenoidBlackList(void* solenoid_port_pointer, int32_t *status){
+ solenoid_port_t* port = (solenoid_port_t*) solenoid_port_pointer;
+ UINT8 value;
+
+ *status = port->module->GetSolenoidBlackList(value);
+
+ return value;
+}
+bool getPCMSolenoidVoltageStickyFault(void* solenoid_port_pointer, int32_t *status){
+ solenoid_port_t* port = (solenoid_port_t*) solenoid_port_pointer;
+ bool value;
+
+ *status = port->module->GetSolenoidStickyFault(value);
+
+ return value;
+}
+bool getPCMSolenoidVoltageFault(void* solenoid_port_pointer, int32_t *status){
+ solenoid_port_t* port = (solenoid_port_t*) solenoid_port_pointer;
+ bool value;
+
+ *status = port->module->GetSolenoidFault(value);
+
+ return value;
+}
+void clearAllPCMStickyFaults_sol(void *solenoid_port_pointer, int32_t *status){
+ solenoid_port_t* port = (solenoid_port_t*) solenoid_port_pointer;
+
+ *status = port->module->ClearStickyFaults();
+}
diff --git a/aos/externals/allwpilib/hal/lib/Athena/Task.cpp b/aos/externals/allwpilib/hal/lib/Athena/Task.cpp
new file mode 100644
index 0000000..2be66f5
--- /dev/null
+++ b/aos/externals/allwpilib/hal/lib/Athena/Task.cpp
@@ -0,0 +1,104 @@
+
+#include "HAL/Task.hpp"
+
+#include <stdio.h>
+#include <signal.h>
+
+#ifndef OK
+#define OK 0
+#endif /* OK */
+#ifndef ERROR
+#define ERROR (-1)
+#endif /* ERROR */
+
+const uint32_t VXWORKS_FP_TASK = 0x01000000;
+const int32_t HAL_objLib_OBJ_ID_ERROR = -1; // TODO: update to relevant TaskIDError
+const int32_t HAL_objLib_OBJ_DELETED = -1; // TODO: update to relevant TaskDeletedError
+const int32_t HAL_taskLib_ILLEGAL_OPTIONS = -1; // TODO: update to relevant TaskOptionsError
+const int32_t HAL_memLib_NOT_ENOUGH_MEMORY = -1; // TODO: update to relevant TaskMemoryError
+const int32_t HAL_taskLib_ILLEGAL_PRIORITY = -1; // TODO: update to relevant TaskPriorityError
+
+struct TaskArgs {
+ FUNCPTR fun;
+ char* name;
+ pthread_t** task;
+ uint32_t arg0, arg1, arg2, arg3, arg4,
+ arg5, arg6, arg7, arg8, arg9;
+};
+
+void* startRoutine(void* data) {
+ TaskArgs* args = (TaskArgs*) data;
+ printf("[HAL] Starting task %s...\n", args->name);
+ int val = args->fun(args->arg0, args->arg1, args->arg2, args->arg3, args->arg4,
+ args->arg5, args->arg6, args->arg7, args->arg8, args->arg9);
+ printf("[HAL] Exited task %s with code %i\n", args->name, val);
+ *args->task = NULL;
+ int* ret = new int(); *ret = val;
+ return ret;
+}
+
+TASK spawnTask(char * name, int priority, int options, int stackSize,
+ FUNCPTR entryPt, uint32_t arg0, uint32_t arg1, uint32_t arg2,
+ uint32_t arg3, uint32_t arg4, uint32_t arg5, uint32_t arg6,
+ uint32_t arg7, uint32_t arg8, uint32_t arg9) {
+ printf("[HAL] Spawning task %s...\n", name);
+ pthread_t* task = new pthread_t;
+ pthread_attr_t* attr = new pthread_attr_t;
+ pthread_attr_init(attr);
+ TaskArgs* args = new TaskArgs();
+ args->fun = entryPt;
+ args->name = name;
+ args->task = new pthread_t*;
+ *args->task = task;
+ args->arg0 = arg0; args->arg1 = arg1; args->arg2 = arg2; args->arg3 = arg3; args->arg4 = arg4;
+ args->arg5 = arg5; args->arg6 = arg6; args->arg7 = arg7; args->arg8 = arg8; args->arg9 = arg9;
+ if (pthread_create(task, attr, startRoutine, args) == 0) {
+ printf("[HAL] Success\n");
+ pthread_detach(*task);
+ } else {
+ printf("[HAL] Failure\n");
+ task = NULL;
+ }
+ pthread_attr_destroy(attr);
+ return task;
+}
+
+STATUS restartTask(TASK task) {
+ return ERROR; // TODO: implement;
+}
+
+STATUS deleteTask(TASK task) {
+ return ERROR; // TODO: implement
+}
+
+STATUS isTaskReady(TASK task) {
+ return ERROR; // TODO: implement
+}
+
+STATUS isTaskSuspended(TASK task) {
+ return ERROR; // TODO: implement
+}
+
+STATUS suspendTask(TASK task) {
+ return ERROR; // TODO: implement
+}
+
+STATUS resumeTask(TASK task) {
+ return ERROR; // TODO: implement
+}
+
+STATUS verifyTaskID(TASK task) {
+ if (task != NULL && pthread_kill(*task, 0) == 0) {
+ return OK;
+ } else {
+ return ERROR;
+ }
+}
+
+STATUS setTaskPriority(TASK task, int priority) {
+ return ERROR; // TODO: implement
+}
+
+STATUS getTaskPriority(TASK task, int* priority) {
+ return ERROR; // TODO: implement
+}
diff --git a/aos/externals/allwpilib/hal/lib/Athena/Utilities.cpp b/aos/externals/allwpilib/hal/lib/Athena/Utilities.cpp
new file mode 100644
index 0000000..b34677f
--- /dev/null
+++ b/aos/externals/allwpilib/hal/lib/Athena/Utilities.cpp
@@ -0,0 +1,44 @@
+#include "HAL/Utilities.hpp"
+#include <time.h>
+
+const int32_t HAL_NO_WAIT = 0;
+const int32_t HAL_WAIT_FOREVER = -1;
+
+void delayTicks(int32_t ticks)
+{
+ struct timespec test, remaining;
+ test.tv_sec = 0;
+ test.tv_nsec = ticks * 3;
+
+ /* Sleep until the requested number of ticks has passed, with additional
+ time added if nanosleep is interrupted. */
+ while(nanosleep(&test, &remaining) == -1) {
+ test = remaining;
+ }
+}
+
+void delayMillis(double ms)
+{
+ struct timespec test, remaining;
+ test.tv_sec = ms / 1000;
+ test.tv_nsec = 1000 * (((uint64_t)ms) % 1000000);
+
+ /* Sleep until the requested number of milliseconds has passed, with
+ additional time added if nanosleep is interrupted. */
+ while(nanosleep(&test, &remaining) == -1) {
+ test = remaining;
+ }
+}
+
+void delaySeconds(double s)
+{
+ struct timespec test, remaining;
+ test.tv_sec = (int)s;
+ test.tv_nsec = (s - (int)s) * 1000000000.0;
+
+ /* Sleep until the requested number of seconds has passed, with additional
+ time added if nanosleep is interrupted. */
+ while(nanosleep(&test, &remaining) == -1) {
+ test = remaining;
+ }
+}
diff --git a/aos/externals/allwpilib/hal/lib/Athena/cpp/Resource.cpp b/aos/externals/allwpilib/hal/lib/Athena/cpp/Resource.cpp
new file mode 100644
index 0000000..ceed1b1
--- /dev/null
+++ b/aos/externals/allwpilib/hal/lib/Athena/cpp/Resource.cpp
@@ -0,0 +1,120 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+
+#include "HAL/cpp/Resource.hpp"
+#include "HAL/Errors.hpp"
+#include <cstddef>
+
+ReentrantMutex Resource::m_createLock;
+
+/**
+ * Allocate storage for a new instance of Resource.
+ * Allocate a bool array of values that will get initialized to indicate that no resources
+ * have been allocated yet. The indicies of the resources are [0 .. elements - 1].
+ */
+Resource::Resource(uint32_t elements)
+{
+ ::std::unique_lock<ReentrantMutex> sync(m_createLock);
+ m_size = elements;
+ m_isAllocated = new bool[m_size];
+ for (uint32_t i=0; i < m_size; i++)
+ {
+ m_isAllocated[i] = false;
+ }
+}
+
+/**
+ * Factory method to create a Resource allocation-tracker *if* needed.
+ *
+ * @param r -- address of the caller's Resource pointer. If *r == NULL, this
+ * will construct a Resource and make *r point to it. If *r != NULL, i.e.
+ * the caller already has a Resource instance, this won't do anything.
+ * @param elements -- the number of elements for this Resource allocator to
+ * track, that is, it will allocate resource numbers in the range
+ * [0 .. elements - 1].
+ */
+/*static*/ void Resource::CreateResourceObject(Resource **r, uint32_t elements)
+{
+ ::std::unique_lock<ReentrantMutex> sync(m_createLock);
+ if (*r == NULL)
+ {
+ *r = new Resource(elements);
+ }
+}
+
+/**
+ * Delete the allocated array or resources.
+ * This happens when the module is unloaded (provided it was statically allocated).
+ */
+Resource::~Resource()
+{
+ delete[] m_isAllocated;
+}
+
+/**
+ * Allocate a resource.
+ * When a resource is requested, mark it allocated. In this case, a free resource value
+ * within the range is located and returned after it is marked allocated.
+ */
+uint32_t Resource::Allocate(const char *resourceDesc)
+{
+ ::std::unique_lock<ReentrantMutex> sync(m_allocateLock);
+ for (uint32_t i=0; i < m_size; i++)
+ {
+ if (!m_isAllocated[i])
+ {
+ m_isAllocated[i] = true;
+ return i;
+ }
+ }
+ // TODO: wpi_setWPIErrorWithContext(NoAvailableResources, resourceDesc);
+ return ~0ul;
+}
+
+/**
+ * Allocate a specific resource value.
+ * The user requests a specific resource value, i.e. channel number and it is verified
+ * unallocated, then returned.
+ */
+uint32_t Resource::Allocate(uint32_t index, const char *resourceDesc)
+{
+ ::std::unique_lock<ReentrantMutex> sync(m_allocateLock);
+ if (index >= m_size)
+ {
+ // TODO: wpi_setWPIErrorWithContext(ChannelIndexOutOfRange, resourceDesc);
+ return ~0ul;
+ }
+ if ( m_isAllocated[index] )
+ {
+ // TODO: wpi_setWPIErrorWithContext(ResourceAlreadyAllocated, resourceDesc);
+ return ~0ul;
+ }
+ m_isAllocated[index] = true;
+ return index;
+}
+
+
+/**
+ * Free an allocated resource.
+ * After a resource is no longer needed, for example a destructor is called for a channel assignment
+ * class, Free will release the resource value so it can be reused somewhere else in the program.
+ */
+void Resource::Free(uint32_t index)
+{
+ ::std::unique_lock<ReentrantMutex> sync(m_allocateLock);
+ if (index == ~0ul) return;
+ if (index >= m_size)
+ {
+ // TODO: wpi_setWPIError(NotAllocated);
+ return;
+ }
+ if (!m_isAllocated[index])
+ {
+ // TODO: wpi_setWPIError(NotAllocated);
+ return;
+ }
+ m_isAllocated[index] = false;
+}
diff --git a/aos/externals/allwpilib/hal/lib/Athena/cpp/Synchronized.cpp b/aos/externals/allwpilib/hal/lib/Athena/cpp/Synchronized.cpp
new file mode 100644
index 0000000..cfc2ad5
--- /dev/null
+++ b/aos/externals/allwpilib/hal/lib/Athena/cpp/Synchronized.cpp
@@ -0,0 +1,28 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+
+#include "HAL/cpp/Synchronized.hpp"
+#include "HAL/Semaphore.hpp"
+
+/**
+ * Synchronized class deals with critical regions.
+ * Declare a Synchronized object at the beginning of a block. That will take the semaphore.
+ * When the code exits from the block it will call the destructor which will give the semaphore.
+ * This ensures that no matter how the block is exited, the semaphore will always be released.
+ * @param semaphore The semaphore controlling this critical region.
+ */
+Synchronized::Synchronized(SEMAPHORE_ID semaphore)
+ : m_semaphore(semaphore) {
+ takeSemaphore(m_semaphore);
+}
+
+/**
+ * This destructor unlocks the semaphore.
+ */
+Synchronized::~Synchronized()
+{
+ giveSemaphore(m_semaphore);
+}
diff --git a/aos/externals/allwpilib/hal/lib/Athena/ctre/CanTalonSRX.cpp b/aos/externals/allwpilib/hal/lib/Athena/ctre/CanTalonSRX.cpp
new file mode 100644
index 0000000..a51099c
--- /dev/null
+++ b/aos/externals/allwpilib/hal/lib/Athena/ctre/CanTalonSRX.cpp
@@ -0,0 +1,1241 @@
+/**
+ * @brief CAN TALON SRX driver.
+ *
+ * The TALON SRX is designed to instrument all runtime signals periodically. The default periods are chosen to support 16 TALONs
+ * with 10ms update rate for control (throttle or setpoint). However these can be overridden with SetStatusFrameRate. @see SetStatusFrameRate
+ * The getters for these unsolicited signals are auto generated at the bottom of this module.
+ *
+ * Likewise most control signals are sent periodically using the fire-and-forget CAN API.
+ * The setters for these unsolicited signals are auto generated at the bottom of this module.
+ *
+ * Signals that are not available in an unsolicited fashion are the Close Loop gains.
+ * For teams that have a single profile for their TALON close loop they can use either the webpage to configure their TALONs once
+ * or set the PIDF,Izone,CloseLoopRampRate,etc... once in the robot application. These parameters are saved to flash so once they are
+ * loaded in the TALON, they will persist through power cycles and mode changes.
+ *
+ * For teams that have one or two profiles to switch between, they can use the same strategy since there are two slots to choose from
+ * and the ProfileSlotSelect is periodically sent in the 10 ms control frame.
+ *
+ * For teams that require changing gains frequently, they can use the soliciting API to get and set those parameters. Most likely
+ * they will only need to set them in a periodic fashion as a function of what motion the application is attempting.
+ * If this API is used, be mindful of the CAN utilization reported in the driver station.
+ *
+ * Encoder position is measured in encoder edges. Every edge is counted (similar to roboRIO 4X mode).
+ * Analog position is 10 bits, meaning 1024 ticks per rotation (0V => 3.3V).
+ * Use SetFeedbackDeviceSelect to select which sensor type you need. Once you do that you can use GetSensorPosition()
+ * and GetSensorVelocity(). These signals are updated on CANBus every 20ms (by default).
+ * If a relative sensor is selected, you can zero (or change the current value) using SetSensorPosition.
+ *
+ * Analog Input and quadrature position (and velocity) are also explicitly reported in GetEncPosition, GetEncVel, GetAnalogInWithOv, GetAnalogInVel.
+ * These signals are available all the time, regardless of what sensor is selected at a rate of 100ms. This allows easy instrumentation
+ * for "in the pits" checking of all sensors regardless of modeselect. The 100ms rate is overridable for teams who want to acquire sensor
+ * data for processing, not just instrumentation. Or just select the sensor using SetFeedbackDeviceSelect to get it at 20ms.
+ *
+ * Velocity is in position ticks / 100ms.
+ *
+ * All output units are in respect to duty cycle (throttle) which is -1023(full reverse) to +1023 (full forward).
+ * This includes demand (which specifies duty cycle when in duty cycle mode) and rampRamp, which is in throttle units per 10ms (if nonzero).
+ *
+ * Pos and velocity close loops are calc'd as
+ * err = target - posOrVel.
+ * iErr += err;
+ * if( (IZone!=0) and abs(err) > IZone)
+ * ClearIaccum()
+ * output = P X err + I X iErr + D X dErr + F X target
+ * dErr = err - lastErr
+ * P, I,and D gains are always positive. F can be negative.
+ * Motor direction can be reversed using SetRevMotDuringCloseLoopEn if sensor and motor are out of phase.
+ * Similarly feedback sensor can also be reversed (multiplied by -1) if you prefer the sensor to be inverted.
+ *
+ * P gain is specified in throttle per error tick. For example, a value of 102 is ~9.9% (which is 102/1023) throttle per 1
+ * ADC unit(10bit) or 1 quadrature encoder edge depending on selected sensor.
+ *
+ * I gain is specified in throttle per integrated error. For example, a value of 10 equates to ~0.99% (which is 10/1023)
+ * for each accumulated ADC unit(10bit) or 1 quadrature encoder edge depending on selected sensor.
+ * Close loop and integral accumulator runs every 1ms.
+ *
+ * D gain is specified in throttle per derivative error. For example a value of 102 equates to ~9.9% (which is 102/1023)
+ * per change of 1 unit (ADC or encoder) per ms.
+ *
+ * I Zone is specified in the same units as sensor position (ADC units or quadrature edges). If pos/vel error is outside of
+ * this value, the integrated error will auto-clear...
+ * if( (IZone!=0) and abs(err) > IZone)
+ * ClearIaccum()
+ * ...this is very useful in preventing integral windup and is highly recommended if using full PID to keep stability low.
+ *
+ * CloseLoopRampRate is in throttle units per 1ms. Set to zero to disable ramping.
+ * Works the same as RampThrottle but only is in effect when a close loop mode and profile slot is selected.
+ *
+ * auto generated using spreadsheet and WpiClassGen.csproj
+ * @link https://docs.google.com/spreadsheets/d/1OU_ZV7fZLGYUQ-Uhc8sVAmUmWTlT8XBFYK8lfjg_tac/edit#gid=1766046967
+ */
+#include "CanTalonSRX.h"
+#include "NetworkCommunication/CANSessionMux.h" //CAN Comm
+#include <string.h> // memset
+#include <unistd.h> // usleep
+
+#define STATUS_1 0x02041400
+#define STATUS_2 0x02041440
+#define STATUS_3 0x02041480
+#define STATUS_4 0x020414C0
+#define STATUS_5 0x02041500
+#define STATUS_6 0x02041540
+#define STATUS_7 0x02041580
+
+#define CONTROL_1 0x02040000
+#define CONTROL_2 0x02040040
+#define CONTROL_3 0x02040080
+
+#define EXPECTED_RESPONSE_TIMEOUT_MS (200)
+#define GET_STATUS1() CtreCanNode::recMsg<TALON_Status_1_General_10ms_t > rx = GetRx<TALON_Status_1_General_10ms_t>(STATUS_1 | GetDeviceNumber(), EXPECTED_RESPONSE_TIMEOUT_MS)
+#define GET_STATUS2() CtreCanNode::recMsg<TALON_Status_2_Feedback_20ms_t > rx = GetRx<TALON_Status_2_Feedback_20ms_t>(STATUS_2 | GetDeviceNumber(), EXPECTED_RESPONSE_TIMEOUT_MS)
+#define GET_STATUS3() CtreCanNode::recMsg<TALON_Status_3_Enc_100ms_t > rx = GetRx<TALON_Status_3_Enc_100ms_t>(STATUS_3 | GetDeviceNumber(), EXPECTED_RESPONSE_TIMEOUT_MS)
+#define GET_STATUS4() CtreCanNode::recMsg<TALON_Status_4_AinTempVbat_100ms_t> rx = GetRx<TALON_Status_4_AinTempVbat_100ms_t>(STATUS_4 | GetDeviceNumber(), EXPECTED_RESPONSE_TIMEOUT_MS)
+#define GET_STATUS5() CtreCanNode::recMsg<TALON_Status_5_Startup_OneShot_t > rx = GetRx<TALON_Status_5_Startup_OneShot_t>(STATUS_5 | GetDeviceNumber(), EXPECTED_RESPONSE_TIMEOUT_MS)
+#define GET_STATUS6() CtreCanNode::recMsg<TALON_Status_6_Eol_t > rx = GetRx<TALON_Status_6_Eol_t>(STATUS_6 | GetDeviceNumber(), EXPECTED_RESPONSE_TIMEOUT_MS)
+#define GET_STATUS7() CtreCanNode::recMsg<TALON_Status_7_Debug_200ms_t > rx = GetRx<TALON_Status_7_Debug_200ms_t>(STATUS_7 | GetDeviceNumber(), EXPECTED_RESPONSE_TIMEOUT_MS)
+
+#define PARAM_REQUEST 0x02041800
+#define PARAM_RESPONSE 0x02041840
+#define PARAM_SET 0x02041880
+
+const int kParamArbIdValue = PARAM_RESPONSE;
+const int kParamArbIdMask = 0xFFFFFFFF;
+
+const double FLOAT_TO_FXP = (double)0x400000;
+const double FXP_TO_FLOAT = 0.0000002384185791015625;
+
+/* encoder/decoders */
+/** control */
+typedef struct _TALON_Control_1_General_10ms_t {
+ unsigned TokenH:8;
+ unsigned TokenL:8;
+ unsigned DemandH:8;
+ unsigned DemandM:8;
+ unsigned DemandL:8;
+ unsigned ProfileSlotSelect:1;
+ unsigned FeedbackDeviceSelect:4;
+ unsigned OverrideLimitSwitchEn:3;
+ unsigned RevFeedbackSensor:1;
+ unsigned RevMotDuringCloseLoopEn:1;
+ unsigned OverrideBrakeType:2;
+ unsigned ModeSelect:4;
+ unsigned RampThrottle:8;
+} TALON_Control_1_General_10ms_t ;
+typedef struct _TALON_Control_2_Rates_OneShot_t {
+ unsigned Status1Ms:8;
+ unsigned Status2Ms:8;
+ unsigned Status3Ms:8;
+ unsigned Status4Ms:8;
+} TALON_Control_2_Rates_OneShot_t ;
+typedef struct _TALON_Control_3_ClearFlags_OneShot_t {
+ unsigned ZeroFeedbackSensor:1;
+ unsigned ClearStickyFaults:1;
+} TALON_Control_3_ClearFlags_OneShot_t ;
+
+/** status */
+typedef struct _TALON_Status_1_General_10ms_t {
+ unsigned CloseLoopErrH:8;
+ unsigned CloseLoopErrM:8;
+ unsigned CloseLoopErrL:8;
+ unsigned AppliedThrottle_h3:3;
+ unsigned Fault_RevSoftLim:1;
+ unsigned Fault_ForSoftLim:1;
+ unsigned TokLocked:1;
+ unsigned LimitSwitchClosedRev:1;
+ unsigned LimitSwitchClosedFor:1;
+ unsigned AppliedThrottle_l8:8;
+ unsigned ModeSelect_h1:1;
+ unsigned FeedbackDeviceSelect:4;
+ unsigned LimitSwitchEn:3;
+ unsigned Fault_HardwareFailure:1;
+ unsigned Fault_RevLim:1;
+ unsigned Fault_ForLim:1;
+ unsigned Fault_UnderVoltage:1;
+ unsigned Fault_OverTemp:1;
+ unsigned ModeSelect_b3:3;
+ unsigned TokenSeed:8;
+} TALON_Status_1_General_10ms_t ;
+typedef struct _TALON_Status_2_Feedback_20ms_t {
+ unsigned SensorPositionH:8;
+ unsigned SensorPositionM:8;
+ unsigned SensorPositionL:8;
+ unsigned SensorVelocityH:8;
+ unsigned SensorVelocityL:8;
+ unsigned Current_h8:8;
+ unsigned StckyFault_RevSoftLim:1;
+ unsigned StckyFault_ForSoftLim:1;
+ unsigned StckyFault_RevLim:1;
+ unsigned StckyFault_ForLim:1;
+ unsigned StckyFault_UnderVoltage:1;
+ unsigned StckyFault_OverTemp:1;
+ unsigned Current_l2:2;
+ unsigned reserved:6;
+ unsigned ProfileSlotSelect:1;
+ unsigned BrakeIsEnabled:1;
+} TALON_Status_2_Feedback_20ms_t ;
+typedef struct _TALON_Status_3_Enc_100ms_t {
+ unsigned EncPositionH:8;
+ unsigned EncPositionM:8;
+ unsigned EncPositionL:8;
+ unsigned EncVelH:8;
+ unsigned EncVelL:8;
+ unsigned EncIndexRiseEventsH:8;
+ unsigned EncIndexRiseEventsL:8;
+ unsigned reserved:5;
+ unsigned QuadIdxpin:1;
+ unsigned QuadBpin:1;
+ unsigned QuadApin:1;
+} TALON_Status_3_Enc_100ms_t ;
+typedef struct _TALON_Status_4_AinTempVbat_100ms_t {
+ unsigned AnalogInWithOvH:8;
+ unsigned AnalogInWithOvM:8;
+ unsigned AnalogInWithOvL:8;
+ unsigned AnalogInVelH:8;
+ unsigned AnalogInVelL:8;
+ unsigned Temp:8;
+ unsigned BatteryV:8;
+ unsigned reserved:8;
+} TALON_Status_4_AinTempVbat_100ms_t ;
+typedef struct _TALON_Status_5_Startup_OneShot_t {
+ unsigned ResetCountH:8;
+ unsigned ResetCountL:8;
+ unsigned ResetFlagsH:8;
+ unsigned ResetFlagsL:8;
+ unsigned FirmVersH:8;
+ unsigned FirmVersL:8;
+} TALON_Status_5_Startup_OneShot_t ;
+typedef struct _TALON_Status_6_Eol_t {
+ unsigned currentAdcUncal_h2:2;
+ unsigned reserved1:5;
+ unsigned SpiCsPin_GadgeteerPin6:1;
+ unsigned currentAdcUncal_l8:8;
+ unsigned tempAdcUncal_h2:2;
+ unsigned reserved2:6;
+ unsigned tempAdcUncal_l8:8;
+ unsigned vbatAdcUncal_h2:2;
+ unsigned reserved3:6;
+ unsigned vbatAdcUncal_l8:8;
+ unsigned analogAdcUncal_h2:2;
+ unsigned reserved4:6;
+ unsigned analogAdcUncal_l8:8;
+} TALON_Status_6_Eol_t ;
+typedef struct _TALON_Status_7_Debug_200ms_t {
+ unsigned TokenizationFails_h8:8;
+ unsigned TokenizationFails_l8:8;
+ unsigned LastFailedToken_h8:8;
+ unsigned LastFailedToken_l8:8;
+ unsigned TokenizationSucceses_h8:8;
+ unsigned TokenizationSucceses_l8:8;
+} TALON_Status_7_Debug_200ms_t ;
+typedef struct _TALON_Param_Request_t {
+ unsigned ParamEnum:8;
+} TALON_Param_Request_t ;
+typedef struct _TALON_Param_Response_t {
+ unsigned ParamEnum:8;
+ unsigned ParamValueL:8;
+ unsigned ParamValueML:8;
+ unsigned ParamValueMH:8;
+ unsigned ParamValueH:8;
+} TALON_Param_Response_t ;
+
+
+CanTalonSRX::CanTalonSRX(int deviceNumber,int controlPeriodMs): CtreCanNode(deviceNumber), _can_h(0), _can_stat(0)
+{
+ /* bound period to be within [1 ms,95 ms] */
+ if(controlPeriodMs < 1)
+ controlPeriodMs = 1;
+ else if(controlPeriodMs > 95)
+ controlPeriodMs = 95;
+ RegisterRx(STATUS_1 | (UINT8)deviceNumber );
+ RegisterRx(STATUS_2 | (UINT8)deviceNumber );
+ RegisterRx(STATUS_3 | (UINT8)deviceNumber );
+ RegisterRx(STATUS_4 | (UINT8)deviceNumber );
+ RegisterRx(STATUS_5 | (UINT8)deviceNumber );
+ RegisterRx(STATUS_6 | (UINT8)deviceNumber );
+ RegisterRx(STATUS_7 | (UINT8)deviceNumber );
+ RegisterTx(CONTROL_1 | (UINT8)deviceNumber, (UINT8)controlPeriodMs);
+ /* default our frame rate table to what firmware defaults to. */
+ _statusRateMs[0] = 10; /* TALON_Status_1_General_10ms_t */
+ _statusRateMs[1] = 20; /* TALON_Status_2_Feedback_20ms_t */
+ _statusRateMs[2] = 100; /* TALON_Status_3_Enc_100ms_t */
+ _statusRateMs[3] = 100; /* TALON_Status_4_AinTempVbat_100ms_t */
+ /* the only default param that is nonzero is limit switch.
+ * Default to using the flash settings. */
+ SetOverrideLimitSwitchEn(kLimitSwitchOverride_UseDefaultsFromFlash);
+}
+/* CanTalonSRX D'tor
+ */
+CanTalonSRX::~CanTalonSRX()
+{
+ if(_can_h){
+ FRC_NetworkCommunication_CANSessionMux_closeStreamSession(_can_h);
+ _can_h = 0;
+ }
+}
+void CanTalonSRX::OpenSessionIfNeedBe()
+{
+ _can_stat = 0;
+ if (_can_h == 0) {
+ /* bit30 - bit8 must match $000002XX. Top bit is not masked to get remote frames */
+ FRC_NetworkCommunication_CANSessionMux_openStreamSession(&_can_h,kParamArbIdValue | GetDeviceNumber(), kParamArbIdMask, kMsgCapacity, &_can_stat);
+ if (_can_stat == 0) {
+ /* success */
+ } else {
+ /* something went wrong, try again later */
+ _can_h = 0;
+ }
+ }
+}
+void CanTalonSRX::ProcessStreamMessages()
+{
+ if(0 == _can_h)
+ OpenSessionIfNeedBe();
+ /* process receive messages */
+ uint32_t i;
+ uint32_t messagesToRead = sizeof(_msgBuff) / sizeof(_msgBuff[0]);
+ uint32_t messagesRead = 0;
+ /* read out latest bunch of messages */
+ _can_stat = 0;
+ if (_can_h){
+ FRC_NetworkCommunication_CANSessionMux_readStreamSession(_can_h,_msgBuff, messagesToRead, &messagesRead, &_can_stat);
+ }
+ /* loop thru each message of interest */
+ for (i = 0; i < messagesRead; ++i) {
+ tCANStreamMessage * msg = _msgBuff + i;
+ if(msg->messageID == (PARAM_RESPONSE | GetDeviceNumber()) ){
+ TALON_Param_Response_t * paramResp = (TALON_Param_Response_t*)msg->data;
+ /* decode value */
+ int32_t val = paramResp->ParamValueH;
+ val <<= 8;
+ val |= paramResp->ParamValueMH;
+ val <<= 8;
+ val |= paramResp->ParamValueML;
+ val <<= 8;
+ val |= paramResp->ParamValueL;
+ /* save latest signal */
+ _sigs[paramResp->ParamEnum] = val;
+ }else{
+ int brkpthere = 42;
+ ++brkpthere;
+ }
+ }
+}
+void CanTalonSRX::Set(double value)
+{
+ if(value > 1)
+ value = 1;
+ else if(value < -1)
+ value = -1;
+ SetDemand(1023*value); /* must be within [-1023,1023] */
+}
+/*---------------------setters and getters that use the param request/response-------------*/
+/**
+ * Send a one shot frame to set an arbitrary signal.
+ * Most signals are in the control frame so avoid using this API unless you have to.
+ * Use this api for...
+ * -A motor controller profile signal eProfileParam_XXXs. These are backed up in flash. If you are gain-scheduling then call this periodically.
+ * -Default brake and limit switch signals... eOnBoot_XXXs. Avoid doing this, use the override signals in the control frame.
+ * Talon will automatically send a PARAM_RESPONSE after the set, so GetParamResponse will catch the latest value after a couple ms.
+ */
+CTR_Code CanTalonSRX::SetParamRaw(unsigned paramEnum, int rawBits)
+{
+ /* caller is using param API. Open session if it hasn'T been done. */
+ if(0 == _can_h)
+ OpenSessionIfNeedBe();
+ TALON_Param_Response_t frame;
+ memset(&frame,0,sizeof(frame));
+ frame.ParamEnum = paramEnum;
+ frame.ParamValueH = rawBits >> 0x18;
+ frame.ParamValueMH = rawBits >> 0x10;
+ frame.ParamValueML = rawBits >> 0x08;
+ frame.ParamValueL = rawBits;
+ int32_t status = 0;
+ FRC_NetworkCommunication_CANSessionMux_sendMessage(PARAM_SET | GetDeviceNumber(), (const uint8_t*)&frame, 5, 0, &status);
+ if(status)
+ return CTR_TxFailed;
+ return CTR_OKAY;
+}
+/**
+ * Checks cached CAN frames and updating solicited signals.
+ */
+CTR_Code CanTalonSRX::GetParamResponseRaw(unsigned paramEnum, int & rawBits)
+{
+ CTR_Code retval = CTR_OKAY;
+ /* process received param events. We don't expect many since this API is not used often. */
+ ProcessStreamMessages();
+ /* grab the solicited signal value */
+ sigs_t::iterator i = _sigs.find(paramEnum);
+ if(i == _sigs.end()){
+ retval = CTR_SigNotUpdated;
+ }else{
+ rawBits = i->second;
+ }
+ return retval;
+}
+/**
+ * Asks TALON to immedietely respond with signal value. This API is only used for signals that are not sent periodically.
+ * This can be useful for reading params that rarely change like Limit Switch settings and PIDF values.
+ * @param param to request.
+ */
+CTR_Code CanTalonSRX::RequestParam(param_t paramEnum)
+{
+ /* process received param events. We don't expect many since this API is not used often. */
+ ProcessStreamMessages();
+ TALON_Param_Request_t frame;
+ memset(&frame,0,sizeof(frame));
+ frame.ParamEnum = paramEnum;
+ int32_t status = 0;
+ FRC_NetworkCommunication_CANSessionMux_sendMessage(PARAM_REQUEST | GetDeviceNumber(), (const uint8_t*)&frame, 1, 0, &status);
+ if(status)
+ return CTR_TxFailed;
+ return CTR_OKAY;
+}
+
+CTR_Code CanTalonSRX::SetParam(param_t paramEnum, double value)
+{
+ int32_t rawbits = 0;
+ switch(paramEnum){
+ case eProfileParamSlot0_P:/* unsigned 10.22 fixed pt value */
+ case eProfileParamSlot0_I:
+ case eProfileParamSlot0_D:
+ case eProfileParamSlot1_P:
+ case eProfileParamSlot1_I:
+ case eProfileParamSlot1_D:
+ {
+ uint32_t urawbits;
+ value = std::min(value,1023.0); /* bounds check doubles that are outside u10.22 */
+ value = std::max(value,0.0);
+ urawbits = value * FLOAT_TO_FXP; /* perform unsign arithmetic */
+ rawbits = urawbits; /* copy bits over. SetParamRaw just stuffs into CAN frame with no sense of signedness */
+ } break;
+ case eProfileParamSlot1_F: /* signed 10.22 fixed pt value */
+ case eProfileParamSlot0_F:
+ value = std::min(value, 512.0); /* bounds check doubles that are outside s10.22 */
+ value = std::max(value,-512.0);
+ rawbits = value * FLOAT_TO_FXP;
+ break;
+ default: /* everything else is integral */
+ rawbits = (int32_t)value;
+ break;
+ }
+ return SetParamRaw(paramEnum,rawbits);
+}
+CTR_Code CanTalonSRX::GetParamResponse(param_t paramEnum, double & value)
+{
+ int32_t rawbits = 0;
+ CTR_Code retval = GetParamResponseRaw(paramEnum,rawbits);
+ switch(paramEnum){
+ case eProfileParamSlot0_P:/* 10.22 fixed pt value */
+ case eProfileParamSlot0_I:
+ case eProfileParamSlot0_D:
+ case eProfileParamSlot0_F:
+ case eProfileParamSlot1_P:
+ case eProfileParamSlot1_I:
+ case eProfileParamSlot1_D:
+ case eProfileParamSlot1_F:
+ case eCurrent:
+ case eTemp:
+ case eBatteryV:
+ value = ((double)rawbits) * FXP_TO_FLOAT;
+ break;
+ default: /* everything else is integral */
+ value = (double)rawbits;
+ break;
+ }
+ return retval;
+}
+CTR_Code CanTalonSRX::GetParamResponseInt32(param_t paramEnum, int & value)
+{
+ double dvalue = 0;
+ CTR_Code retval = GetParamResponse(paramEnum, dvalue);
+ value = (int32_t)dvalue;
+ return retval;
+}
+/*----- getters and setters that use param request/response. These signals are backed up in flash and will survive a power cycle. ---------*/
+/*----- If your application requires changing these values consider using both slots and switch between slot0 <=> slot1. ------------------*/
+/*----- If your application requires changing these signals frequently then it makes sense to leverage this API. --------------------------*/
+/*----- Getters don't block, so it may require several calls to get the latest value. --------------------------*/
+CTR_Code CanTalonSRX::SetPgain(unsigned slotIdx,double gain)
+{
+ if(slotIdx == 0)
+ return SetParam(eProfileParamSlot0_P, gain);
+ return SetParam(eProfileParamSlot1_P, gain);
+}
+CTR_Code CanTalonSRX::SetIgain(unsigned slotIdx,double gain)
+{
+ if(slotIdx == 0)
+ return SetParam(eProfileParamSlot0_I, gain);
+ return SetParam(eProfileParamSlot1_I, gain);
+}
+CTR_Code CanTalonSRX::SetDgain(unsigned slotIdx,double gain)
+{
+ if(slotIdx == 0)
+ return SetParam(eProfileParamSlot0_D, gain);
+ return SetParam(eProfileParamSlot1_D, gain);
+}
+CTR_Code CanTalonSRX::SetFgain(unsigned slotIdx,double gain)
+{
+ if(slotIdx == 0)
+ return SetParam(eProfileParamSlot0_F, gain);
+ return SetParam(eProfileParamSlot1_F, gain);
+}
+CTR_Code CanTalonSRX::SetIzone(unsigned slotIdx,int zone)
+{
+ if(slotIdx == 0)
+ return SetParam(eProfileParamSlot0_IZone, zone);
+ return SetParam(eProfileParamSlot1_IZone, zone);
+}
+CTR_Code CanTalonSRX::SetCloseLoopRampRate(unsigned slotIdx,int closeLoopRampRate)
+{
+ if(slotIdx == 0)
+ return SetParam(eProfileParamSlot0_CloseLoopRampRate, closeLoopRampRate);
+ return SetParam(eProfileParamSlot1_CloseLoopRampRate, closeLoopRampRate);
+}
+CTR_Code CanTalonSRX::GetPgain(unsigned slotIdx,double & gain)
+{
+ if(slotIdx == 0)
+ return GetParamResponse(eProfileParamSlot0_P, gain);
+ return GetParamResponse(eProfileParamSlot1_P, gain);
+}
+CTR_Code CanTalonSRX::GetIgain(unsigned slotIdx,double & gain)
+{
+ if(slotIdx == 0)
+ return GetParamResponse(eProfileParamSlot0_I, gain);
+ return GetParamResponse(eProfileParamSlot1_I, gain);
+}
+CTR_Code CanTalonSRX::GetDgain(unsigned slotIdx,double & gain)
+{
+ if(slotIdx == 0)
+ return GetParamResponse(eProfileParamSlot0_D, gain);
+ return GetParamResponse(eProfileParamSlot1_D, gain);
+}
+CTR_Code CanTalonSRX::GetFgain(unsigned slotIdx,double & gain)
+{
+ if(slotIdx == 0)
+ return GetParamResponse(eProfileParamSlot0_F, gain);
+ return GetParamResponse(eProfileParamSlot1_F, gain);
+}
+CTR_Code CanTalonSRX::GetIzone(unsigned slotIdx,int & zone)
+{
+ if(slotIdx == 0)
+ return GetParamResponseInt32(eProfileParamSlot0_IZone, zone);
+ return GetParamResponseInt32(eProfileParamSlot1_IZone, zone);
+}
+CTR_Code CanTalonSRX::GetCloseLoopRampRate(unsigned slotIdx,int & closeLoopRampRate)
+{
+ if(slotIdx == 0)
+ return GetParamResponseInt32(eProfileParamSlot0_CloseLoopRampRate, closeLoopRampRate);
+ return GetParamResponseInt32(eProfileParamSlot1_CloseLoopRampRate, closeLoopRampRate);
+}
+
+CTR_Code CanTalonSRX::SetSensorPosition(int pos)
+{
+ return SetParam(eSensorPosition, pos);
+}
+CTR_Code CanTalonSRX::SetForwardSoftLimit(int forwardLimit)
+{
+ return SetParam(eProfileParamSoftLimitForThreshold, forwardLimit);
+}
+CTR_Code CanTalonSRX::SetReverseSoftLimit(int reverseLimit)
+{
+ return SetParam(eProfileParamSoftLimitRevThreshold, reverseLimit);
+}
+CTR_Code CanTalonSRX::SetForwardSoftEnable(int enable)
+{
+ return SetParam(eProfileParamSoftLimitForEnable, enable);
+}
+CTR_Code CanTalonSRX::SetReverseSoftEnable(int enable)
+{
+ return SetParam(eProfileParamSoftLimitRevEnable, enable);
+}
+CTR_Code CanTalonSRX::GetForwardSoftLimit(int & forwardLimit)
+{
+ return GetParamResponseInt32(eProfileParamSoftLimitForThreshold, forwardLimit);
+}
+CTR_Code CanTalonSRX::GetReverseSoftLimit(int & reverseLimit)
+{
+ return GetParamResponseInt32(eProfileParamSoftLimitRevThreshold, reverseLimit);
+}
+CTR_Code CanTalonSRX::GetForwardSoftEnable(int & enable)
+{
+ return GetParamResponseInt32(eProfileParamSoftLimitForEnable, enable);
+}
+CTR_Code CanTalonSRX::GetReverseSoftEnable(int & enable)
+{
+ return GetParamResponseInt32(eProfileParamSoftLimitRevEnable, enable);
+}
+/**
+ * Change the periodMs of a TALON's status frame. See kStatusFrame_* enums for what's available.
+ */
+CTR_Code CanTalonSRX::SetStatusFrameRate(unsigned frameEnum, unsigned periodMs)
+{
+ int32_t status = 0;
+ /* bounds check the period */
+ if(periodMs < 1)
+ periodMs = 1;
+ else if (periodMs > 255)
+ periodMs = 255;
+ uint8_t period = (uint8_t)periodMs;
+ /* tweak just the status messsage rate the caller cares about */
+ switch(frameEnum){
+ case kStatusFrame_General:
+ _statusRateMs[0] = period;
+ break;
+ case kStatusFrame_Feedback:
+ _statusRateMs[1] = period;
+ break;
+ case kStatusFrame_Encoder:
+ _statusRateMs[2] = period;
+ break;
+ case kStatusFrame_AnalogTempVbat:
+ _statusRateMs[3] = period;
+ break;
+ }
+ /* build our request frame */
+ TALON_Control_2_Rates_OneShot_t frame;
+ memset(&frame,0,sizeof(frame));
+ frame.Status1Ms = _statusRateMs[0];
+ frame.Status2Ms = _statusRateMs[1];
+ frame.Status3Ms = _statusRateMs[2];
+ frame.Status4Ms = _statusRateMs[3];
+ FRC_NetworkCommunication_CANSessionMux_sendMessage(CONTROL_2 | GetDeviceNumber(), (const uint8_t*)&frame, sizeof(frame), 0, &status);
+ if(status)
+ return CTR_TxFailed;
+ return CTR_OKAY;
+}
+/**
+ * Clear all sticky faults in TALON.
+ */
+CTR_Code CanTalonSRX::ClearStickyFaults()
+{
+ int32_t status = 0;
+ /* build request frame */
+ TALON_Control_3_ClearFlags_OneShot_t frame;
+ memset(&frame,0,sizeof(frame));
+ frame.ClearStickyFaults = 1;
+ FRC_NetworkCommunication_CANSessionMux_sendMessage(CONTROL_3 | GetDeviceNumber(), (const uint8_t*)&frame, sizeof(frame), 0, &status);
+ if(status)
+ return CTR_TxFailed;
+ return CTR_OKAY;
+}
+/*------------------------ auto generated. This API is optimal since it uses the fire-and-forget CAN interface ----------------------*/
+/*------------------------ These signals should cover the majority of all use cases. ----------------------------------*/
+CTR_Code CanTalonSRX::GetFault_OverTemp(int ¶m)
+{
+ GET_STATUS1();
+ param = rx->Fault_OverTemp;
+ return rx.err;
+}
+CTR_Code CanTalonSRX::GetFault_UnderVoltage(int ¶m)
+{
+ GET_STATUS1();
+ param = rx->Fault_UnderVoltage;
+ return rx.err;
+}
+CTR_Code CanTalonSRX::GetFault_ForLim(int ¶m)
+{
+ GET_STATUS1();
+ param = rx->Fault_ForLim;
+ return rx.err;
+}
+CTR_Code CanTalonSRX::GetFault_RevLim(int ¶m)
+{
+ GET_STATUS1();
+ param = rx->Fault_RevLim;
+ return rx.err;
+}
+CTR_Code CanTalonSRX::GetFault_HardwareFailure(int ¶m)
+{
+ GET_STATUS1();
+ param = rx->Fault_HardwareFailure;
+ return rx.err;
+}
+CTR_Code CanTalonSRX::GetFault_ForSoftLim(int ¶m)
+{
+ GET_STATUS1();
+ param = rx->Fault_ForSoftLim;
+ return rx.err;
+}
+CTR_Code CanTalonSRX::GetFault_RevSoftLim(int ¶m)
+{
+ GET_STATUS1();
+ param = rx->Fault_RevSoftLim;
+ return rx.err;
+}
+CTR_Code CanTalonSRX::GetStckyFault_OverTemp(int ¶m)
+{
+ GET_STATUS2();
+ param = rx->StckyFault_OverTemp;
+ return rx.err;
+}
+CTR_Code CanTalonSRX::GetStckyFault_UnderVoltage(int ¶m)
+{
+ GET_STATUS2();
+ param = rx->StckyFault_UnderVoltage;
+ return rx.err;
+}
+CTR_Code CanTalonSRX::GetStckyFault_ForLim(int ¶m)
+{
+ GET_STATUS2();
+ param = rx->StckyFault_ForLim;
+ return rx.err;
+}
+CTR_Code CanTalonSRX::GetStckyFault_RevLim(int ¶m)
+{
+ GET_STATUS2();
+ param = rx->StckyFault_RevLim;
+ return rx.err;
+}
+CTR_Code CanTalonSRX::GetStckyFault_ForSoftLim(int ¶m)
+{
+ GET_STATUS2();
+ param = rx->StckyFault_ForSoftLim;
+ return rx.err;
+}
+CTR_Code CanTalonSRX::GetStckyFault_RevSoftLim(int ¶m)
+{
+ GET_STATUS2();
+ param = rx->StckyFault_RevSoftLim;
+ return rx.err;
+}
+CTR_Code CanTalonSRX::GetAppliedThrottle(int ¶m)
+{
+ GET_STATUS1();
+ int32_t raw = 0;
+ raw |= rx->AppliedThrottle_h3;
+ raw <<= 8;
+ raw |= rx->AppliedThrottle_l8;
+ raw <<= (32-11); /* sign extend */
+ raw >>= (32-11); /* sign extend */
+ param = (int)raw;
+ return rx.err;
+}
+CTR_Code CanTalonSRX::GetCloseLoopErr(int ¶m)
+{
+ GET_STATUS1();
+ int32_t raw = 0;
+ raw |= rx->CloseLoopErrH;
+ raw <<= 8;
+ raw |= rx->CloseLoopErrM;
+ raw <<= 8;
+ raw |= rx->CloseLoopErrL;
+ raw <<= (32-24); /* sign extend */
+ raw >>= (32-24); /* sign extend */
+ param = (int)raw;
+ return rx.err;
+}
+CTR_Code CanTalonSRX::GetFeedbackDeviceSelect(int ¶m)
+{
+ GET_STATUS1();
+ param = rx->FeedbackDeviceSelect;
+ return rx.err;
+}
+CTR_Code CanTalonSRX::GetModeSelect(int ¶m)
+{
+ GET_STATUS1();
+ uint32_t raw = 0;
+ raw |= rx->ModeSelect_h1;
+ raw <<= 3;
+ raw |= rx->ModeSelect_b3;
+ param = (int)raw;
+ return rx.err;
+}
+CTR_Code CanTalonSRX::GetLimitSwitchEn(int ¶m)
+{
+ GET_STATUS1();
+ param = rx->LimitSwitchEn;
+ return rx.err;
+}
+CTR_Code CanTalonSRX::GetLimitSwitchClosedFor(int ¶m)
+{
+ GET_STATUS1();
+ param = rx->LimitSwitchClosedFor;
+ return rx.err;
+}
+CTR_Code CanTalonSRX::GetLimitSwitchClosedRev(int ¶m)
+{
+ GET_STATUS1();
+ param = rx->LimitSwitchClosedRev;
+ return rx.err;
+}
+CTR_Code CanTalonSRX::GetSensorPosition(int ¶m)
+{
+ GET_STATUS2();
+ int32_t raw = 0;
+ raw |= rx->SensorPositionH;
+ raw <<= 8;
+ raw |= rx->SensorPositionM;
+ raw <<= 8;
+ raw |= rx->SensorPositionL;
+ raw <<= (32-24); /* sign extend */
+ raw >>= (32-24); /* sign extend */
+ param = (int)raw;
+ return rx.err;
+}
+CTR_Code CanTalonSRX::GetSensorVelocity(int ¶m)
+{
+ GET_STATUS2();
+ int32_t raw = 0;
+ raw |= rx->SensorVelocityH;
+ raw <<= 8;
+ raw |= rx->SensorVelocityL;
+ raw <<= (32-16); /* sign extend */
+ raw >>= (32-16); /* sign extend */
+ param = (int)raw;
+ return rx.err;
+}
+CTR_Code CanTalonSRX::GetCurrent(double ¶m)
+{
+ GET_STATUS2();
+ uint32_t raw = 0;
+ raw |= rx->Current_h8;
+ raw <<= 2;
+ raw |= rx->Current_l2;
+ param = (double)raw * 0.125 + 0;
+ return rx.err;
+}
+CTR_Code CanTalonSRX::GetBrakeIsEnabled(int ¶m)
+{
+ GET_STATUS2();
+ param = rx->BrakeIsEnabled;
+ return rx.err;
+}
+CTR_Code CanTalonSRX::GetEncPosition(int ¶m)
+{
+ GET_STATUS3();
+ int32_t raw = 0;
+ raw |= rx->EncPositionH;
+ raw <<= 8;
+ raw |= rx->EncPositionM;
+ raw <<= 8;
+ raw |= rx->EncPositionL;
+ raw <<= (32-24); /* sign extend */
+ raw >>= (32-24); /* sign extend */
+ param = (int)raw;
+ return rx.err;
+}
+CTR_Code CanTalonSRX::GetEncVel(int ¶m)
+{
+ GET_STATUS3();
+ int32_t raw = 0;
+ raw |= rx->EncVelH;
+ raw <<= 8;
+ raw |= rx->EncVelL;
+ raw <<= (32-16); /* sign extend */
+ raw >>= (32-16); /* sign extend */
+ param = (int)raw;
+ return rx.err;
+}
+CTR_Code CanTalonSRX::GetEncIndexRiseEvents(int ¶m)
+{
+ GET_STATUS3();
+ uint32_t raw = 0;
+ raw |= rx->EncIndexRiseEventsH;
+ raw <<= 8;
+ raw |= rx->EncIndexRiseEventsL;
+ param = (int)raw;
+ return rx.err;
+}
+CTR_Code CanTalonSRX::GetQuadApin(int ¶m)
+{
+ GET_STATUS3();
+ param = rx->QuadApin;
+ return rx.err;
+}
+CTR_Code CanTalonSRX::GetQuadBpin(int ¶m)
+{
+ GET_STATUS3();
+ param = rx->QuadBpin;
+ return rx.err;
+}
+CTR_Code CanTalonSRX::GetQuadIdxpin(int ¶m)
+{
+ GET_STATUS3();
+ param = rx->QuadIdxpin;
+ return rx.err;
+}
+CTR_Code CanTalonSRX::GetAnalogInWithOv(int ¶m)
+{
+ GET_STATUS4();
+ int32_t raw = 0;
+ raw |= rx->AnalogInWithOvH;
+ raw <<= 8;
+ raw |= rx->AnalogInWithOvM;
+ raw <<= 8;
+ raw |= rx->AnalogInWithOvL;
+ raw <<= (32-24); /* sign extend */
+ raw >>= (32-24); /* sign extend */
+ param = (int)raw;
+ return rx.err;
+}
+CTR_Code CanTalonSRX::GetAnalogInVel(int ¶m)
+{
+ GET_STATUS4();
+ int32_t raw = 0;
+ raw |= rx->AnalogInVelH;
+ raw <<= 8;
+ raw |= rx->AnalogInVelL;
+ raw <<= (32-16); /* sign extend */
+ raw >>= (32-16); /* sign extend */
+ param = (int)raw;
+ return rx.err;
+}
+CTR_Code CanTalonSRX::GetTemp(double ¶m)
+{
+ GET_STATUS4();
+ uint32_t raw = rx->Temp;
+ param = (double)raw * 0.6451612903 + -50;
+ return rx.err;
+}
+CTR_Code CanTalonSRX::GetBatteryV(double ¶m)
+{
+ GET_STATUS4();
+ uint32_t raw = rx->BatteryV;
+ param = (double)raw * 0.05 + 4;
+ return rx.err;
+}
+CTR_Code CanTalonSRX::GetResetCount(int ¶m)
+{
+ GET_STATUS5();
+ uint32_t raw = 0;
+ raw |= rx->ResetCountH;
+ raw <<= 8;
+ raw |= rx->ResetCountL;
+ param = (int)raw;
+ return rx.err;
+}
+CTR_Code CanTalonSRX::GetResetFlags(int ¶m)
+{
+ GET_STATUS5();
+ uint32_t raw = 0;
+ raw |= rx->ResetFlagsH;
+ raw <<= 8;
+ raw |= rx->ResetFlagsL;
+ param = (int)raw;
+ return rx.err;
+}
+CTR_Code CanTalonSRX::GetFirmVers(int ¶m)
+{
+ GET_STATUS5();
+ uint32_t raw = 0;
+ raw |= rx->FirmVersH;
+ raw <<= 8;
+ raw |= rx->FirmVersL;
+ param = (int)raw;
+ return rx.err;
+}
+CTR_Code CanTalonSRX::SetDemand(int param)
+{
+ CtreCanNode::txTask<TALON_Control_1_General_10ms_t> toFill = GetTx<TALON_Control_1_General_10ms_t>(CONTROL_1 | GetDeviceNumber());
+ if (toFill.IsEmpty()) return CTR_UnexpectedArbId;
+ toFill->DemandH = param>>16;
+ toFill->DemandM = param>>8;
+ toFill->DemandL = param>>0;
+ FlushTx(toFill);
+ return CTR_OKAY;
+}
+CTR_Code CanTalonSRX::SetOverrideLimitSwitchEn(int param)
+{
+ CtreCanNode::txTask<TALON_Control_1_General_10ms_t> toFill = GetTx<TALON_Control_1_General_10ms_t>(CONTROL_1 | GetDeviceNumber());
+ if (toFill.IsEmpty()) return CTR_UnexpectedArbId;
+ toFill->OverrideLimitSwitchEn = param;
+ FlushTx(toFill);
+ return CTR_OKAY;
+}
+CTR_Code CanTalonSRX::SetFeedbackDeviceSelect(int param)
+{
+ CtreCanNode::txTask<TALON_Control_1_General_10ms_t> toFill = GetTx<TALON_Control_1_General_10ms_t>(CONTROL_1 | GetDeviceNumber());
+ if (toFill.IsEmpty()) return CTR_UnexpectedArbId;
+ toFill->FeedbackDeviceSelect = param;
+ FlushTx(toFill);
+ return CTR_OKAY;
+}
+CTR_Code CanTalonSRX::SetRevMotDuringCloseLoopEn(int param)
+{
+ CtreCanNode::txTask<TALON_Control_1_General_10ms_t> toFill = GetTx<TALON_Control_1_General_10ms_t>(CONTROL_1 | GetDeviceNumber());
+ if (toFill.IsEmpty()) return CTR_UnexpectedArbId;
+ toFill->RevMotDuringCloseLoopEn = param;
+ FlushTx(toFill);
+ return CTR_OKAY;
+}
+CTR_Code CanTalonSRX::SetOverrideBrakeType(int param)
+{
+ CtreCanNode::txTask<TALON_Control_1_General_10ms_t> toFill = GetTx<TALON_Control_1_General_10ms_t>(CONTROL_1 | GetDeviceNumber());
+ if (toFill.IsEmpty()) return CTR_UnexpectedArbId;
+ toFill->OverrideBrakeType = param;
+ FlushTx(toFill);
+ return CTR_OKAY;
+}
+CTR_Code CanTalonSRX::SetModeSelect(int param)
+{
+ CtreCanNode::txTask<TALON_Control_1_General_10ms_t> toFill = GetTx<TALON_Control_1_General_10ms_t>(CONTROL_1 | GetDeviceNumber());
+ if (toFill.IsEmpty()) return CTR_UnexpectedArbId;
+ toFill->ModeSelect = param;
+ FlushTx(toFill);
+ return CTR_OKAY;
+}
+/**
+ * @param modeSelect selects which mode.
+ * @param demand setpt or throttle or masterId to follow.
+ * @return error code, 0 iff successful.
+ * This function has the advantage of atomically setting mode and demand.
+ */
+CTR_Code CanTalonSRX::SetModeSelect(int modeSelect,int demand)
+{
+ CtreCanNode::txTask<TALON_Control_1_General_10ms_t> toFill = GetTx<TALON_Control_1_General_10ms_t>(CONTROL_1 | GetDeviceNumber());
+ if (toFill.IsEmpty()) return CTR_UnexpectedArbId;
+ toFill->ModeSelect = modeSelect;
+ toFill->DemandH = demand>>16;
+ toFill->DemandM = demand>>8;
+ toFill->DemandL = demand>>0;
+ FlushTx(toFill);
+ return CTR_OKAY;
+}
+CTR_Code CanTalonSRX::SetProfileSlotSelect(int param)
+{
+ CtreCanNode::txTask<TALON_Control_1_General_10ms_t> toFill = GetTx<TALON_Control_1_General_10ms_t>(CONTROL_1 | GetDeviceNumber());
+ if (toFill.IsEmpty()) return CTR_UnexpectedArbId;
+ toFill->ProfileSlotSelect = param;
+ FlushTx(toFill);
+ return CTR_OKAY;
+}
+CTR_Code CanTalonSRX::SetRampThrottle(int param)
+{
+ CtreCanNode::txTask<TALON_Control_1_General_10ms_t> toFill = GetTx<TALON_Control_1_General_10ms_t>(CONTROL_1 | GetDeviceNumber());
+ if (toFill.IsEmpty()) return CTR_UnexpectedArbId;
+ toFill->RampThrottle = param;
+ FlushTx(toFill);
+ return CTR_OKAY;
+}
+CTR_Code CanTalonSRX::SetRevFeedbackSensor(int param)
+{
+ CtreCanNode::txTask<TALON_Control_1_General_10ms_t> toFill = GetTx<TALON_Control_1_General_10ms_t>(CONTROL_1 | GetDeviceNumber());
+ if (toFill.IsEmpty()) return CTR_UnexpectedArbId;
+ toFill->RevFeedbackSensor = param ? 1 : 0;
+ FlushTx(toFill);
+ return CTR_OKAY;
+}
+//------------------ C interface --------------------------------------------//
+extern "C" {
+void *c_TalonSRX_Create(int deviceNumber, int controlPeriodMs)
+{
+ return new CanTalonSRX(deviceNumber, controlPeriodMs);
+}
+void c_TalonSRX_Destroy(void *handle)
+{
+ delete (CanTalonSRX*)handle;
+}
+CTR_Code c_TalonSRX_SetParam(void *handle, int paramEnum, double value)
+{
+ return ((CanTalonSRX*)handle)->SetParam((CanTalonSRX::param_t)paramEnum, value);
+}
+CTR_Code c_TalonSRX_RequestParam(void *handle, int paramEnum)
+{
+ return ((CanTalonSRX*)handle)->RequestParam((CanTalonSRX::param_t)paramEnum);
+}
+CTR_Code c_TalonSRX_GetParamResponse(void *handle, int paramEnum, double *value)
+{
+ return ((CanTalonSRX*)handle)->GetParamResponse((CanTalonSRX::param_t)paramEnum, *value);
+}
+CTR_Code c_TalonSRX_GetParamResponseInt32(void *handle, int paramEnum, int *value)
+{
+ return ((CanTalonSRX*)handle)->GetParamResponseInt32((CanTalonSRX::param_t)paramEnum, *value);
+}
+CTR_Code c_TalonSRX_SetStatusFrameRate(void *handle, unsigned frameEnum, unsigned periodMs)
+{
+ return ((CanTalonSRX*)handle)->SetStatusFrameRate(frameEnum, periodMs);
+}
+CTR_Code c_TalonSRX_ClearStickyFaults(void *handle)
+{
+ return ((CanTalonSRX*)handle)->ClearStickyFaults();
+}
+CTR_Code c_TalonSRX_GetFault_OverTemp(void *handle, int *param)
+{
+ return ((CanTalonSRX*)handle)->GetFault_OverTemp(*param);
+}
+CTR_Code c_TalonSRX_GetFault_UnderVoltage(void *handle, int *param)
+{
+ return ((CanTalonSRX*)handle)->GetFault_UnderVoltage(*param);
+}
+CTR_Code c_TalonSRX_GetFault_ForLim(void *handle, int *param)
+{
+ return ((CanTalonSRX*)handle)->GetFault_ForLim(*param);
+}
+CTR_Code c_TalonSRX_GetFault_RevLim(void *handle, int *param)
+{
+ return ((CanTalonSRX*)handle)->GetFault_RevLim(*param);
+}
+CTR_Code c_TalonSRX_GetFault_HardwareFailure(void *handle, int *param)
+{
+ return ((CanTalonSRX*)handle)->GetFault_HardwareFailure(*param);
+}
+CTR_Code c_TalonSRX_GetFault_ForSoftLim(void *handle, int *param)
+{
+ return ((CanTalonSRX*)handle)->GetFault_ForSoftLim(*param);
+}
+CTR_Code c_TalonSRX_GetFault_RevSoftLim(void *handle, int *param)
+{
+ return ((CanTalonSRX*)handle)->GetFault_RevSoftLim(*param);
+}
+CTR_Code c_TalonSRX_GetStckyFault_OverTemp(void *handle, int *param)
+{
+ return ((CanTalonSRX*)handle)->GetStckyFault_OverTemp(*param);
+}
+CTR_Code c_TalonSRX_GetStckyFault_UnderVoltage(void *handle, int *param)
+{
+ return ((CanTalonSRX*)handle)->GetStckyFault_UnderVoltage(*param);
+}
+CTR_Code c_TalonSRX_GetStckyFault_ForLim(void *handle, int *param)
+{
+ return ((CanTalonSRX*)handle)->GetStckyFault_ForLim(*param);
+}
+CTR_Code c_TalonSRX_GetStckyFault_RevLim(void *handle, int *param)
+{
+ return ((CanTalonSRX*)handle)->GetStckyFault_RevLim(*param);
+}
+CTR_Code c_TalonSRX_GetStckyFault_ForSoftLim(void *handle, int *param)
+{
+ return ((CanTalonSRX*)handle)->GetStckyFault_ForSoftLim(*param);
+}
+CTR_Code c_TalonSRX_GetStckyFault_RevSoftLim(void *handle, int *param)
+{
+ return ((CanTalonSRX*)handle)->GetStckyFault_RevSoftLim(*param);
+}
+CTR_Code c_TalonSRX_GetAppliedThrottle(void *handle, int *param)
+{
+ return ((CanTalonSRX*)handle)->GetAppliedThrottle(*param);
+}
+CTR_Code c_TalonSRX_GetCloseLoopErr(void *handle, int *param)
+{
+ return ((CanTalonSRX*)handle)->GetCloseLoopErr(*param);
+}
+CTR_Code c_TalonSRX_GetFeedbackDeviceSelect(void *handle, int *param)
+{
+ return ((CanTalonSRX*)handle)->GetFeedbackDeviceSelect(*param);
+}
+CTR_Code c_TalonSRX_GetModeSelect(void *handle, int *param)
+{
+ return ((CanTalonSRX*)handle)->GetModeSelect(*param);
+}
+CTR_Code c_TalonSRX_GetLimitSwitchEn(void *handle, int *param)
+{
+ return ((CanTalonSRX*)handle)->GetLimitSwitchEn(*param);
+}
+CTR_Code c_TalonSRX_GetLimitSwitchClosedFor(void *handle, int *param)
+{
+ return ((CanTalonSRX*)handle)->GetLimitSwitchClosedFor(*param);
+}
+CTR_Code c_TalonSRX_GetLimitSwitchClosedRev(void *handle, int *param)
+{
+ return ((CanTalonSRX*)handle)->GetLimitSwitchClosedRev(*param);
+}
+CTR_Code c_TalonSRX_GetSensorPosition(void *handle, int *param)
+{
+ return ((CanTalonSRX*)handle)->GetSensorPosition(*param);
+}
+CTR_Code c_TalonSRX_GetSensorVelocity(void *handle, int *param)
+{
+ return ((CanTalonSRX*)handle)->GetSensorVelocity(*param);
+}
+CTR_Code c_TalonSRX_GetCurrent(void *handle, double *param)
+{
+ return ((CanTalonSRX*)handle)->GetCurrent(*param);
+}
+CTR_Code c_TalonSRX_GetBrakeIsEnabled(void *handle, int *param)
+{
+ return ((CanTalonSRX*)handle)->GetBrakeIsEnabled(*param);
+}
+CTR_Code c_TalonSRX_GetEncPosition(void *handle, int *param)
+{
+ return ((CanTalonSRX*)handle)->GetEncPosition(*param);
+}
+CTR_Code c_TalonSRX_GetEncVel(void *handle, int *param)
+{
+ return ((CanTalonSRX*)handle)->GetEncVel(*param);
+}
+CTR_Code c_TalonSRX_GetEncIndexRiseEvents(void *handle, int *param)
+{
+ return ((CanTalonSRX*)handle)->GetEncIndexRiseEvents(*param);
+}
+CTR_Code c_TalonSRX_GetQuadApin(void *handle, int *param)
+{
+ return ((CanTalonSRX*)handle)->GetQuadApin(*param);
+}
+CTR_Code c_TalonSRX_GetQuadBpin(void *handle, int *param)
+{
+ return ((CanTalonSRX*)handle)->GetQuadBpin(*param);
+}
+CTR_Code c_TalonSRX_GetQuadIdxpin(void *handle, int *param)
+{
+ return ((CanTalonSRX*)handle)->GetQuadIdxpin(*param);
+}
+CTR_Code c_TalonSRX_GetAnalogInWithOv(void *handle, int *param)
+{
+ return ((CanTalonSRX*)handle)->GetAnalogInWithOv(*param);
+}
+CTR_Code c_TalonSRX_GetAnalogInVel(void *handle, int *param)
+{
+ return ((CanTalonSRX*)handle)->GetAnalogInVel(*param);
+}
+CTR_Code c_TalonSRX_GetTemp(void *handle, double *param)
+{
+ return ((CanTalonSRX*)handle)->GetTemp(*param);
+}
+CTR_Code c_TalonSRX_GetBatteryV(void *handle, double *param)
+{
+ return ((CanTalonSRX*)handle)->GetBatteryV(*param);
+}
+CTR_Code c_TalonSRX_GetResetCount(void *handle, int *param)
+{
+ return ((CanTalonSRX*)handle)->GetResetCount(*param);
+}
+CTR_Code c_TalonSRX_GetResetFlags(void *handle, int *param)
+{
+ return ((CanTalonSRX*)handle)->GetResetFlags(*param);
+}
+CTR_Code c_TalonSRX_GetFirmVers(void *handle, int *param)
+{
+ return ((CanTalonSRX*)handle)->GetFirmVers(*param);
+}
+CTR_Code c_TalonSRX_SetDemand(void *handle, int param)
+{
+ return ((CanTalonSRX*)handle)->SetDemand(param);
+}
+CTR_Code c_TalonSRX_SetOverrideLimitSwitchEn(void *handle, int param)
+{
+ return ((CanTalonSRX*)handle)->SetOverrideLimitSwitchEn(param);
+}
+CTR_Code c_TalonSRX_SetFeedbackDeviceSelect(void *handle, int param)
+{
+ return ((CanTalonSRX*)handle)->SetFeedbackDeviceSelect(param);
+}
+CTR_Code c_TalonSRX_SetRevMotDuringCloseLoopEn(void *handle, int param)
+{
+ return ((CanTalonSRX*)handle)->SetRevMotDuringCloseLoopEn(param);
+}
+CTR_Code c_TalonSRX_SetOverrideBrakeType(void *handle, int param)
+{
+ return ((CanTalonSRX*)handle)->SetOverrideBrakeType(param);
+}
+CTR_Code c_TalonSRX_SetModeSelect(void *handle, int param)
+{
+ return ((CanTalonSRX*)handle)->SetModeSelect(param);
+}
+CTR_Code c_TalonSRX_SetModeSelect2(void *handle, int modeSelect, int demand)
+{
+ return ((CanTalonSRX*)handle)->SetModeSelect(modeSelect, demand);
+}
+CTR_Code c_TalonSRX_SetProfileSlotSelect(void *handle, int param)
+{
+ return ((CanTalonSRX*)handle)->SetProfileSlotSelect(param);
+}
+CTR_Code c_TalonSRX_SetRampThrottle(void *handle, int param)
+{
+ return ((CanTalonSRX*)handle)->SetRampThrottle(param);
+}
+CTR_Code c_TalonSRX_SetRevFeedbackSensor(void *handle, int param)
+{
+ return ((CanTalonSRX*)handle)->SetRevFeedbackSensor(param);
+}
+}
diff --git a/aos/externals/allwpilib/hal/lib/Athena/ctre/CanTalonSRX.h b/aos/externals/allwpilib/hal/lib/Athena/ctre/CanTalonSRX.h
new file mode 100644
index 0000000..cc2236b
--- /dev/null
+++ b/aos/externals/allwpilib/hal/lib/Athena/ctre/CanTalonSRX.h
@@ -0,0 +1,375 @@
+/**
+ * @brief CAN TALON SRX driver.
+ *
+ * The TALON SRX is designed to instrument all runtime signals periodically. The default periods are chosen to support 16 TALONs
+ * with 10ms update rate for control (throttle or setpoint). However these can be overridden with SetStatusFrameRate. @see SetStatusFrameRate
+ * The getters for these unsolicited signals are auto generated at the bottom of this module.
+ *
+ * Likewise most control signals are sent periodically using the fire-and-forget CAN API.
+ * The setters for these unsolicited signals are auto generated at the bottom of this module.
+ *
+ * Signals that are not available in an unsolicited fashion are the Close Loop gains.
+ * For teams that have a single profile for their TALON close loop they can use either the webpage to configure their TALONs once
+ * or set the PIDF,Izone,CloseLoopRampRate,etc... once in the robot application. These parameters are saved to flash so once they are
+ * loaded in the TALON, they will persist through power cycles and mode changes.
+ *
+ * For teams that have one or two profiles to switch between, they can use the same strategy since there are two slots to choose from
+ * and the ProfileSlotSelect is periodically sent in the 10 ms control frame.
+ *
+ * For teams that require changing gains frequently, they can use the soliciting API to get and set those parameters. Most likely
+ * they will only need to set them in a periodic fashion as a function of what motion the application is attempting.
+ * If this API is used, be mindful of the CAN utilization reported in the driver station.
+ *
+ * Encoder position is measured in encoder edges. Every edge is counted (similar to roboRIO 4X mode).
+ * Analog position is 10 bits, meaning 1024 ticks per rotation (0V => 3.3V).
+ * Use SetFeedbackDeviceSelect to select which sensor type you need. Once you do that you can use GetSensorPosition()
+ * and GetSensorVelocity(). These signals are updated on CANBus every 20ms (by default).
+ * If a relative sensor is selected, you can zero (or change the current value) using SetSensorPosition.
+ *
+ * Analog Input and quadrature position (and velocity) are also explicitly reported in GetEncPosition, GetEncVel, GetAnalogInWithOv, GetAnalogInVel.
+ * These signals are available all the time, regardless of what sensor is selected at a rate of 100ms. This allows easy instrumentation
+ * for "in the pits" checking of all sensors regardless of modeselect. The 100ms rate is overridable for teams who want to acquire sensor
+ * data for processing, not just instrumentation. Or just select the sensor using SetFeedbackDeviceSelect to get it at 20ms.
+ *
+ * Velocity is in position ticks / 100ms.
+ *
+ * All output units are in respect to duty cycle (throttle) which is -1023(full reverse) to +1023 (full forward).
+ * This includes demand (which specifies duty cycle when in duty cycle mode) and rampRamp, which is in throttle units per 10ms (if nonzero).
+ *
+ * Pos and velocity close loops are calc'd as
+ * err = target - posOrVel.
+ * iErr += err;
+ * if( (IZone!=0) and abs(err) > IZone)
+ * ClearIaccum()
+ * output = P X err + I X iErr + D X dErr + F X target
+ * dErr = err - lastErr
+ * P, I,and D gains are always positive. F can be negative.
+ * Motor direction can be reversed using SetRevMotDuringCloseLoopEn if sensor and motor are out of phase.
+ * Similarly feedback sensor can also be reversed (multiplied by -1) if you prefer the sensor to be inverted.
+ *
+ * P gain is specified in throttle per error tick. For example, a value of 102 is ~9.9% (which is 102/1023) throttle per 1
+ * ADC unit(10bit) or 1 quadrature encoder edge depending on selected sensor.
+ *
+ * I gain is specified in throttle per integrated error. For example, a value of 10 equates to ~0.99% (which is 10/1023)
+ * for each accumulated ADC unit(10bit) or 1 quadrature encoder edge depending on selected sensor.
+ * Close loop and integral accumulator runs every 1ms.
+ *
+ * D gain is specified in throttle per derivative error. For example a value of 102 equates to ~9.9% (which is 102/1023)
+ * per change of 1 unit (ADC or encoder) per ms.
+ *
+ * I Zone is specified in the same units as sensor position (ADC units or quadrature edges). If pos/vel error is outside of
+ * this value, the integrated error will auto-clear...
+ * if( (IZone!=0) and abs(err) > IZone)
+ * ClearIaccum()
+ * ...this is very useful in preventing integral windup and is highly recommended if using full PID to keep stability low.
+ *
+ * CloseLoopRampRate is in throttle units per 1ms. Set to zero to disable ramping.
+ * Works the same as RampThrottle but only is in effect when a close loop mode and profile slot is selected.
+ *
+ * auto generated using spreadsheet and WpiClassGen.csproj
+ * @link https://docs.google.com/spreadsheets/d/1OU_ZV7fZLGYUQ-Uhc8sVAmUmWTlT8XBFYK8lfjg_tac/edit#gid=1766046967
+ */
+#ifndef CanTalonSRX_H_
+#define CanTalonSRX_H_
+#include "ctre.h" //BIT Defines + Typedefs
+#include "CtreCanNode.h"
+#include <NetworkCommunication/CANSessionMux.h> //CAN Comm
+#include <map>
+class CanTalonSRX : public CtreCanNode
+{
+private:
+
+ /** just in case user wants to modify periods of certain status frames.
+ * Default the vars to match the firmware default. */
+ uint32_t _statusRateMs[4];
+ //---------------------- Vars for opening a CAN stream if caller needs signals that require soliciting */
+ uint32_t _can_h; //!< Session handle for catching response params.
+ int32_t _can_stat; //!< Session handle status.
+ struct tCANStreamMessage _msgBuff[20];
+ static int const kMsgCapacity = 20;
+ typedef std::map<uint32_t, uint32_t> sigs_t;
+ sigs_t _sigs; //!< Catches signal updates that are solicited. Expect this to be very few.
+ void OpenSessionIfNeedBe();
+ void ProcessStreamMessages();
+ /**
+ * Send a one shot frame to set an arbitrary signal.
+ * Most signals are in the control frame so avoid using this API unless you have to.
+ * Use this api for...
+ * -A motor controller profile signal eProfileParam_XXXs. These are backed up in flash. If you are gain-scheduling then call this periodically.
+ * -Default brake and limit switch signals... eOnBoot_XXXs. Avoid doing this, use the override signals in the control frame.
+ * Talon will automatically send a PARAM_RESPONSE after the set, so GetParamResponse will catch the latest value after a couple ms.
+ */
+ CTR_Code SetParamRaw(uint32_t paramEnum, int32_t rawBits);
+ /**
+ * Checks cached CAN frames and updating solicited signals.
+ */
+ CTR_Code GetParamResponseRaw(uint32_t paramEnum, int32_t & rawBits);
+public:
+ static const int kDefaultControlPeriodMs = 10; //!< default control update rate is 10ms.
+ CanTalonSRX(int deviceNumber = 0,int controlPeriodMs = kDefaultControlPeriodMs);
+ ~CanTalonSRX();
+ void Set(double value);
+ /* mode select enumerations */
+ static const int kMode_DutyCycle = 0; //!< Demand is 11bit signed duty cycle [-1023,1023].
+ static const int kMode_PositionCloseLoop = 1; //!< Position PIDF.
+ static const int kMode_VelocityCloseLoop = 2; //!< Velocity PIDF.
+ static const int kMode_CurrentCloseLoop = 3; //!< Current close loop - not done.
+ static const int kMode_VoltCompen = 4; //!< Voltage Compensation Mode - not done. Demand is fixed pt target 8.8 volts.
+ static const int kMode_SlaveFollower = 5; //!< Demand is the 6 bit Device ID of the 'master' TALON SRX.
+ static const int kMode_NoDrive = 15; //!< Zero the output (honors brake/coast) regardless of demand. Might be useful if we need to change modes but can't atomically change all the signals we want in between.
+ /* limit switch enumerations */
+ static const int kLimitSwitchOverride_UseDefaultsFromFlash = 1;
+ static const int kLimitSwitchOverride_DisableFwd_DisableRev = 4;
+ static const int kLimitSwitchOverride_DisableFwd_EnableRev = 5;
+ static const int kLimitSwitchOverride_EnableFwd_DisableRev = 6;
+ static const int kLimitSwitchOverride_EnableFwd_EnableRev = 7;
+ /* brake override enumerations */
+ static const int kBrakeOverride_UseDefaultsFromFlash = 0;
+ static const int kBrakeOverride_OverrideCoast = 1;
+ static const int kBrakeOverride_OverrideBrake = 2;
+ /* feedback device enumerations */
+ static const int kFeedbackDev_DigitalQuadEnc=0;
+ static const int kFeedbackDev_AnalogPot=2;
+ static const int kFeedbackDev_AnalogEncoder=3;
+ static const int kFeedbackDev_CountEveryRisingEdge=4;
+ static const int kFeedbackDev_CountEveryFallingEdge=5;
+ static const int kFeedbackDev_PosIsPulseWidth=8;
+ /* ProfileSlotSelect enumerations*/
+ static const int kProfileSlotSelect_Slot0 = 0;
+ static const int kProfileSlotSelect_Slot1 = 1;
+ /* status frame rate types */
+ static const int kStatusFrame_General = 0;
+ static const int kStatusFrame_Feedback = 1;
+ static const int kStatusFrame_Encoder = 2;
+ static const int kStatusFrame_AnalogTempVbat = 3;
+ /**
+ * Signal enumeration for generic signal access.
+ * Although every signal is enumerated, only use this for traffic that must be solicited.
+ * Use the auto generated getters/setters at bottom of this header as much as possible.
+ */
+ typedef enum _param_t{
+ eProfileParamSlot0_P=1,
+ eProfileParamSlot0_I=2,
+ eProfileParamSlot0_D=3,
+ eProfileParamSlot0_F=4,
+ eProfileParamSlot0_IZone=5,
+ eProfileParamSlot0_CloseLoopRampRate=6,
+ eProfileParamSlot1_P=11,
+ eProfileParamSlot1_I=12,
+ eProfileParamSlot1_D=13,
+ eProfileParamSlot1_F=14,
+ eProfileParamSlot1_IZone=15,
+ eProfileParamSlot1_CloseLoopRampRate=16,
+ eProfileParamSoftLimitForThreshold=21,
+ eProfileParamSoftLimitRevThreshold=22,
+ eProfileParamSoftLimitForEnable=23,
+ eProfileParamSoftLimitRevEnable=24,
+ eOnBoot_BrakeMode=31,
+ eOnBoot_LimitSwitch_Forward_NormallyClosed=32,
+ eOnBoot_LimitSwitch_Reverse_NormallyClosed=33,
+ eOnBoot_LimitSwitch_Forward_Disable=34,
+ eOnBoot_LimitSwitch_Reverse_Disable=35,
+ eFault_OverTemp=41,
+ eFault_UnderVoltage=42,
+ eFault_ForLim=43,
+ eFault_RevLim=44,
+ eFault_HardwareFailure=45,
+ eFault_ForSoftLim=46,
+ eFault_RevSoftLim=47,
+ eStckyFault_OverTemp=48,
+ eStckyFault_UnderVoltage=49,
+ eStckyFault_ForLim=50,
+ eStckyFault_RevLim=51,
+ eStckyFault_ForSoftLim=52,
+ eStckyFault_RevSoftLim=53,
+ eAppliedThrottle=61,
+ eCloseLoopErr=62,
+ eFeedbackDeviceSelect=63,
+ eRevMotDuringCloseLoopEn=64,
+ eModeSelect=65,
+ eProfileSlotSelect=66,
+ eRampThrottle=67,
+ eRevFeedbackSensor=68,
+ eLimitSwitchEn=69,
+ eLimitSwitchClosedFor=70,
+ eLimitSwitchClosedRev=71,
+ eSensorPosition=73,
+ eSensorVelocity=74,
+ eCurrent=75,
+ eBrakeIsEnabled=76,
+ eEncPosition=77,
+ eEncVel=78,
+ eEncIndexRiseEvents=79,
+ eQuadApin=80,
+ eQuadBpin=81,
+ eQuadIdxpin=82,
+ eAnalogInWithOv=83,
+ eAnalogInVel=84,
+ eTemp=85,
+ eBatteryV=86,
+ eResetCount=87,
+ eResetFlags=88,
+ eFirmVers=89,
+ eSettingsChanged=90,
+ eQuadFilterEn=91,
+ ePidIaccum=93,
+ }param_t;
+ /*---------------------setters and getters that use the solicated param request/response-------------*//**
+ * Send a one shot frame to set an arbitrary signal.
+ * Most signals are in the control frame so avoid using this API unless you have to.
+ * Use this api for...
+ * -A motor controller profile signal eProfileParam_XXXs. These are backed up in flash. If you are gain-scheduling then call this periodically.
+ * -Default brake and limit switch signals... eOnBoot_XXXs. Avoid doing this, use the override signals in the control frame.
+ * Talon will automatically send a PARAM_RESPONSE after the set, so GetParamResponse will catch the latest value after a couple ms.
+ */
+ CTR_Code SetParam(param_t paramEnum, double value);
+ /**
+ * Asks TALON to immedietely respond with signal value. This API is only used for signals that are not sent periodically.
+ * This can be useful for reading params that rarely change like Limit Switch settings and PIDF values.
+ * @param param to request.
+ */
+ CTR_Code RequestParam(param_t paramEnum);
+ CTR_Code GetParamResponse(param_t paramEnum, double & value);
+ CTR_Code GetParamResponseInt32(param_t paramEnum, int & value);
+ /*----- getters and setters that use param request/response. These signals are backed up in flash and will survive a power cycle. ---------*/
+ /*----- If your application requires changing these values consider using both slots and switch between slot0 <=> slot1. ------------------*/
+ /*----- If your application requires changing these signals frequently then it makes sense to leverage this API. --------------------------*/
+ /*----- Getters don't block, so it may require several calls to get the latest value. --------------------------*/
+ CTR_Code SetPgain(unsigned slotIdx,double gain);
+ CTR_Code SetIgain(unsigned slotIdx,double gain);
+ CTR_Code SetDgain(unsigned slotIdx,double gain);
+ CTR_Code SetFgain(unsigned slotIdx,double gain);
+ CTR_Code SetIzone(unsigned slotIdx,int zone);
+ CTR_Code SetCloseLoopRampRate(unsigned slotIdx,int closeLoopRampRate);
+ CTR_Code SetSensorPosition(int pos);
+ CTR_Code SetForwardSoftLimit(int forwardLimit);
+ CTR_Code SetReverseSoftLimit(int reverseLimit);
+ CTR_Code SetForwardSoftEnable(int enable);
+ CTR_Code SetReverseSoftEnable(int enable);
+ CTR_Code GetPgain(unsigned slotIdx,double & gain);
+ CTR_Code GetIgain(unsigned slotIdx,double & gain);
+ CTR_Code GetDgain(unsigned slotIdx,double & gain);
+ CTR_Code GetFgain(unsigned slotIdx,double & gain);
+ CTR_Code GetIzone(unsigned slotIdx,int & zone);
+ CTR_Code GetCloseLoopRampRate(unsigned slotIdx,int & closeLoopRampRate);
+ CTR_Code GetForwardSoftLimit(int & forwardLimit);
+ CTR_Code GetReverseSoftLimit(int & reverseLimit);
+ CTR_Code GetForwardSoftEnable(int & enable);
+ CTR_Code GetReverseSoftEnable(int & enable);
+ /**
+ * Change the periodMs of a TALON's status frame. See kStatusFrame_* enums for what's available.
+ */
+ CTR_Code SetStatusFrameRate(unsigned frameEnum, unsigned periodMs);
+ /**
+ * Clear all sticky faults in TALON.
+ */
+ CTR_Code ClearStickyFaults();
+ /*------------------------ auto generated. This API is optimal since it uses the fire-and-forget CAN interface ----------------------*/
+ /*------------------------ These signals should cover the majority of all use cases. ----------------------------------*/
+ CTR_Code GetFault_OverTemp(int ¶m);
+ CTR_Code GetFault_UnderVoltage(int ¶m);
+ CTR_Code GetFault_ForLim(int ¶m);
+ CTR_Code GetFault_RevLim(int ¶m);
+ CTR_Code GetFault_HardwareFailure(int ¶m);
+ CTR_Code GetFault_ForSoftLim(int ¶m);
+ CTR_Code GetFault_RevSoftLim(int ¶m);
+ CTR_Code GetStckyFault_OverTemp(int ¶m);
+ CTR_Code GetStckyFault_UnderVoltage(int ¶m);
+ CTR_Code GetStckyFault_ForLim(int ¶m);
+ CTR_Code GetStckyFault_RevLim(int ¶m);
+ CTR_Code GetStckyFault_ForSoftLim(int ¶m);
+ CTR_Code GetStckyFault_RevSoftLim(int ¶m);
+ CTR_Code GetAppliedThrottle(int ¶m);
+ CTR_Code GetCloseLoopErr(int ¶m);
+ CTR_Code GetFeedbackDeviceSelect(int ¶m);
+ CTR_Code GetModeSelect(int ¶m);
+ CTR_Code GetLimitSwitchEn(int ¶m);
+ CTR_Code GetLimitSwitchClosedFor(int ¶m);
+ CTR_Code GetLimitSwitchClosedRev(int ¶m);
+ CTR_Code GetSensorPosition(int ¶m);
+ CTR_Code GetSensorVelocity(int ¶m);
+ CTR_Code GetCurrent(double ¶m);
+ CTR_Code GetBrakeIsEnabled(int ¶m);
+ CTR_Code GetEncPosition(int ¶m);
+ CTR_Code GetEncVel(int ¶m);
+ CTR_Code GetEncIndexRiseEvents(int ¶m);
+ CTR_Code GetQuadApin(int ¶m);
+ CTR_Code GetQuadBpin(int ¶m);
+ CTR_Code GetQuadIdxpin(int ¶m);
+ CTR_Code GetAnalogInWithOv(int ¶m);
+ CTR_Code GetAnalogInVel(int ¶m);
+ CTR_Code GetTemp(double ¶m);
+ CTR_Code GetBatteryV(double ¶m);
+ CTR_Code GetResetCount(int ¶m);
+ CTR_Code GetResetFlags(int ¶m);
+ CTR_Code GetFirmVers(int ¶m);
+ CTR_Code SetDemand(int param);
+ CTR_Code SetOverrideLimitSwitchEn(int param);
+ CTR_Code SetFeedbackDeviceSelect(int param);
+ CTR_Code SetRevMotDuringCloseLoopEn(int param);
+ CTR_Code SetOverrideBrakeType(int param);
+ CTR_Code SetModeSelect(int param);
+ CTR_Code SetModeSelect(int modeSelect,int demand);
+ CTR_Code SetProfileSlotSelect(int param);
+ CTR_Code SetRampThrottle(int param);
+ CTR_Code SetRevFeedbackSensor(int param);
+};
+extern "C" {
+ void *c_TalonSRX_Create(int deviceNumber, int controlPeriodMs);
+ void c_TalonSRX_Destroy(void *handle);
+ CTR_Code c_TalonSRX_SetParam(void *handle, int paramEnum, double value);
+ CTR_Code c_TalonSRX_RequestParam(void *handle, int paramEnum);
+ CTR_Code c_TalonSRX_GetParamResponse(void *handle, int paramEnum, double *value);
+ CTR_Code c_TalonSRX_GetParamResponseInt32(void *handle, int paramEnum, int *value);
+ CTR_Code c_TalonSRX_SetStatusFrameRate(void *handle, unsigned frameEnum, unsigned periodMs);
+ CTR_Code c_TalonSRX_ClearStickyFaults(void *handle);
+ CTR_Code c_TalonSRX_GetFault_OverTemp(void *handle, int *param);
+ CTR_Code c_TalonSRX_GetFault_UnderVoltage(void *handle, int *param);
+ CTR_Code c_TalonSRX_GetFault_ForLim(void *handle, int *param);
+ CTR_Code c_TalonSRX_GetFault_RevLim(void *handle, int *param);
+ CTR_Code c_TalonSRX_GetFault_HardwareFailure(void *handle, int *param);
+ CTR_Code c_TalonSRX_GetFault_ForSoftLim(void *handle, int *param);
+ CTR_Code c_TalonSRX_GetFault_RevSoftLim(void *handle, int *param);
+ CTR_Code c_TalonSRX_GetStckyFault_OverTemp(void *handle, int *param);
+ CTR_Code c_TalonSRX_GetStckyFault_UnderVoltage(void *handle, int *param);
+ CTR_Code c_TalonSRX_GetStckyFault_ForLim(void *handle, int *param);
+ CTR_Code c_TalonSRX_GetStckyFault_RevLim(void *handle, int *param);
+ CTR_Code c_TalonSRX_GetStckyFault_ForSoftLim(void *handle, int *param);
+ CTR_Code c_TalonSRX_GetStckyFault_RevSoftLim(void *handle, int *param);
+ CTR_Code c_TalonSRX_GetAppliedThrottle(void *handle, int *param);
+ CTR_Code c_TalonSRX_GetCloseLoopErr(void *handle, int *param);
+ CTR_Code c_TalonSRX_GetFeedbackDeviceSelect(void *handle, int *param);
+ CTR_Code c_TalonSRX_GetModeSelect(void *handle, int *param);
+ CTR_Code c_TalonSRX_GetLimitSwitchEn(void *handle, int *param);
+ CTR_Code c_TalonSRX_GetLimitSwitchClosedFor(void *handle, int *param);
+ CTR_Code c_TalonSRX_GetLimitSwitchClosedRev(void *handle, int *param);
+ CTR_Code c_TalonSRX_GetSensorPosition(void *handle, int *param);
+ CTR_Code c_TalonSRX_GetSensorVelocity(void *handle, int *param);
+ CTR_Code c_TalonSRX_GetCurrent(void *handle, double *param);
+ CTR_Code c_TalonSRX_GetBrakeIsEnabled(void *handle, int *param);
+ CTR_Code c_TalonSRX_GetEncPosition(void *handle, int *param);
+ CTR_Code c_TalonSRX_GetEncVel(void *handle, int *param);
+ CTR_Code c_TalonSRX_GetEncIndexRiseEvents(void *handle, int *param);
+ CTR_Code c_TalonSRX_GetQuadApin(void *handle, int *param);
+ CTR_Code c_TalonSRX_GetQuadBpin(void *handle, int *param);
+ CTR_Code c_TalonSRX_GetQuadIdxpin(void *handle, int *param);
+ CTR_Code c_TalonSRX_GetAnalogInWithOv(void *handle, int *param);
+ CTR_Code c_TalonSRX_GetAnalogInVel(void *handle, int *param);
+ CTR_Code c_TalonSRX_GetTemp(void *handle, double *param);
+ CTR_Code c_TalonSRX_GetBatteryV(void *handle, double *param);
+ CTR_Code c_TalonSRX_GetResetCount(void *handle, int *param);
+ CTR_Code c_TalonSRX_GetResetFlags(void *handle, int *param);
+ CTR_Code c_TalonSRX_GetFirmVers(void *handle, int *param);
+ CTR_Code c_TalonSRX_SetDemand(void *handle, int param);
+ CTR_Code c_TalonSRX_SetOverrideLimitSwitchEn(void *handle, int param);
+ CTR_Code c_TalonSRX_SetFeedbackDeviceSelect(void *handle, int param);
+ CTR_Code c_TalonSRX_SetRevMotDuringCloseLoopEn(void *handle, int param);
+ CTR_Code c_TalonSRX_SetOverrideBrakeType(void *handle, int param);
+ CTR_Code c_TalonSRX_SetModeSelect(void *handle, int param);
+ CTR_Code c_TalonSRX_SetModeSelect2(void *handle, int modeSelect, int demand);
+ CTR_Code c_TalonSRX_SetProfileSlotSelect(void *handle, int param);
+ CTR_Code c_TalonSRX_SetRampThrottle(void *handle, int param);
+ CTR_Code c_TalonSRX_SetRevFeedbackSensor(void *handle, int param);
+}
+#endif
+
diff --git a/aos/externals/allwpilib/hal/lib/Athena/ctre/CtreCanNode.cpp b/aos/externals/allwpilib/hal/lib/Athena/ctre/CtreCanNode.cpp
new file mode 100644
index 0000000..b2e3620
--- /dev/null
+++ b/aos/externals/allwpilib/hal/lib/Athena/ctre/CtreCanNode.cpp
@@ -0,0 +1,101 @@
+#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
+
+#include "CtreCanNode.h"
+#include "NetworkCommunication/CANSessionMux.h"
+#include <string.h> // memset
+#include <unistd.h> // usleep
+
+static const UINT32 kFullMessageIDMask = 0x1fffffff;
+
+CtreCanNode::CtreCanNode(UINT8 deviceNumber)
+{
+ _deviceNumber = deviceNumber;
+}
+CtreCanNode::~CtreCanNode()
+{
+}
+void CtreCanNode::RegisterRx(uint32_t arbId)
+{
+ /* no need to do anything, we just use new API to poll last received message */
+}
+void CtreCanNode::RegisterTx(uint32_t arbId, uint32_t periodMs)
+{
+ int32_t status = 0;
+
+ txJob_t job = {0};
+ job.arbId = arbId;
+ job.periodMs = periodMs;
+ _txJobs[arbId] = job;
+ FRC_NetworkCommunication_CANSessionMux_sendMessage( job.arbId,
+ job.toSend,
+ 8,
+ job.periodMs,
+ &status);
+}
+timespec diff(const timespec & start, const timespec & end)
+{
+ timespec temp;
+ if ((end.tv_nsec-start.tv_nsec)<0) {
+ temp.tv_sec = end.tv_sec-start.tv_sec-1;
+ temp.tv_nsec = 1000000000+end.tv_nsec-start.tv_nsec;
+ } else {
+ temp.tv_sec = end.tv_sec-start.tv_sec;
+ temp.tv_nsec = end.tv_nsec-start.tv_nsec;
+ }
+ return temp;
+}
+CTR_Code CtreCanNode::GetRx(uint32_t arbId,uint8_t * dataBytes, uint32_t timeoutMs)
+{
+ CTR_Code retval = CTR_OKAY;
+ int32_t status = 0;
+ uint8_t len = 0;
+ uint32_t timeStamp;
+ /* cap timeout at 999ms */
+ if(timeoutMs > 999)
+ timeoutMs = 999;
+ FRC_NetworkCommunication_CANSessionMux_receiveMessage(&arbId,kFullMessageIDMask,dataBytes,&len,&timeStamp,&status);
+ if(status == 0){
+ /* fresh update */
+ rxEvent_t & r = _rxRxEvents[arbId]; /* lookup entry or make a default new one with all zeroes */
+ clock_gettime(2,&r.time); /* fill in time */
+ memcpy(r.bytes, dataBytes, 8); /* fill in databytes */
+ }else{
+ /* did not get the message */
+ rxRxEvents_t::iterator i = _rxRxEvents.find(arbId);
+ if(i == _rxRxEvents.end()){
+ /* we've never gotten this mesage */
+ retval = CTR_RxTimeout;
+ /* fill caller's buffer with zeros */
+ memset(dataBytes,0,8);
+ }else{
+ /* we've gotten this message before but not recently */
+ memcpy(dataBytes,i->second.bytes,8);
+ /* get the time now */
+ struct timespec temp;
+ clock_gettime(2,&temp); /* get now */
+ /* how long has it been? */
+ temp = diff(i->second.time,temp); /* temp = now - last */
+ if(temp.tv_sec > 0){
+ retval = CTR_RxTimeout;
+ }else if(temp.tv_nsec > ((int32_t)timeoutMs*1000*1000)){
+ retval = CTR_RxTimeout;
+ }else {
+ /* our last update was recent enough */
+ }
+ }
+ }
+
+ return retval;
+}
+void CtreCanNode::FlushTx(uint32_t arbId)
+{
+ int32_t status = 0;
+ txJobs_t::iterator iter = _txJobs.find(arbId);
+ if(iter != _txJobs.end())
+ FRC_NetworkCommunication_CANSessionMux_sendMessage( iter->second.arbId,
+ iter->second.toSend,
+ 8,
+ iter->second.periodMs,
+ &status);
+}
+
diff --git a/aos/externals/allwpilib/hal/lib/Athena/ctre/CtreCanNode.h b/aos/externals/allwpilib/hal/lib/Athena/ctre/CtreCanNode.h
new file mode 100644
index 0000000..7a2d690
--- /dev/null
+++ b/aos/externals/allwpilib/hal/lib/Athena/ctre/CtreCanNode.h
@@ -0,0 +1,116 @@
+#ifndef CtreCanNode_H_
+#define CtreCanNode_H_
+#include "ctre.h" //BIT Defines + Typedefs
+#include <NetworkCommunication/CANSessionMux.h> //CAN Comm
+#include <pthread.h>
+#include <map>
+#include <string.h> // memcpy
+#include <sys/time.h>
+class CtreCanNode
+{
+public:
+ CtreCanNode(UINT8 deviceNumber);
+ ~CtreCanNode();
+
+ UINT8 GetDeviceNumber()
+ {
+ return _deviceNumber;
+ }
+protected:
+
+
+ template <typename T> class txTask{
+ public:
+ uint32_t arbId;
+ T * toSend;
+ T * operator -> ()
+ {
+ return toSend;
+ }
+ T & operator*()
+ {
+ return *toSend;
+ }
+ bool IsEmpty()
+ {
+ if(toSend == 0)
+ return true;
+ return false;
+ }
+ };
+ template <typename T> class recMsg{
+ public:
+ uint32_t arbId;
+ uint8_t bytes[8];
+ CTR_Code err;
+ T * operator -> ()
+ {
+ return (T *)bytes;
+ }
+ T & operator*()
+ {
+ return *(T *)bytes;
+ }
+ };
+ UINT8 _deviceNumber;
+ void RegisterRx(uint32_t arbId);
+ void RegisterTx(uint32_t arbId, uint32_t periodMs);
+
+ CTR_Code GetRx(uint32_t arbId,uint8_t * dataBytes,uint32_t timeoutMs);
+ void FlushTx(uint32_t arbId);
+
+ template<typename T> txTask<T> GetTx(uint32_t arbId)
+ {
+ txTask<T> retval = {0, nullptr};
+ txJobs_t::iterator i = _txJobs.find(arbId);
+ if(i != _txJobs.end()){
+ retval.arbId = i->second.arbId;
+ retval.toSend = (T*)i->second.toSend;
+ }
+ return retval;
+ }
+ template<class T> void FlushTx(T & par)
+ {
+ FlushTx(par.arbId);
+ }
+
+ template<class T> recMsg<T> GetRx(uint32_t arbId, uint32_t timeoutMs)
+ {
+ recMsg<T> retval;
+ retval.err = GetRx(arbId,retval.bytes, timeoutMs);
+ return retval;
+ }
+
+private:
+
+ class txJob_t {
+ public:
+ uint32_t arbId;
+ uint8_t toSend[8];
+ uint32_t periodMs;
+ };
+
+ class rxEvent_t{
+ public:
+ uint8_t bytes[8];
+ struct timespec time;
+ rxEvent_t()
+ {
+ bytes[0] = 0;
+ bytes[1] = 0;
+ bytes[2] = 0;
+ bytes[3] = 0;
+ bytes[4] = 0;
+ bytes[5] = 0;
+ bytes[6] = 0;
+ bytes[7] = 0;
+ }
+ };
+
+ typedef std::map<uint32_t,txJob_t> txJobs_t;
+ txJobs_t _txJobs;
+
+ typedef std::map<uint32_t,rxEvent_t> rxRxEvents_t;
+ rxRxEvents_t _rxRxEvents;
+};
+#endif
diff --git a/aos/externals/allwpilib/hal/lib/Athena/ctre/PCM.cpp b/aos/externals/allwpilib/hal/lib/Athena/ctre/PCM.cpp
new file mode 100644
index 0000000..fbf6555
--- /dev/null
+++ b/aos/externals/allwpilib/hal/lib/Athena/ctre/PCM.cpp
@@ -0,0 +1,541 @@
+#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
+
+#include "PCM.h"
+#include "NetworkCommunication/CANSessionMux.h"
+#include <string.h> // memset
+#include <unistd.h> // usleep
+/* This can be a constant, as long as nobody needs to updatie solenoids within
+ 1/50 of a second. */
+static const INT32 kCANPeriod = 20;
+
+#define STATUS_1 0x9041400
+#define STATUS_SOL_FAULTS 0x9041440
+#define STATUS_DEBUG 0x9041480
+
+#define EXPECTED_RESPONSE_TIMEOUT_MS (50)
+#define GET_PCM_STATUS() CtreCanNode::recMsg<PcmStatus_t> rx = GetRx<PcmStatus_t> (STATUS_1,EXPECTED_RESPONSE_TIMEOUT_MS)
+#define GET_PCM_SOL_FAULTS() CtreCanNode::recMsg<PcmStatusFault_t> rx = GetRx<PcmStatusFault_t> (STATUS_SOL_FAULTS,EXPECTED_RESPONSE_TIMEOUT_MS)
+#define GET_PCM_DEBUG() CtreCanNode::recMsg<PcmDebug_t> rx = GetRx<PcmDebug_t> (STATUS_DEBUG,EXPECTED_RESPONSE_TIMEOUT_MS)
+
+#define CONTROL_1 0x09041C00 /* PCM_Control */
+#define CONTROL_2 0x09041C40 /* PCM_SupplemControl */
+#define CONTROL_3 0x09041C80 /* PcmControlSetOneShotDur_t */
+
+/* encoder/decoders */
+typedef struct _PcmStatus_t{
+ /* Byte 0 */
+ unsigned SolenoidBits:8;
+ /* Byte 1 */
+ unsigned compressorOn:1;
+ unsigned stickyFaultFuseTripped:1;
+ unsigned stickyFaultCompCurrentTooHigh:1;
+ unsigned faultFuseTripped:1;
+ unsigned faultCompCurrentTooHigh:1;
+ unsigned faultHardwareFailure:1;
+ unsigned isCloseloopEnabled:1;
+ unsigned pressureSwitchEn:1;
+ /* Byte 2*/
+ unsigned battVoltage:8;
+ /* Byte 3 */
+ unsigned solenoidVoltageTop8:8;
+ /* Byte 4 */
+ unsigned compressorCurrentTop6:6;
+ unsigned solenoidVoltageBtm2:2;
+ /* Byte 5 */
+ unsigned StickyFault_dItooHigh :1;
+ unsigned Fault_dItooHigh :1;
+ unsigned moduleEnabled:1;
+ unsigned closedLoopOutput:1;
+ unsigned compressorCurrentBtm4:4;
+ /* Byte 6 */
+ unsigned tokenSeedTop8:8;
+ /* Byte 7 */
+ unsigned tokenSeedBtm8:8;
+}PcmStatus_t;
+
+typedef struct _PcmControl_t{
+ /* Byte 0 */
+ unsigned tokenTop8:8;
+ /* Byte 1 */
+ unsigned tokenBtm8:8;
+ /* Byte 2 */
+ unsigned solenoidBits:8;
+ /* Byte 3*/
+ unsigned reserved:4;
+ unsigned closeLoopOutput:1;
+ unsigned compressorOn:1;
+ unsigned closedLoopEnable:1;
+ unsigned clearStickyFaults:1;
+ /* Byte 4 */
+ unsigned OneShotField_h8:8;
+ /* Byte 5 */
+ unsigned OneShotField_l8:8;
+}PcmControl_t;
+
+typedef struct _PcmControlSetOneShotDur_t{
+ uint8_t sol10MsPerUnit[8];
+}PcmControlSetOneShotDur_t;
+
+typedef struct _PcmStatusFault_t{
+ /* Byte 0 */
+ unsigned SolenoidBlacklist:8;
+ /* Byte 1 */
+ unsigned reserved_bit0 :1;
+ unsigned reserved_bit1 :1;
+ unsigned reserved_bit2 :1;
+ unsigned reserved_bit3 :1;
+ unsigned StickyFault_CompNoCurrent :1;
+ unsigned Fault_CompNoCurrent :1;
+ unsigned StickyFault_SolenoidJumper :1;
+ unsigned Fault_SolenoidJumper :1;
+}PcmStatusFault_t;
+
+typedef struct _PcmDebug_t{
+ unsigned tokFailsTop8:8;
+ unsigned tokFailsBtm8:8;
+ unsigned lastFailedTokTop8:8;
+ unsigned lastFailedTokBtm8:8;
+ unsigned tokSuccessTop8:8;
+ unsigned tokSuccessBtm8:8;
+}PcmDebug_t;
+
+
+/* PCM Constructor - Clears all vars, establishes default settings, starts PCM background process
+ *
+ * @Return - void
+ *
+ * @Param - deviceNumber - Device ID of PCM to be controlled
+ */
+PCM::PCM(UINT8 deviceNumber): CtreCanNode(deviceNumber)
+{
+ RegisterRx(STATUS_1 | deviceNumber );
+ RegisterRx(STATUS_SOL_FAULTS | deviceNumber );
+ RegisterRx(STATUS_DEBUG | deviceNumber );
+ RegisterTx(CONTROL_1 | deviceNumber, kCANPeriod);
+ /* enable close loop */
+ SetClosedLoopControl(1);
+}
+/* PCM D'tor
+ */
+PCM::~PCM()
+{
+
+}
+
+/* Set PCM solenoid state
+ *
+ * @Return - CTR_Code - Error code (if any) for setting solenoid
+ *
+ * @Param - idx - ID of solenoid (0-7)
+ * @Param - en - Enable / Disable identified solenoid
+ */
+CTR_Code PCM::SetSolenoids(uint8_t value, uint8_t mask)
+{
+ CtreCanNode::txTask<PcmControl_t> toFill = GetTx<PcmControl_t>(CONTROL_1 | GetDeviceNumber());
+ if(toFill.IsEmpty())return CTR_UnexpectedArbId;
+ toFill->solenoidBits |= mask & value;
+ toFill->solenoidBits &= ~(mask & ~value);
+ FlushTx(toFill);
+ return CTR_OKAY;
+}
+
+/* Clears PCM sticky faults (indicators of past faults
+ *
+ * @Return - CTR_Code - Error code (if any) for setting solenoid
+ *
+ * @Param - clr - Clear / do not clear faults
+ */
+CTR_Code PCM::ClearStickyFaults()
+{
+ int32_t status = 0;
+ uint8_t pcmSupplemControl[] = { 0, 0, 0, 0x80 }; /* only bit set is ClearStickyFaults */
+ FRC_NetworkCommunication_CANSessionMux_sendMessage(CONTROL_2 | GetDeviceNumber(), pcmSupplemControl, sizeof(pcmSupplemControl), 0, &status);
+ if(status)
+ return CTR_TxFailed;
+ return CTR_OKAY;
+}
+
+/* Enables PCM Closed Loop Control of Compressor via pressure switch
+ *
+ * @Return - CTR_Code - Error code (if any) for setting solenoid
+ *
+ * @Param - en - Enable / Disable Closed Loop Control
+ */
+CTR_Code PCM::SetClosedLoopControl(bool en)
+{
+ CtreCanNode::txTask<PcmControl_t> toFill = GetTx<PcmControl_t>(CONTROL_1 | GetDeviceNumber());
+ if(toFill.IsEmpty())return CTR_UnexpectedArbId;
+ toFill->closedLoopEnable = en;
+ FlushTx(toFill);
+ return CTR_OKAY;
+}
+/* Get solenoid Blacklist status
+ * @Return - CTR_Code - Error code (if any)
+ * @Param - idx - ID of solenoid [0,7] to fire one shot pulse.
+ */
+CTR_Code PCM::FireOneShotSolenoid(UINT8 idx)
+{
+ CtreCanNode::txTask<PcmControl_t> toFill = GetTx<PcmControl_t>(CONTROL_1 | GetDeviceNumber());
+ if(toFill.IsEmpty())return CTR_UnexpectedArbId;
+ /* grab field as it is now */
+ uint16_t oneShotField;
+ oneShotField = toFill->OneShotField_h8;
+ oneShotField <<= 8;
+ oneShotField |= toFill->OneShotField_l8;
+ /* get the caller's channel */
+ uint16_t shift = 2*idx;
+ uint16_t mask = 3; /* two bits wide */
+ uint8_t chBits = (oneShotField >> shift) & mask;
+ /* flip it */
+ chBits = (chBits)%3 + 1;
+ /* clear out 2bits for this channel*/
+ oneShotField &= ~(mask << shift);
+ /* put new field in */
+ oneShotField |= chBits << shift;
+ /* apply field as it is now */
+ toFill->OneShotField_h8 = oneShotField >> 8;
+ toFill->OneShotField_l8 = oneShotField;
+ FlushTx(toFill);
+ return CTR_OKAY;
+}
+/* Configure the pulse width of a solenoid channel for one-shot pulse.
+ * Preprogrammed pulsewidth is 10ms resolute and can be between 20ms and 5.1s.
+ * @Return - CTR_Code - Error code (if any)
+ * @Param - idx - ID of solenoid [0,7] to configure.
+ * @Param - durMs - pulse width in ms.
+ */
+CTR_Code PCM::SetOneShotDurationMs(UINT8 idx,uint32_t durMs)
+{
+ /* sanity check caller's param */
+ if(idx > 8)
+ return CTR_InvalidParamValue;
+ /* get latest tx frame */
+ CtreCanNode::txTask<PcmControlSetOneShotDur_t> toFill = GetTx<PcmControlSetOneShotDur_t>(CONTROL_3 | GetDeviceNumber());
+ if(toFill.IsEmpty()){
+ /* only send this out if caller wants to do one-shots */
+ RegisterTx(CONTROL_3 | _deviceNumber, kCANPeriod);
+ /* grab it */
+ toFill = GetTx<PcmControlSetOneShotDur_t>(CONTROL_3 | GetDeviceNumber());
+ }
+ toFill->sol10MsPerUnit[idx] = std::min(durMs/10,(uint32_t)0xFF);
+ /* apply the new data bytes */
+ FlushTx(toFill);
+ return CTR_OKAY;
+}
+
+/* Get solenoid state
+ *
+ * @Return - True/False - True if solenoid enabled, false otherwise
+ *
+ * @Param - idx - ID of solenoid (0-7) to return status of
+ */
+CTR_Code PCM::GetSolenoid(UINT8 idx, bool &status)
+{
+ GET_PCM_STATUS();
+ status = (rx->SolenoidBits & (1ul<<(idx)) ) ? 1 : 0;
+ return rx.err;
+}
+
+/* Get pressure switch state
+ *
+ * @Return - True/False - True if pressure adequate, false if low
+ */
+CTR_Code PCM::GetPressure(bool &status)
+{
+ GET_PCM_STATUS();
+ status = (rx->pressureSwitchEn ) ? 1 : 0;
+ return rx.err;
+}
+
+/* Get compressor state
+ *
+ * @Return - True/False - True if enabled, false if otherwise
+ */
+CTR_Code PCM::GetCompressor(bool &status)
+{
+ GET_PCM_STATUS();
+ status = (rx->compressorOn);
+ return rx.err;
+}
+
+/* Get closed loop control state
+ *
+ * @Return - True/False - True if closed loop enabled, false if otherwise
+ */
+CTR_Code PCM::GetClosedLoopControl(bool &status)
+{
+ GET_PCM_STATUS();
+ status = (rx->isCloseloopEnabled);
+ return rx.err;
+}
+
+/* Get compressor current draw
+ *
+ * @Return - Amperes - Compressor current
+ */
+CTR_Code PCM::GetCompressorCurrent(float &status)
+{
+ GET_PCM_STATUS();
+ uint32_t temp =(rx->compressorCurrentTop6);
+ temp <<= 4;
+ temp |= rx->compressorCurrentBtm4;
+ status = temp * 0.03125; /* 5.5 fixed pt value in Amps */
+ return rx.err;
+}
+
+/* Get voltage across solenoid rail
+ *
+ * @Return - Volts - Voltage across solenoid rail
+ */
+CTR_Code PCM::GetSolenoidVoltage(float &status)
+{
+ GET_PCM_STATUS();
+ uint32_t raw =(rx->solenoidVoltageTop8);
+ raw <<= 2;
+ raw |= rx->solenoidVoltageBtm2;
+ status = (double) raw * 0.03125; /* 5.5 fixed pt value in Volts */
+ return rx.err;
+}
+
+/* Get hardware fault value
+ *
+ * @Return - True/False - True if hardware failure detected, false if otherwise
+ */
+CTR_Code PCM::GetHardwareFault(bool &status)
+{
+ GET_PCM_STATUS();
+ status = rx->faultHardwareFailure;
+ return rx.err;
+}
+
+/* Get compressor fault value
+ *
+ * @Return - True/False - True if shorted compressor detected, false if otherwise
+ */
+CTR_Code PCM::GetCompressorCurrentTooHighFault(bool &status)
+{
+ GET_PCM_STATUS();
+ status = rx->faultCompCurrentTooHigh;
+ return rx.err;
+}
+CTR_Code PCM::GetCompressorShortedStickyFault(bool &status)
+{
+ GET_PCM_STATUS();
+ status = rx->StickyFault_dItooHigh;
+ return rx.err;
+}
+CTR_Code PCM::GetCompressorShortedFault(bool &status)
+{
+ GET_PCM_STATUS();
+ status = rx->Fault_dItooHigh;
+ return rx.err;
+}
+CTR_Code PCM::GetCompressorNotConnectedStickyFault(bool &status)
+{
+ GET_PCM_SOL_FAULTS();
+ status = rx->StickyFault_CompNoCurrent;
+ return rx.err;
+}
+CTR_Code PCM::GetCompressorNotConnectedFault(bool &status)
+{
+ GET_PCM_SOL_FAULTS();
+ status = rx->Fault_CompNoCurrent;
+ return rx.err;
+}
+
+/* Get solenoid fault value
+ *
+ * @Return - True/False - True if shorted solenoid detected, false if otherwise
+ */
+CTR_Code PCM::GetSolenoidFault(bool &status)
+{
+ GET_PCM_STATUS();
+ status = rx->faultFuseTripped;
+ return rx.err;
+}
+
+/* Get compressor sticky fault value
+ *
+ * @Return - True/False - True if solenoid had previously been shorted
+ * (and sticky fault was not cleared), false if otherwise
+ */
+CTR_Code PCM::GetCompressorCurrentTooHighStickyFault(bool &status)
+{
+ GET_PCM_STATUS();
+ status = rx->stickyFaultCompCurrentTooHigh;
+ return rx.err;
+}
+
+/* Get solenoid sticky fault value
+ *
+ * @Return - True/False - True if compressor had previously been shorted
+ * (and sticky fault was not cleared), false if otherwise
+ */
+CTR_Code PCM::GetSolenoidStickyFault(bool &status)
+{
+ GET_PCM_STATUS();
+ status = rx->stickyFaultFuseTripped;
+ return rx.err;
+}
+/* Get battery voltage
+ *
+ * @Return - Volts - Voltage across PCM power ports
+ */
+CTR_Code PCM::GetBatteryVoltage(float &status)
+{
+ GET_PCM_STATUS();
+ status = (float)rx->battVoltage * 0.05 + 4.0; /* 50mV per unit plus 4V. */
+ return rx.err;
+}
+/* Return status of module enable/disable
+ *
+ * @Return - bool - Returns TRUE if PCM is enabled, FALSE if disabled
+ */
+CTR_Code PCM::isModuleEnabled(bool &status)
+{
+ GET_PCM_STATUS();
+ status = rx->moduleEnabled;
+ return rx.err;
+}
+/* Get number of total failed PCM Control Frame
+ *
+ * @Return - Failed Control Frames - Number of failed control frames (tokenization fails)
+ *
+ * @WARNING - Return only valid if [SeekDebugFrames] is enabled
+ * See function SeekDebugFrames
+ * See function EnableSeekDebugFrames
+ */
+CTR_Code PCM::GetNumberOfFailedControlFrames(UINT16 &status)
+{
+ GET_PCM_DEBUG();
+ status = rx->tokFailsTop8;
+ status <<= 8;
+ status |= rx->tokFailsBtm8;
+ return rx.err;
+}
+/* Get raw Solenoid Blacklist
+ *
+ * @Return - BINARY - Raw binary breakdown of Solenoid Blacklist
+ * BIT7 = Solenoid 1, BIT6 = Solenoid 2, etc.
+ *
+ * @WARNING - Return only valid if [SeekStatusFaultFrames] is enabled
+ * See function SeekStatusFaultFrames
+ * See function EnableSeekStatusFaultFrames
+ */
+CTR_Code PCM::GetSolenoidBlackList(UINT8 &status)
+{
+ GET_PCM_SOL_FAULTS();
+ status = rx->SolenoidBlacklist;
+ return rx.err;
+}
+/* Get solenoid Blacklist status
+ * - Blacklisted solenoids cannot be enabled until PCM is power cycled
+ *
+ * @Return - True/False - True if Solenoid is blacklisted, false if otherwise
+ *
+ * @Param - idx - ID of solenoid [0,7]
+ *
+ * @WARNING - Return only valid if [SeekStatusFaultFrames] is enabled
+ * See function SeekStatusFaultFrames
+ * See function EnableSeekStatusFaultFrames
+ */
+CTR_Code PCM::IsSolenoidBlacklisted(UINT8 idx, bool &status)
+{
+ GET_PCM_SOL_FAULTS();
+ status = (rx->SolenoidBlacklist & (1ul<<(idx)) )? 1 : 0;
+ return rx.err;
+}
+//------------------ C interface --------------------------------------------//
+extern "C" {
+ void * c_PCM_Init(void) {
+ return new PCM();
+ }
+ CTR_Code c_SetSolenoid(void * handle, uint8_t value, uint8_t mask) {
+ return ((PCM*) handle)->SetSolenoids(value, mask);
+ }
+ CTR_Code c_SetClosedLoopControl(void * handle, INT8 param) {
+ return ((PCM*) handle)->SetClosedLoopControl(param);
+ }
+ CTR_Code c_ClearStickyFaults(void * handle, INT8 param) {
+ return ((PCM*) handle)->ClearStickyFaults();
+ }
+ CTR_Code c_GetSolenoid(void * handle, UINT8 idx, INT8 * status) {
+ bool bstatus;
+ CTR_Code retval = ((PCM*) handle)->GetSolenoid(idx, bstatus);
+ *status = bstatus;
+ return retval;
+ }
+ CTR_Code c_GetPressure(void * handle, INT8 * status) {
+ bool bstatus;
+ CTR_Code retval = ((PCM*) handle)->GetPressure(bstatus);
+ *status = bstatus;
+ return retval;
+ }
+ CTR_Code c_GetCompressor(void * handle, INT8 * status) {
+ bool bstatus;
+ CTR_Code retval = ((PCM*) handle)->GetCompressor(bstatus);
+ *status = bstatus;
+ return retval;
+ }
+ CTR_Code c_GetClosedLoopControl(void * handle, INT8 * status) {
+ bool bstatus;
+ CTR_Code retval = ((PCM*) handle)->GetClosedLoopControl(bstatus);
+ *status = bstatus;
+ return retval;
+ }
+ CTR_Code c_GetCompressorCurrent(void * handle, float * status) {
+ CTR_Code retval = ((PCM*) handle)->GetCompressorCurrent(*status);
+ return retval;
+ }
+ CTR_Code c_GetSolenoidVoltage(void * handle, float*status) {
+ return ((PCM*) handle)->GetSolenoidVoltage(*status);
+ }
+ CTR_Code c_GetHardwareFault(void * handle, INT8*status) {
+ bool bstatus;
+ CTR_Code retval = ((PCM*) handle)->GetHardwareFault(bstatus);
+ *status = bstatus;
+ return retval;
+ }
+ CTR_Code c_GetCompressorFault(void * handle, INT8*status) {
+ bool bstatus;
+ CTR_Code retval = ((PCM*) handle)->GetCompressorCurrentTooHighFault(bstatus);
+ *status = bstatus;
+ return retval;
+ }
+ CTR_Code c_GetSolenoidFault(void * handle, INT8*status) {
+ bool bstatus;
+ CTR_Code retval = ((PCM*) handle)->GetSolenoidFault(bstatus);
+ *status = bstatus;
+ return retval;
+ }
+ CTR_Code c_GetCompressorStickyFault(void * handle, INT8*status) {
+ bool bstatus;
+ CTR_Code retval = ((PCM*) handle)->GetCompressorCurrentTooHighStickyFault(bstatus);
+ *status = bstatus;
+ return retval;
+ }
+ CTR_Code c_GetSolenoidStickyFault(void * handle, INT8*status) {
+ bool bstatus;
+ CTR_Code retval = ((PCM*) handle)->GetSolenoidStickyFault(bstatus);
+ *status = bstatus;
+ return retval;
+ }
+ CTR_Code c_GetBatteryVoltage(void * handle, float*status) {
+ CTR_Code retval = ((PCM*) handle)->GetBatteryVoltage(*status);
+ return retval;
+ }
+ void c_SetDeviceNumber_PCM(void * handle, UINT8 deviceNumber) {
+ }
+ CTR_Code c_GetNumberOfFailedControlFrames(void * handle, UINT16*status) {
+ return ((PCM*) handle)->GetNumberOfFailedControlFrames(*status);
+ }
+ CTR_Code c_GetSolenoidBlackList(void * handle, UINT8 *status) {
+ return ((PCM*) handle)->GetSolenoidBlackList(*status);
+ }
+ CTR_Code c_IsSolenoidBlacklisted(void * handle, UINT8 idx, INT8*status) {
+ bool bstatus;
+ CTR_Code retval = ((PCM*) handle)->IsSolenoidBlacklisted(idx, bstatus);
+ *status = bstatus;
+ return retval;
+ }
+}
diff --git a/aos/externals/allwpilib/hal/lib/Athena/ctre/PCM.h b/aos/externals/allwpilib/hal/lib/Athena/ctre/PCM.h
new file mode 100644
index 0000000..19dfc54
--- /dev/null
+++ b/aos/externals/allwpilib/hal/lib/Athena/ctre/PCM.h
@@ -0,0 +1,212 @@
+#ifndef PCM_H_
+#define PCM_H_
+#include "ctre.h" //BIT Defines + Typedefs
+#include <NetworkCommunication/CANSessionMux.h> //CAN Comm
+#include "CtreCanNode.h"
+#include <pthread.h>
+class PCM : public CtreCanNode
+{
+public:
+ PCM(UINT8 deviceNumber=0);
+ ~PCM();
+
+ /* Set PCM solenoids state
+ *
+ * @Return - CTR_Code - Error code (if any) for setting solenoids
+ * @Param - value - Values for specified solenoids
+ * @Param - mask - Mask of solenoids to set
+ */
+ CTR_Code SetSolenoids(uint8_t value, uint8_t mask);
+
+ /* Enables PCM Closed Loop Control of Compressor via pressure switch
+ * @Return - CTR_Code - Error code (if any) for setting solenoid
+ * @Param - en - Enable / Disable Closed Loop Control
+ */
+ CTR_Code SetClosedLoopControl(bool en);
+
+ /* Clears PCM sticky faults (indicators of past faults
+ * @Return - CTR_Code - Error code (if any) for setting solenoid
+ */
+ CTR_Code ClearStickyFaults();
+
+ /* Get solenoid state
+ *
+ * @Return - CTR_Code - Error code (if any)
+ * @Param - idx - ID of solenoid (0-7) to return if solenoid is on.
+ * @Param - status - OK if solenoid enabled, false otherwise
+ */
+ CTR_Code GetSolenoid(UINT8 idx, bool &status);
+
+ /* Get pressure switch state
+ * @Return - CTR_Code - Error code (if any)
+ * @Param - status - True if pressure adequate, false if low
+ */
+ CTR_Code GetPressure(bool &status);
+
+ /* Get compressor state
+ * @Return - CTR_Code - Error code (if any)
+ * @Param - status - True if compress ouput is on, false if otherwise
+ */
+ CTR_Code GetCompressor(bool &status);
+
+ /* Get closed loop control state
+ * @Return - CTR_Code - Error code (if any)
+ * @Param - status - True if closed loop enabled, false if otherwise
+ */
+ CTR_Code GetClosedLoopControl(bool &status);
+
+ /* Get compressor current draw
+ * @Return - CTR_Code - Error code (if any)
+ * @Param - status - Compressor current returned in Amperes (A)
+ */
+ CTR_Code GetCompressorCurrent(float &status);
+
+ /* Get voltage across solenoid rail
+ * @Return - CTR_Code - Error code (if any)
+ * @Param - status - Voltage across solenoid rail in Volts (V)
+ */
+ CTR_Code GetSolenoidVoltage(float &status);
+
+ /* Get hardware fault value
+ * @Return - CTR_Code - Error code (if any)
+ * @Param - status - True if hardware failure detected, false if otherwise
+ */
+ CTR_Code GetHardwareFault(bool &status);
+
+ /* Get compressor fault value
+ * @Return - CTR_Code - Error code (if any)
+ * @Param - status - True if abnormally high compressor current detected, false if otherwise
+ */
+ CTR_Code GetCompressorCurrentTooHighFault(bool &status);
+
+ /* Get solenoid fault value
+ * @Return - CTR_Code - Error code (if any)
+ * @Param - status - True if shorted solenoid detected, false if otherwise
+ */
+ CTR_Code GetSolenoidFault(bool &status);
+
+ /* Get compressor sticky fault value
+ * @Return - CTR_Code - Error code (if any)
+ * @Param - status - True if solenoid had previously been shorted
+ * (and sticky fault was not cleared), false if otherwise
+ */
+ CTR_Code GetCompressorCurrentTooHighStickyFault(bool &status);
+ /* Get compressor shorted sticky fault value
+ * @Return - CTR_Code - Error code (if any)
+ * @Param - status - True if compressor output is shorted, false if otherwise
+ */
+ CTR_Code GetCompressorShortedStickyFault(bool &status);
+ /* Get compressor shorted fault value
+ * @Return - CTR_Code - Error code (if any)
+ * @Param - status - True if compressor output is shorted, false if otherwise
+ */
+ CTR_Code GetCompressorShortedFault(bool &status);
+ /* Get compressor is not connected sticky fault value
+ * @Return - CTR_Code - Error code (if any)
+ * @Param - status - True if compressor current is too low,
+ * indicating compressor is not connected, false if otherwise
+ */
+ CTR_Code GetCompressorNotConnectedStickyFault(bool &status);
+ /* Get compressor is not connected fault value
+ * @Return - CTR_Code - Error code (if any)
+ * @Param - status - True if compressor current is too low,
+ * indicating compressor is not connected, false if otherwise
+ */
+ CTR_Code GetCompressorNotConnectedFault(bool &status);
+
+ /* Get solenoid sticky fault value
+ * @Return - CTR_Code - Error code (if any)
+ * @Param - status - True if compressor had previously been shorted
+ * (and sticky fault was not cleared), false if otherwise
+ */
+ CTR_Code GetSolenoidStickyFault(bool &status);
+
+ /* Get battery voltage
+ * @Return - CTR_Code - Error code (if any)
+ * @Param - status - Voltage across PCM power ports in Volts (V)
+ */
+ CTR_Code GetBatteryVoltage(float &status);
+
+ /* Set PCM Device Number and according CAN frame IDs
+ * @Return - void
+ * @Param - deviceNumber - Device number of PCM to control
+ */
+ void SetDeviceNumber(UINT8 deviceNumber);
+ /* Get number of total failed PCM Control Frame
+ * @Return - CTR_Code - Error code (if any)
+ * @Param - status - Number of failed control frames (tokenization fails)
+ * @WARNING - Return only valid if [SeekDebugFrames] is enabled
+ * See function SeekDebugFrames
+ * See function EnableSeekDebugFrames
+ */
+ CTR_Code GetNumberOfFailedControlFrames(UINT16 &status);
+
+ /* Get raw Solenoid Blacklist
+ * @Return - CTR_Code - Error code (if any)
+ * @Param - status - Raw binary breakdown of Solenoid Blacklist
+ * BIT7 = Solenoid 1, BIT6 = Solenoid 2, etc.
+ * @WARNING - Return only valid if [SeekStatusFaultFrames] is enabled
+ * See function SeekStatusFaultFrames
+ * See function EnableSeekStatusFaultFrames
+ */
+ CTR_Code GetSolenoidBlackList(UINT8 &status);
+
+ /* Get solenoid Blacklist status
+ * - Blacklisted solenoids cannot be enabled until PCM is power cycled
+ * @Return - CTR_Code - Error code (if any)
+ * @Param - idx - ID of solenoid [0,7]
+ * @Param - status - True if Solenoid is blacklisted, false if otherwise
+ * @WARNING - Return only valid if [SeekStatusFaultFrames] is enabled
+ * See function SeekStatusFaultFrames
+ * See function EnableSeekStatusFaultFrames
+ */
+ CTR_Code IsSolenoidBlacklisted(UINT8 idx, bool &status);
+
+ /* Return status of module enable/disable
+ * @Return - CTR_Code - Error code (if any)
+ * @Param - status - Returns TRUE if PCM is enabled, FALSE if disabled
+ */
+ CTR_Code isModuleEnabled(bool &status);
+
+ /* Get solenoid Blacklist status
+ * @Return - CTR_Code - Error code (if any)
+ * @Param - idx - ID of solenoid [0,7] to fire one shot pulse.
+ */
+ CTR_Code FireOneShotSolenoid(UINT8 idx);
+
+ /* Configure the pulse width of a solenoid channel for one-shot pulse.
+ * Preprogrammed pulsewidth is 10ms resolute and can be between 20ms and 5.1s.
+ * @Return - CTR_Code - Error code (if any)
+ * @Param - idx - ID of solenoid [0,7] to configure.
+ * @Param - durMs - pulse width in ms.
+ */
+ CTR_Code SetOneShotDurationMs(UINT8 idx,uint32_t durMs);
+
+};
+//------------------ C interface --------------------------------------------//
+extern "C" {
+ void * c_PCM_Init(void);
+ CTR_Code c_SetSolenoids(void * handle,uint8_t value,uint8_t mask);
+ CTR_Code c_SetClosedLoopControl(void * handle,INT8 param);
+ CTR_Code c_ClearStickyFaults(void * handle,INT8 param);
+ CTR_Code c_GetSolenoid(void * handle,UINT8 idx,INT8 * status);
+ CTR_Code c_GetPressure(void * handle,INT8 * status);
+ CTR_Code c_GetCompressor(void * handle,INT8 * status);
+ CTR_Code c_GetClosedLoopControl(void * handle,INT8 * status);
+ CTR_Code c_GetCompressorCurrent(void * handle,float * status);
+ CTR_Code c_GetSolenoidVoltage(void * handle,float*status);
+ CTR_Code c_GetHardwareFault(void * handle,INT8*status);
+ CTR_Code c_GetCompressorFault(void * handle,INT8*status);
+ CTR_Code c_GetSolenoidFault(void * handle,INT8*status);
+ CTR_Code c_GetCompressorStickyFault(void * handle,INT8*status);
+ CTR_Code c_GetSolenoidStickyFault(void * handle,INT8*status);
+ CTR_Code c_GetBatteryVoltage(void * handle,float*status);
+ void c_SetDeviceNumber_PCM(void * handle,UINT8 deviceNumber);
+ void c_EnableSeekStatusFrames(void * handle,INT8 enable);
+ void c_EnableSeekStatusFaultFrames(void * handle,INT8 enable);
+ void c_EnableSeekDebugFrames(void * handle,INT8 enable);
+ CTR_Code c_GetNumberOfFailedControlFrames(void * handle,UINT16*status);
+ CTR_Code c_GetSolenoidBlackList(void * handle,UINT8 *status);
+ CTR_Code c_IsSolenoidBlacklisted(void * handle,UINT8 idx,INT8*status);
+}
+#endif
diff --git a/aos/externals/allwpilib/hal/lib/Athena/ctre/PDP.cpp b/aos/externals/allwpilib/hal/lib/Athena/ctre/PDP.cpp
new file mode 100644
index 0000000..2fb4c8f
--- /dev/null
+++ b/aos/externals/allwpilib/hal/lib/Athena/ctre/PDP.cpp
@@ -0,0 +1,230 @@
+#include "PDP.h"
+#include "NetworkCommunication/CANSessionMux.h" //CAN Comm
+#include <string.h> // memset
+#include <unistd.h> // usleep
+
+#define STATUS_1 0x8041400
+#define STATUS_2 0x8041440
+#define STATUS_3 0x8041480
+#define STATUS_ENERGY 0x8041740
+
+#define CONTROL_1 0x08041C00 /* PDP_Control_ClearStats */
+
+#define EXPECTED_RESPONSE_TIMEOUT_MS (50)
+#define GET_STATUS1() CtreCanNode::recMsg<PdpStatus1_t> rx = GetRx<PdpStatus1_t>(STATUS_1,EXPECTED_RESPONSE_TIMEOUT_MS)
+#define GET_STATUS2() CtreCanNode::recMsg<PdpStatus2_t> rx = GetRx<PdpStatus2_t>(STATUS_2,EXPECTED_RESPONSE_TIMEOUT_MS)
+#define GET_STATUS3() CtreCanNode::recMsg<PdpStatus3_t> rx = GetRx<PdpStatus3_t>(STATUS_3,EXPECTED_RESPONSE_TIMEOUT_MS)
+#define GET_STATUS_ENERGY() CtreCanNode::recMsg<PDP_Status_Energy_t> rx = GetRx<PDP_Status_Energy_t>(STATUS_ENERGY,EXPECTED_RESPONSE_TIMEOUT_MS)
+
+/* encoder/decoders */
+typedef struct _PdpStatus1_t{
+ unsigned chan1_h8:8;
+ unsigned chan2_h6:6;
+ unsigned chan1_l2:2;
+ unsigned chan3_h4:4;
+ unsigned chan2_l4:4;
+ unsigned chan4_h2:2;
+ unsigned chan3_l6:6;
+ unsigned chan4_l8:8;
+ unsigned chan5_h8:8;
+ unsigned chan6_h6:6;
+ unsigned chan5_l2:2;
+ unsigned reserved4:4;
+ unsigned chan6_l4:4;
+}PdpStatus1_t;
+typedef struct _PdpStatus2_t{
+ unsigned chan7_h8:8;
+ unsigned chan8_h6:6;
+ unsigned chan7_l2:2;
+ unsigned chan9_h4:4;
+ unsigned chan8_l4:4;
+ unsigned chan10_h2:2;
+ unsigned chan9_l6:6;
+ unsigned chan10_l8:8;
+ unsigned chan11_h8:8;
+ unsigned chan12_h6:6;
+ unsigned chan11_l2:2;
+ unsigned reserved4:4;
+ unsigned chan12_l4:4;
+}PdpStatus2_t;
+typedef struct _PdpStatus3_t{
+ unsigned chan13_h8:8;
+ unsigned chan14_h6:6;
+ unsigned chan13_l2:2;
+ unsigned chan15_h4:4;
+ unsigned chan14_l4:4;
+ unsigned chan16_h2:2;
+ unsigned chan15_l6:6;
+ unsigned chan16_l8:8;
+ unsigned internalResBattery_mOhms:8;
+ unsigned busVoltage:8;
+ unsigned temp:8;
+}PdpStatus3_t;
+typedef struct _PDP_Status_Energy_t {
+ unsigned TmeasMs_likelywillbe20ms_:8;
+ unsigned TotalCurrent_125mAperunit_h8:8;
+ unsigned Power_125mWperunit_h4:4;
+ unsigned TotalCurrent_125mAperunit_l4:4;
+ unsigned Power_125mWperunit_m8:8;
+ unsigned Energy_125mWPerUnitXTmeas_h4:4;
+ unsigned Power_125mWperunit_l4:4;
+ unsigned Energy_125mWPerUnitXTmeas_mh8:8;
+ unsigned Energy_125mWPerUnitXTmeas_ml8:8;
+ unsigned Energy_125mWPerUnitXTmeas_l8:8;
+} PDP_Status_Energy_t ;
+
+PDP::PDP(UINT8 deviceNumber): CtreCanNode(deviceNumber)
+{
+ RegisterRx(STATUS_1 | deviceNumber );
+ RegisterRx(STATUS_2 | deviceNumber );
+ RegisterRx(STATUS_3 | deviceNumber );
+}
+/* PDP D'tor
+ */
+PDP::~PDP()
+{
+}
+
+CTR_Code PDP::GetChannelCurrent(UINT8 idx, double ¤t)
+{
+ CTR_Code retval = CTR_InvalidParamValue;
+ uint32_t raw = 0;
+
+ if(idx <= 5){
+ GET_STATUS1();
+ retval = rx.err;
+ switch(idx){
+ case 0: raw = ((uint32_t)rx->chan1_h8 << 2) | rx->chan1_l2; break;
+ case 1: raw = ((uint32_t)rx->chan2_h6 << 4) | rx->chan2_l4; break;
+ case 2: raw = ((uint32_t)rx->chan3_h4 << 6) | rx->chan3_l6; break;
+ case 3: raw = ((uint32_t)rx->chan4_h2 << 8) | rx->chan4_l8; break;
+ case 4: raw = ((uint32_t)rx->chan5_h8 << 2) | rx->chan5_l2; break;
+ case 5: raw = ((uint32_t)rx->chan6_h6 << 4) | rx->chan6_l4; break;
+ default: retval = CTR_InvalidParamValue; break;
+ }
+ }else if(idx <= 11){
+ GET_STATUS2();
+ retval = rx.err;
+ switch(idx){
+ case 6: raw = ((uint32_t)rx->chan7_h8 << 2) | rx->chan7_l2; break;
+ case 7: raw = ((uint32_t)rx->chan8_h6 << 4) | rx->chan8_l4; break;
+ case 8: raw = ((uint32_t)rx->chan9_h4 << 6) | rx->chan9_l6; break;
+ case 9: raw = ((uint32_t)rx->chan10_h2 << 8) | rx->chan10_l8; break;
+ case 10: raw = ((uint32_t)rx->chan11_h8 << 2) | rx->chan11_l2; break;
+ case 11: raw = ((uint32_t)rx->chan12_h6 << 4) | rx->chan12_l4; break;
+ default: retval = CTR_InvalidParamValue; break;
+ }
+ }else if(idx <= 15){
+ GET_STATUS3();
+ retval = rx.err;
+ switch(idx){
+ case 12: raw = ((uint32_t)rx->chan13_h8 << 2) | rx->chan13_l2; break;
+ case 13: raw = ((uint32_t)rx->chan14_h6 << 4) | rx->chan14_l4; break;
+ case 14: raw = ((uint32_t)rx->chan15_h4 << 6) | rx->chan15_l6; break;
+ case 15: raw = ((uint32_t)rx->chan16_h2 << 8) | rx->chan16_l8; break;
+ default: retval = CTR_InvalidParamValue; break;
+ }
+ }
+ /* convert to amps */
+ current = (double)raw * 0.125; /* 7.3 fixed pt value in Amps */
+ /* signal caller with success */
+ return retval;
+}
+CTR_Code PDP::GetVoltage(double &voltage)
+{
+ GET_STATUS3();
+ uint32_t raw = rx->busVoltage;
+ voltage = (double)raw * 0.05 + 4.0; /* 50mV per unit plus 4V. */;
+ return rx.err;
+}
+CTR_Code PDP::GetTemperature(double &tempC)
+{
+ GET_STATUS3();
+ uint32_t raw = rx->temp;
+ tempC = (double)raw * 1.03250836957542 - 67.8564500484966;
+ return rx.err;
+}
+CTR_Code PDP::GetTotalCurrent(double ¤tAmps)
+{
+ GET_STATUS_ENERGY();
+ uint32_t raw;
+ raw = rx->TotalCurrent_125mAperunit_h8;
+ raw <<= 4;
+ raw |= rx->TotalCurrent_125mAperunit_l4;
+ currentAmps = 0.125 * raw;
+ return rx.err;
+}
+CTR_Code PDP::GetTotalPower(double &powerWatts)
+{
+ GET_STATUS_ENERGY();
+ uint32_t raw;
+ raw = rx->Power_125mWperunit_h4;
+ raw <<= 8;
+ raw |= rx->Power_125mWperunit_m8;
+ raw <<= 4;
+ raw |= rx->Power_125mWperunit_l4;
+ powerWatts = 0.125 * raw;
+ return rx.err;
+}
+CTR_Code PDP::GetTotalEnergy(double &energyJoules)
+{
+ GET_STATUS_ENERGY();
+ uint32_t raw;
+ raw = rx->Energy_125mWPerUnitXTmeas_h4;
+ raw <<= 8;
+ raw |= rx->Energy_125mWPerUnitXTmeas_mh8;
+ raw <<= 8;
+ raw |= rx->Energy_125mWPerUnitXTmeas_ml8;
+ raw <<= 8;
+ raw |= rx->Energy_125mWPerUnitXTmeas_l8;
+ energyJoules = 0.125 * raw; /* mW integrated every TmeasMs */
+ energyJoules *= rx->TmeasMs_likelywillbe20ms_; /* multiplied by TmeasMs = joules */
+ return rx.err;
+}
+/* Clear sticky faults.
+ * @Return - CTR_Code - Error code (if any)
+ */
+CTR_Code PDP::ClearStickyFaults()
+{
+ int32_t status = 0;
+ uint8_t pdpControl[] = { 0x80 }; /* only bit set is ClearStickyFaults */
+ FRC_NetworkCommunication_CANSessionMux_sendMessage(CONTROL_1 | GetDeviceNumber(), pdpControl, sizeof(pdpControl), 0, &status);
+ if(status)
+ return CTR_TxFailed;
+ return CTR_OKAY;
+}
+
+/* Reset Energy Signals
+ * @Return - CTR_Code - Error code (if any)
+ */
+CTR_Code PDP::ResetEnergy()
+{
+ int32_t status = 0;
+ uint8_t pdpControl[] = { 0x40 }; /* only bit set is ResetEnergy */
+ FRC_NetworkCommunication_CANSessionMux_sendMessage(CONTROL_1 | GetDeviceNumber(), pdpControl, sizeof(pdpControl), 0, &status);
+ if(status)
+ return CTR_TxFailed;
+ return CTR_OKAY;
+}
+//------------------ C interface --------------------------------------------//
+extern "C" {
+ void * c_PDP_Init(void)
+ {
+ return new PDP();
+ }
+ CTR_Code c_GetChannelCurrent(void * handle,UINT8 idx, double *status)
+ {
+ return ((PDP*)handle)-> GetChannelCurrent(idx,*status);
+ }
+ CTR_Code c_GetVoltage(void * handle,double *status)
+ {
+ return ((PDP*)handle)-> GetVoltage(*status);
+ }
+ CTR_Code c_GetTemperature(void * handle,double *status)
+ {
+ return ((PDP*)handle)-> GetTemperature(*status);
+ }
+ void c_SetDeviceNumber_PDP(void * handle,UINT8 deviceNumber)
+ {
+ }
+}
diff --git a/aos/externals/allwpilib/hal/lib/Athena/ctre/PDP.h b/aos/externals/allwpilib/hal/lib/Athena/ctre/PDP.h
new file mode 100644
index 0000000..a289d1a
--- /dev/null
+++ b/aos/externals/allwpilib/hal/lib/Athena/ctre/PDP.h
@@ -0,0 +1,64 @@
+#ifndef PDP_H_
+#define PDP_H_
+#include "ctre.h" //BIT Defines + Typedefs
+#include <NetworkCommunication/CANSessionMux.h> //CAN Comm
+#include "CtreCanNode.h"
+#include <pthread.h>
+class PDP : public CtreCanNode
+{
+public:
+ /* Get PDP Channel Current
+ *
+ * @Param - deviceNumber - Device ID for PDP. Factory default is 60. Function defaults to 60.
+ */
+ PDP(UINT8 deviceNumber=0);
+ ~PDP();
+ /* Get PDP Channel Current
+ *
+ * @Return - CTR_Code - Error code (if any)
+ *
+ * @Param - idx - ID of channel to return current for (channels 1-16)
+ *
+ * @Param - status - Current of channel 'idx' in Amps (A)
+ */
+ CTR_Code GetChannelCurrent(UINT8 idx, double &status);
+
+ /* Get Bus Voltage of PDP
+ *
+ * @Return - CTR_Code - Error code (if any)
+ *
+ * @Param - status - Voltage (V) across PDP
+ */
+ CTR_Code GetVoltage(double &status);
+
+ /* Get Temperature of PDP
+ *
+ * @Return - CTR_Code - Error code (if any)
+ *
+ * @Param - status - Temperature of PDP in Centigrade / Celcius (C)
+ */
+ CTR_Code GetTemperature(double &status);
+
+ CTR_Code GetTotalCurrent(double ¤tAmps);
+ CTR_Code GetTotalPower(double &powerWatts);
+ CTR_Code GetTotalEnergy(double &energyJoules);
+ /* Clear sticky faults.
+ * @Return - CTR_Code - Error code (if any)
+ */
+ CTR_Code ClearStickyFaults();
+
+ /* Reset Energy Signals
+ * @Return - CTR_Code - Error code (if any)
+ */
+ CTR_Code ResetEnergy();
+private:
+ uint64_t ReadCurrents(uint8_t api);
+};
+extern "C" {
+ void * c_PDP_Init();
+ CTR_Code c_GetChannelCurrent(void * handle,UINT8 idx, double *status);
+ CTR_Code c_GetVoltage(void * handle,double *status);
+ CTR_Code c_GetTemperature(void * handle,double *status);
+ void c_SetDeviceNumber_PDP(void * handle,UINT8 deviceNumber);
+}
+#endif /* PDP_H_ */
diff --git a/aos/externals/allwpilib/hal/lib/Athena/ctre/ctre.h b/aos/externals/allwpilib/hal/lib/Athena/ctre/ctre.h
new file mode 100644
index 0000000..c2d3f69
--- /dev/null
+++ b/aos/externals/allwpilib/hal/lib/Athena/ctre/ctre.h
@@ -0,0 +1,50 @@
+#ifndef GLOBAL_H
+#define GLOBAL_H
+
+//Bit Defines
+#define BIT0 0x01
+#define BIT1 0x02
+#define BIT2 0x04
+#define BIT3 0x08
+#define BIT4 0x10
+#define BIT5 0x20
+#define BIT6 0x40
+#define BIT7 0x80
+#define BIT8 0x0100
+#define BIT9 0x0200
+#define BIT10 0x0400
+#define BIT11 0x0800
+#define BIT12 0x1000
+#define BIT13 0x2000
+#define BIT14 0x4000
+#define BIT15 0x8000
+
+//Signed
+typedef signed char INT8;
+typedef signed short INT16;
+typedef signed int INT32;
+typedef signed long long INT64;
+
+//Unsigned
+typedef unsigned char UINT8;
+typedef unsigned short UINT16;
+typedef unsigned int UINT32;
+typedef unsigned long long UINT64;
+
+//Other
+typedef unsigned char UCHAR;
+typedef unsigned short USHORT;
+typedef unsigned int UINT;
+typedef unsigned long ULONG;
+
+typedef enum {
+ CTR_OKAY, //!< No Error - Function executed as expected
+ CTR_RxTimeout, //!< CAN frame has not been received within specified period of time.
+ CTR_TxTimeout, //!< Not used.
+ CTR_InvalidParamValue, //!< Caller passed an invalid param
+ CTR_UnexpectedArbId, //!< Specified CAN Id is invalid.
+ CTR_TxFailed, //!< Could not transmit the CAN frame.
+ CTR_SigNotUpdated, //!< Have not received an value response for signal.
+}CTR_Code;
+
+#endif
diff --git a/aos/externals/allwpilib/hal/lib/Athena/i2clib/i2c-lib.h b/aos/externals/allwpilib/hal/lib/Athena/i2clib/i2c-lib.h
new file mode 100644
index 0000000..b34cb33
--- /dev/null
+++ b/aos/externals/allwpilib/hal/lib/Athena/i2clib/i2c-lib.h
@@ -0,0 +1,16 @@
+#ifndef __I2C_LIB_H__
+#define __I2C_LIB_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+int i2clib_open(const char *device);
+void i2clib_close(int handle);
+int i2clib_read(int handle, uint8_t dev_addr, char *recv_buf, int32_t recv_size);
+int i2clib_write(int handle, uint8_t dev_addr, const char *send_buf, int32_t send_size);
+int i2clib_writeread(int handle, uint8_t dev_addr, const char *send_buf, int32_t send_size, char *recv_buf, int32_t recv_size);
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __I2C_LIB_H__ */
\ No newline at end of file
diff --git a/aos/externals/allwpilib/hal/lib/Athena/i2clib/i2clib/environs.h b/aos/externals/allwpilib/hal/lib/Athena/i2clib/i2clib/environs.h
new file mode 100644
index 0000000..d250585
--- /dev/null
+++ b/aos/externals/allwpilib/hal/lib/Athena/i2clib/i2clib/environs.h
@@ -0,0 +1,66 @@
+/*!
+ \file environs.h
+ \brief Defines export symbols and namespace aliases
+*/
+/*
+ Copyright (c) 2014,
+ National Instruments Corporation.
+ All rights reserved.
+
+ File: $Id: //lvaddon/FIRST/FRC/trunk/2015/tools/frcrti/export/1.0/1.0.0a6/includes/wpilib/i2clib/i2clib/environs.h#1 $
+ Author: nipg.pl
+ Originated: Mon Feb 10 10:39:42 2014
+*/
+
+#ifndef ___i2clib_environs_h___
+#define ___i2clib_environs_h___
+
+#include <nibuild/platform.h>
+
+/*
+ * kI2CLIBExportSymbols directs the build to export symbols modified by
+ * the kI2CLIBExport keyword. kI2CLIBNoExportSymbols directs the build to not
+ * import or export symbols modified by the kI2CLIBExport keyword. If
+ * neither of these are defined, the symbols modified by kI2CLIBExport are
+ * imported. These should be defined only when building the component,
+ * so clients do not need to trouble themselves with it.
+ */
+#if defined(kI2CLIBExportSymbols)
+ #define kI2CLIBExport kNIExport
+ #define kI2CLIBExportPre kNIExportPre
+ #define kI2CLIBExportPost kNIExportPost
+ #define kI2CLIBExportInline kNIExportInline
+ #define kI2CLIBExportData kNIExportData
+#elif defined(kI2CLIBNoExportSymbols)
+ #define kI2CLIBExport
+ #define kI2CLIBExportPre
+ #define kI2CLIBExportPost
+ #define kI2CLIBExportInline
+ #define kI2CLIBExportData
+#else
+ #define kI2CLIBExport kNIImport
+ #define kI2CLIBExportPre kNIImportPre
+ #define kI2CLIBExportPost kNIImportPost
+ #define kI2CLIBExportInline kNIImportInline
+ #define kI2CLIBExportData kNIImportData
+#endif
+
+// namespace declarations for aliasing ...
+
+#ifdef __cplusplus
+
+
+/*!
+ \namespace nI2CLIB_1_0
+ \brief i2c user-mode library Release 1.0
+*/
+namespace nI2CLIB_1_0
+{
+ // current versioned namespace aliases used by this package
+
+}
+
+#endif // __cplusplus
+
+#endif // ___i2clib_environs_h___
+
diff --git a/aos/externals/allwpilib/hal/lib/Athena/spilib/spi-lib.h b/aos/externals/allwpilib/hal/lib/Athena/spilib/spi-lib.h
new file mode 100644
index 0000000..b5c2a7a
--- /dev/null
+++ b/aos/externals/allwpilib/hal/lib/Athena/spilib/spi-lib.h
@@ -0,0 +1,19 @@
+#ifndef __SPI_LIB_H__
+#define __SPI_LIB_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+int spilib_open(const char *device);
+void spilib_close(int handle);
+int spilib_setspeed(int handle, uint32_t speed);
+int spilib_setbitsperword(int handle, uint8_t bpw);
+int spilib_setopts(int handle, int msb_first, int sample_on_trailing, int clk_idle_high);
+int spilib_read(int handle, char *recv_buf, int32_t size);
+int spilib_write(int handle, const char *send_buf, int32_t size);
+int spilib_writeread(int handle, const char *send_buf, char *recv_buf, int32_t size);
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __SPI_LIB_H__ */
\ No newline at end of file
diff --git a/aos/externals/allwpilib/hal/lib/Athena/visa/visa.h b/aos/externals/allwpilib/hal/lib/Athena/visa/visa.h
new file mode 100644
index 0000000..3c6ad30
--- /dev/null
+++ b/aos/externals/allwpilib/hal/lib/Athena/visa/visa.h
@@ -0,0 +1,1064 @@
+/*---------------------------------------------------------------------------*/
+/* Distributed by IVI Foundation Inc. */
+/* Contains National Instruments extensions. */
+/* Do not modify the contents of this file. */
+/*---------------------------------------------------------------------------*/
+/* */
+/* Title : VISA.H */
+/* Date : 10-09-2006 */
+/* Purpose : Include file for the VISA Library 4.0 specification */
+/* */
+/*---------------------------------------------------------------------------*/
+/* When using NI-VISA extensions, you must link with the VISA library that */
+/* comes with NI-VISA. Currently, the extensions provided by NI-VISA are: */
+/* */
+/* PXI (Compact PCI eXtensions for Instrumentation) and PCI support. To use */
+/* this, you must define the macro NIVISA_PXI before including this header. */
+/* You must also create an INF file with the VISA Driver Development Wizard. */
+/* */
+/* A fast set of macros for viPeekXX/viPokeXX that guarantees binary */
+/* compatibility with other implementations of VISA. To use this, you must */
+/* define the macro NIVISA_PEEKPOKE before including this header. */
+/* */
+/* Support for USB devices that do not conform to a specific class. To use */
+/* this, you must define the macro NIVISA_USB before including this header. */
+/* You must also create an INF file with the VISA Driver Development Wizard. */
+/*---------------------------------------------------------------------------*/
+
+#ifndef __VISA_HEADER__
+#define __VISA_HEADER__
+
+#include <stdarg.h>
+
+#if !defined(__VISATYPE_HEADER__)
+#include "visatype.h"
+#endif
+
+#define VI_SPEC_VERSION (0x00400000UL)
+
+#if defined(__cplusplus) || defined(__cplusplus__)
+ extern "C" {
+#endif
+
+#if defined(_CVI_)
+#pragma EnableLibraryRuntimeChecking
+#endif
+
+/*- VISA Types --------------------------------------------------------------*/
+
+typedef ViObject ViEvent;
+typedef ViEvent _VI_PTR ViPEvent;
+typedef ViObject ViFindList;
+typedef ViFindList _VI_PTR ViPFindList;
+
+#if defined(_VI_INT64_UINT64_DEFINED) && defined(_VISA_ENV_IS_64_BIT)
+typedef ViUInt64 ViBusAddress;
+typedef ViUInt64 ViBusSize;
+typedef ViUInt64 ViAttrState;
+#else
+typedef ViUInt32 ViBusAddress;
+typedef ViUInt32 ViBusSize;
+typedef ViUInt32 ViAttrState;
+#endif
+
+#if defined(_VI_INT64_UINT64_DEFINED)
+typedef ViUInt64 ViBusAddress64;
+typedef ViBusAddress64 _VI_PTR ViPBusAddress64;
+#endif
+
+typedef ViUInt32 ViEventType;
+typedef ViEventType _VI_PTR ViPEventType;
+typedef ViEventType _VI_PTR ViAEventType;
+typedef void _VI_PTR ViPAttrState;
+typedef ViAttr _VI_PTR ViPAttr;
+typedef ViAttr _VI_PTR ViAAttr;
+
+typedef ViString ViKeyId;
+typedef ViPString ViPKeyId;
+typedef ViUInt32 ViJobId;
+typedef ViJobId _VI_PTR ViPJobId;
+typedef ViUInt32 ViAccessMode;
+typedef ViAccessMode _VI_PTR ViPAccessMode;
+typedef ViBusAddress _VI_PTR ViPBusAddress;
+typedef ViUInt32 ViEventFilter;
+
+typedef va_list ViVAList;
+
+typedef ViStatus (_VI_FUNCH _VI_PTR ViHndlr)
+ (ViSession vi, ViEventType eventType, ViEvent event, ViAddr userHandle);
+
+/*- Resource Manager Functions and Operations -------------------------------*/
+
+ViStatus _VI_FUNC viOpenDefaultRM (ViPSession vi);
+ViStatus _VI_FUNC viFindRsrc (ViSession sesn, ViString expr, ViPFindList vi,
+ ViPUInt32 retCnt, ViChar _VI_FAR desc[]);
+ViStatus _VI_FUNC viFindNext (ViFindList vi, ViChar _VI_FAR desc[]);
+ViStatus _VI_FUNC viParseRsrc (ViSession rmSesn, ViRsrc rsrcName,
+ ViPUInt16 intfType, ViPUInt16 intfNum);
+ViStatus _VI_FUNC viParseRsrcEx (ViSession rmSesn, ViRsrc rsrcName, ViPUInt16 intfType,
+ ViPUInt16 intfNum, ViChar _VI_FAR rsrcClass[],
+ ViChar _VI_FAR expandedUnaliasedName[],
+ ViChar _VI_FAR aliasIfExists[]);
+ViStatus _VI_FUNC viOpen (ViSession sesn, ViRsrc name, ViAccessMode mode,
+ ViUInt32 timeout, ViPSession vi);
+
+/*- Resource Template Operations --------------------------------------------*/
+
+ViStatus _VI_FUNC viClose (ViObject vi);
+ViStatus _VI_FUNC viSetAttribute (ViObject vi, ViAttr attrName, ViAttrState attrValue);
+ViStatus _VI_FUNC viGetAttribute (ViObject vi, ViAttr attrName, void _VI_PTR attrValue);
+ViStatus _VI_FUNC viStatusDesc (ViObject vi, ViStatus status, ViChar _VI_FAR desc[]);
+ViStatus _VI_FUNC viTerminate (ViObject vi, ViUInt16 degree, ViJobId jobId);
+
+ViStatus _VI_FUNC viLock (ViSession vi, ViAccessMode lockType, ViUInt32 timeout,
+ ViKeyId requestedKey, ViChar _VI_FAR accessKey[]);
+ViStatus _VI_FUNC viUnlock (ViSession vi);
+ViStatus _VI_FUNC viEnableEvent (ViSession vi, ViEventType eventType, ViUInt16 mechanism,
+ ViEventFilter context);
+ViStatus _VI_FUNC viDisableEvent (ViSession vi, ViEventType eventType, ViUInt16 mechanism);
+ViStatus _VI_FUNC viDiscardEvents (ViSession vi, ViEventType eventType, ViUInt16 mechanism);
+ViStatus _VI_FUNC viWaitOnEvent (ViSession vi, ViEventType inEventType, ViUInt32 timeout,
+ ViPEventType outEventType, ViPEvent outContext);
+ViStatus _VI_FUNC viInstallHandler(ViSession vi, ViEventType eventType, ViHndlr handler,
+ ViAddr userHandle);
+ViStatus _VI_FUNC viUninstallHandler(ViSession vi, ViEventType eventType, ViHndlr handler,
+ ViAddr userHandle);
+
+/*- Basic I/O Operations ----------------------------------------------------*/
+
+ViStatus _VI_FUNC viRead (ViSession vi, ViPBuf buf, ViUInt32 cnt, ViPUInt32 retCnt);
+ViStatus _VI_FUNC viReadAsync (ViSession vi, ViPBuf buf, ViUInt32 cnt, ViPJobId jobId);
+ViStatus _VI_FUNC viReadToFile (ViSession vi, ViConstString filename, ViUInt32 cnt,
+ ViPUInt32 retCnt);
+ViStatus _VI_FUNC viWrite (ViSession vi, ViBuf buf, ViUInt32 cnt, ViPUInt32 retCnt);
+ViStatus _VI_FUNC viWriteAsync (ViSession vi, ViBuf buf, ViUInt32 cnt, ViPJobId jobId);
+ViStatus _VI_FUNC viWriteFromFile (ViSession vi, ViConstString filename, ViUInt32 cnt,
+ ViPUInt32 retCnt);
+ViStatus _VI_FUNC viAssertTrigger (ViSession vi, ViUInt16 protocol);
+ViStatus _VI_FUNC viReadSTB (ViSession vi, ViPUInt16 status);
+ViStatus _VI_FUNC viClear (ViSession vi);
+
+/*- Formatted and Buffered I/O Operations -----------------------------------*/
+
+ViStatus _VI_FUNC viSetBuf (ViSession vi, ViUInt16 mask, ViUInt32 size);
+ViStatus _VI_FUNC viFlush (ViSession vi, ViUInt16 mask);
+
+ViStatus _VI_FUNC viBufWrite (ViSession vi, ViBuf buf, ViUInt32 cnt, ViPUInt32 retCnt);
+ViStatus _VI_FUNC viBufRead (ViSession vi, ViPBuf buf, ViUInt32 cnt, ViPUInt32 retCnt);
+
+ViStatus _VI_FUNCC viPrintf (ViSession vi, ViString writeFmt, ...);
+ViStatus _VI_FUNC viVPrintf (ViSession vi, ViString writeFmt, ViVAList params);
+ViStatus _VI_FUNCC viSPrintf (ViSession vi, ViPBuf buf, ViString writeFmt, ...);
+ViStatus _VI_FUNC viVSPrintf (ViSession vi, ViPBuf buf, ViString writeFmt,
+ ViVAList parms);
+
+ViStatus _VI_FUNCC viScanf (ViSession vi, ViString readFmt, ...);
+ViStatus _VI_FUNC viVScanf (ViSession vi, ViString readFmt, ViVAList params);
+ViStatus _VI_FUNCC viSScanf (ViSession vi, ViBuf buf, ViString readFmt, ...);
+ViStatus _VI_FUNC viVSScanf (ViSession vi, ViBuf buf, ViString readFmt,
+ ViVAList parms);
+
+ViStatus _VI_FUNCC viQueryf (ViSession vi, ViString writeFmt, ViString readFmt, ...);
+ViStatus _VI_FUNC viVQueryf (ViSession vi, ViString writeFmt, ViString readFmt,
+ ViVAList params);
+
+/*- Memory I/O Operations ---------------------------------------------------*/
+
+ViStatus _VI_FUNC viIn8 (ViSession vi, ViUInt16 space,
+ ViBusAddress offset, ViPUInt8 val8);
+ViStatus _VI_FUNC viOut8 (ViSession vi, ViUInt16 space,
+ ViBusAddress offset, ViUInt8 val8);
+ViStatus _VI_FUNC viIn16 (ViSession vi, ViUInt16 space,
+ ViBusAddress offset, ViPUInt16 val16);
+ViStatus _VI_FUNC viOut16 (ViSession vi, ViUInt16 space,
+ ViBusAddress offset, ViUInt16 val16);
+ViStatus _VI_FUNC viIn32 (ViSession vi, ViUInt16 space,
+ ViBusAddress offset, ViPUInt32 val32);
+ViStatus _VI_FUNC viOut32 (ViSession vi, ViUInt16 space,
+ ViBusAddress offset, ViUInt32 val32);
+
+#if defined(_VI_INT64_UINT64_DEFINED)
+ViStatus _VI_FUNC viIn64 (ViSession vi, ViUInt16 space,
+ ViBusAddress offset, ViPUInt64 val64);
+ViStatus _VI_FUNC viOut64 (ViSession vi, ViUInt16 space,
+ ViBusAddress offset, ViUInt64 val64);
+
+ViStatus _VI_FUNC viIn8Ex (ViSession vi, ViUInt16 space,
+ ViBusAddress64 offset, ViPUInt8 val8);
+ViStatus _VI_FUNC viOut8Ex (ViSession vi, ViUInt16 space,
+ ViBusAddress64 offset, ViUInt8 val8);
+ViStatus _VI_FUNC viIn16Ex (ViSession vi, ViUInt16 space,
+ ViBusAddress64 offset, ViPUInt16 val16);
+ViStatus _VI_FUNC viOut16Ex (ViSession vi, ViUInt16 space,
+ ViBusAddress64 offset, ViUInt16 val16);
+ViStatus _VI_FUNC viIn32Ex (ViSession vi, ViUInt16 space,
+ ViBusAddress64 offset, ViPUInt32 val32);
+ViStatus _VI_FUNC viOut32Ex (ViSession vi, ViUInt16 space,
+ ViBusAddress64 offset, ViUInt32 val32);
+ViStatus _VI_FUNC viIn64Ex (ViSession vi, ViUInt16 space,
+ ViBusAddress64 offset, ViPUInt64 val64);
+ViStatus _VI_FUNC viOut64Ex (ViSession vi, ViUInt16 space,
+ ViBusAddress64 offset, ViUInt64 val64);
+#endif
+
+ViStatus _VI_FUNC viMoveIn8 (ViSession vi, ViUInt16 space, ViBusAddress offset,
+ ViBusSize length, ViAUInt8 buf8);
+ViStatus _VI_FUNC viMoveOut8 (ViSession vi, ViUInt16 space, ViBusAddress offset,
+ ViBusSize length, ViAUInt8 buf8);
+ViStatus _VI_FUNC viMoveIn16 (ViSession vi, ViUInt16 space, ViBusAddress offset,
+ ViBusSize length, ViAUInt16 buf16);
+ViStatus _VI_FUNC viMoveOut16 (ViSession vi, ViUInt16 space, ViBusAddress offset,
+ ViBusSize length, ViAUInt16 buf16);
+ViStatus _VI_FUNC viMoveIn32 (ViSession vi, ViUInt16 space, ViBusAddress offset,
+ ViBusSize length, ViAUInt32 buf32);
+ViStatus _VI_FUNC viMoveOut32 (ViSession vi, ViUInt16 space, ViBusAddress offset,
+ ViBusSize length, ViAUInt32 buf32);
+
+#if defined(_VI_INT64_UINT64_DEFINED)
+ViStatus _VI_FUNC viMoveIn64 (ViSession vi, ViUInt16 space, ViBusAddress offset,
+ ViBusSize length, ViAUInt64 buf64);
+ViStatus _VI_FUNC viMoveOut64 (ViSession vi, ViUInt16 space, ViBusAddress offset,
+ ViBusSize length, ViAUInt64 buf64);
+
+ViStatus _VI_FUNC viMoveIn8Ex (ViSession vi, ViUInt16 space, ViBusAddress64 offset,
+ ViBusSize length, ViAUInt8 buf8);
+ViStatus _VI_FUNC viMoveOut8Ex (ViSession vi, ViUInt16 space, ViBusAddress64 offset,
+ ViBusSize length, ViAUInt8 buf8);
+ViStatus _VI_FUNC viMoveIn16Ex (ViSession vi, ViUInt16 space, ViBusAddress64 offset,
+ ViBusSize length, ViAUInt16 buf16);
+ViStatus _VI_FUNC viMoveOut16Ex (ViSession vi, ViUInt16 space, ViBusAddress64 offset,
+ ViBusSize length, ViAUInt16 buf16);
+ViStatus _VI_FUNC viMoveIn32Ex (ViSession vi, ViUInt16 space, ViBusAddress64 offset,
+ ViBusSize length, ViAUInt32 buf32);
+ViStatus _VI_FUNC viMoveOut32Ex (ViSession vi, ViUInt16 space, ViBusAddress64 offset,
+ ViBusSize length, ViAUInt32 buf32);
+ViStatus _VI_FUNC viMoveIn64Ex (ViSession vi, ViUInt16 space, ViBusAddress64 offset,
+ ViBusSize length, ViAUInt64 buf64);
+ViStatus _VI_FUNC viMoveOut64Ex (ViSession vi, ViUInt16 space, ViBusAddress64 offset,
+ ViBusSize length, ViAUInt64 buf64);
+#endif
+
+ViStatus _VI_FUNC viMove (ViSession vi, ViUInt16 srcSpace, ViBusAddress srcOffset,
+ ViUInt16 srcWidth, ViUInt16 destSpace,
+ ViBusAddress destOffset, ViUInt16 destWidth,
+ ViBusSize srcLength);
+ViStatus _VI_FUNC viMoveAsync (ViSession vi, ViUInt16 srcSpace, ViBusAddress srcOffset,
+ ViUInt16 srcWidth, ViUInt16 destSpace,
+ ViBusAddress destOffset, ViUInt16 destWidth,
+ ViBusSize srcLength, ViPJobId jobId);
+
+#if defined(_VI_INT64_UINT64_DEFINED)
+ViStatus _VI_FUNC viMoveEx (ViSession vi, ViUInt16 srcSpace, ViBusAddress64 srcOffset,
+ ViUInt16 srcWidth, ViUInt16 destSpace,
+ ViBusAddress64 destOffset, ViUInt16 destWidth,
+ ViBusSize srcLength);
+ViStatus _VI_FUNC viMoveAsyncEx (ViSession vi, ViUInt16 srcSpace, ViBusAddress64 srcOffset,
+ ViUInt16 srcWidth, ViUInt16 destSpace,
+ ViBusAddress64 destOffset, ViUInt16 destWidth,
+ ViBusSize srcLength, ViPJobId jobId);
+#endif
+
+ViStatus _VI_FUNC viMapAddress (ViSession vi, ViUInt16 mapSpace, ViBusAddress mapOffset,
+ ViBusSize mapSize, ViBoolean access,
+ ViAddr suggested, ViPAddr address);
+ViStatus _VI_FUNC viUnmapAddress (ViSession vi);
+
+#if defined(_VI_INT64_UINT64_DEFINED)
+ViStatus _VI_FUNC viMapAddressEx (ViSession vi, ViUInt16 mapSpace, ViBusAddress64 mapOffset,
+ ViBusSize mapSize, ViBoolean access,
+ ViAddr suggested, ViPAddr address);
+#endif
+
+void _VI_FUNC viPeek8 (ViSession vi, ViAddr address, ViPUInt8 val8);
+void _VI_FUNC viPoke8 (ViSession vi, ViAddr address, ViUInt8 val8);
+void _VI_FUNC viPeek16 (ViSession vi, ViAddr address, ViPUInt16 val16);
+void _VI_FUNC viPoke16 (ViSession vi, ViAddr address, ViUInt16 val16);
+void _VI_FUNC viPeek32 (ViSession vi, ViAddr address, ViPUInt32 val32);
+void _VI_FUNC viPoke32 (ViSession vi, ViAddr address, ViUInt32 val32);
+
+#if defined(_VI_INT64_UINT64_DEFINED)
+void _VI_FUNC viPeek64 (ViSession vi, ViAddr address, ViPUInt64 val64);
+void _VI_FUNC viPoke64 (ViSession vi, ViAddr address, ViUInt64 val64);
+#endif
+
+/*- Shared Memory Operations ------------------------------------------------*/
+
+ViStatus _VI_FUNC viMemAlloc (ViSession vi, ViBusSize size, ViPBusAddress offset);
+ViStatus _VI_FUNC viMemFree (ViSession vi, ViBusAddress offset);
+
+#if defined(_VI_INT64_UINT64_DEFINED)
+ViStatus _VI_FUNC viMemAllocEx (ViSession vi, ViBusSize size, ViPBusAddress64 offset);
+ViStatus _VI_FUNC viMemFreeEx (ViSession vi, ViBusAddress64 offset);
+#endif
+
+/*- Interface Specific Operations -------------------------------------------*/
+
+ViStatus _VI_FUNC viGpibControlREN(ViSession vi, ViUInt16 mode);
+ViStatus _VI_FUNC viGpibControlATN(ViSession vi, ViUInt16 mode);
+ViStatus _VI_FUNC viGpibSendIFC (ViSession vi);
+ViStatus _VI_FUNC viGpibCommand (ViSession vi, ViBuf cmd, ViUInt32 cnt, ViPUInt32 retCnt);
+ViStatus _VI_FUNC viGpibPassControl(ViSession vi, ViUInt16 primAddr, ViUInt16 secAddr);
+
+ViStatus _VI_FUNC viVxiCommandQuery(ViSession vi, ViUInt16 mode, ViUInt32 cmd,
+ ViPUInt32 response);
+ViStatus _VI_FUNC viAssertUtilSignal(ViSession vi, ViUInt16 line);
+ViStatus _VI_FUNC viAssertIntrSignal(ViSession vi, ViInt16 mode, ViUInt32 statusID);
+ViStatus _VI_FUNC viMapTrigger (ViSession vi, ViInt16 trigSrc, ViInt16 trigDest,
+ ViUInt16 mode);
+ViStatus _VI_FUNC viUnmapTrigger (ViSession vi, ViInt16 trigSrc, ViInt16 trigDest);
+ViStatus _VI_FUNC viUsbControlOut (ViSession vi, ViInt16 bmRequestType, ViInt16 bRequest,
+ ViUInt16 wValue, ViUInt16 wIndex, ViUInt16 wLength,
+ ViBuf buf);
+ViStatus _VI_FUNC viUsbControlIn (ViSession vi, ViInt16 bmRequestType, ViInt16 bRequest,
+ ViUInt16 wValue, ViUInt16 wIndex, ViUInt16 wLength,
+ ViPBuf buf, ViPUInt16 retCnt);
+
+/*- Attributes (platform independent size) ----------------------------------*/
+
+#define VI_ATTR_RSRC_CLASS (0xBFFF0001UL)
+#define VI_ATTR_RSRC_NAME (0xBFFF0002UL)
+#define VI_ATTR_RSRC_IMPL_VERSION (0x3FFF0003UL)
+#define VI_ATTR_RSRC_LOCK_STATE (0x3FFF0004UL)
+#define VI_ATTR_MAX_QUEUE_LENGTH (0x3FFF0005UL)
+#define VI_ATTR_USER_DATA_32 (0x3FFF0007UL)
+#define VI_ATTR_FDC_CHNL (0x3FFF000DUL)
+#define VI_ATTR_FDC_MODE (0x3FFF000FUL)
+#define VI_ATTR_FDC_GEN_SIGNAL_EN (0x3FFF0011UL)
+#define VI_ATTR_FDC_USE_PAIR (0x3FFF0013UL)
+#define VI_ATTR_SEND_END_EN (0x3FFF0016UL)
+#define VI_ATTR_TERMCHAR (0x3FFF0018UL)
+#define VI_ATTR_TMO_VALUE (0x3FFF001AUL)
+#define VI_ATTR_GPIB_READDR_EN (0x3FFF001BUL)
+#define VI_ATTR_IO_PROT (0x3FFF001CUL)
+#define VI_ATTR_DMA_ALLOW_EN (0x3FFF001EUL)
+#define VI_ATTR_ASRL_BAUD (0x3FFF0021UL)
+#define VI_ATTR_ASRL_DATA_BITS (0x3FFF0022UL)
+#define VI_ATTR_ASRL_PARITY (0x3FFF0023UL)
+#define VI_ATTR_ASRL_STOP_BITS (0x3FFF0024UL)
+#define VI_ATTR_ASRL_FLOW_CNTRL (0x3FFF0025UL)
+#define VI_ATTR_RD_BUF_OPER_MODE (0x3FFF002AUL)
+#define VI_ATTR_RD_BUF_SIZE (0x3FFF002BUL)
+#define VI_ATTR_WR_BUF_OPER_MODE (0x3FFF002DUL)
+#define VI_ATTR_WR_BUF_SIZE (0x3FFF002EUL)
+#define VI_ATTR_SUPPRESS_END_EN (0x3FFF0036UL)
+#define VI_ATTR_TERMCHAR_EN (0x3FFF0038UL)
+#define VI_ATTR_DEST_ACCESS_PRIV (0x3FFF0039UL)
+#define VI_ATTR_DEST_BYTE_ORDER (0x3FFF003AUL)
+#define VI_ATTR_SRC_ACCESS_PRIV (0x3FFF003CUL)
+#define VI_ATTR_SRC_BYTE_ORDER (0x3FFF003DUL)
+#define VI_ATTR_SRC_INCREMENT (0x3FFF0040UL)
+#define VI_ATTR_DEST_INCREMENT (0x3FFF0041UL)
+#define VI_ATTR_WIN_ACCESS_PRIV (0x3FFF0045UL)
+#define VI_ATTR_WIN_BYTE_ORDER (0x3FFF0047UL)
+#define VI_ATTR_GPIB_ATN_STATE (0x3FFF0057UL)
+#define VI_ATTR_GPIB_ADDR_STATE (0x3FFF005CUL)
+#define VI_ATTR_GPIB_CIC_STATE (0x3FFF005EUL)
+#define VI_ATTR_GPIB_NDAC_STATE (0x3FFF0062UL)
+#define VI_ATTR_GPIB_SRQ_STATE (0x3FFF0067UL)
+#define VI_ATTR_GPIB_SYS_CNTRL_STATE (0x3FFF0068UL)
+#define VI_ATTR_GPIB_HS488_CBL_LEN (0x3FFF0069UL)
+#define VI_ATTR_CMDR_LA (0x3FFF006BUL)
+#define VI_ATTR_VXI_DEV_CLASS (0x3FFF006CUL)
+#define VI_ATTR_MAINFRAME_LA (0x3FFF0070UL)
+#define VI_ATTR_MANF_NAME (0xBFFF0072UL)
+#define VI_ATTR_MODEL_NAME (0xBFFF0077UL)
+#define VI_ATTR_VXI_VME_INTR_STATUS (0x3FFF008BUL)
+#define VI_ATTR_VXI_TRIG_STATUS (0x3FFF008DUL)
+#define VI_ATTR_VXI_VME_SYSFAIL_STATE (0x3FFF0094UL)
+#define VI_ATTR_WIN_BASE_ADDR_32 (0x3FFF0098UL)
+#define VI_ATTR_WIN_SIZE_32 (0x3FFF009AUL)
+#define VI_ATTR_ASRL_AVAIL_NUM (0x3FFF00ACUL)
+#define VI_ATTR_MEM_BASE_32 (0x3FFF00ADUL)
+#define VI_ATTR_ASRL_CTS_STATE (0x3FFF00AEUL)
+#define VI_ATTR_ASRL_DCD_STATE (0x3FFF00AFUL)
+#define VI_ATTR_ASRL_DSR_STATE (0x3FFF00B1UL)
+#define VI_ATTR_ASRL_DTR_STATE (0x3FFF00B2UL)
+#define VI_ATTR_ASRL_END_IN (0x3FFF00B3UL)
+#define VI_ATTR_ASRL_END_OUT (0x3FFF00B4UL)
+#define VI_ATTR_ASRL_REPLACE_CHAR (0x3FFF00BEUL)
+#define VI_ATTR_ASRL_RI_STATE (0x3FFF00BFUL)
+#define VI_ATTR_ASRL_RTS_STATE (0x3FFF00C0UL)
+#define VI_ATTR_ASRL_XON_CHAR (0x3FFF00C1UL)
+#define VI_ATTR_ASRL_XOFF_CHAR (0x3FFF00C2UL)
+#define VI_ATTR_WIN_ACCESS (0x3FFF00C3UL)
+#define VI_ATTR_RM_SESSION (0x3FFF00C4UL)
+#define VI_ATTR_VXI_LA (0x3FFF00D5UL)
+#define VI_ATTR_MANF_ID (0x3FFF00D9UL)
+#define VI_ATTR_MEM_SIZE_32 (0x3FFF00DDUL)
+#define VI_ATTR_MEM_SPACE (0x3FFF00DEUL)
+#define VI_ATTR_MODEL_CODE (0x3FFF00DFUL)
+#define VI_ATTR_SLOT (0x3FFF00E8UL)
+#define VI_ATTR_INTF_INST_NAME (0xBFFF00E9UL)
+#define VI_ATTR_IMMEDIATE_SERV (0x3FFF0100UL)
+#define VI_ATTR_INTF_PARENT_NUM (0x3FFF0101UL)
+#define VI_ATTR_RSRC_SPEC_VERSION (0x3FFF0170UL)
+#define VI_ATTR_INTF_TYPE (0x3FFF0171UL)
+#define VI_ATTR_GPIB_PRIMARY_ADDR (0x3FFF0172UL)
+#define VI_ATTR_GPIB_SECONDARY_ADDR (0x3FFF0173UL)
+#define VI_ATTR_RSRC_MANF_NAME (0xBFFF0174UL)
+#define VI_ATTR_RSRC_MANF_ID (0x3FFF0175UL)
+#define VI_ATTR_INTF_NUM (0x3FFF0176UL)
+#define VI_ATTR_TRIG_ID (0x3FFF0177UL)
+#define VI_ATTR_GPIB_REN_STATE (0x3FFF0181UL)
+#define VI_ATTR_GPIB_UNADDR_EN (0x3FFF0184UL)
+#define VI_ATTR_DEV_STATUS_BYTE (0x3FFF0189UL)
+#define VI_ATTR_FILE_APPEND_EN (0x3FFF0192UL)
+#define VI_ATTR_VXI_TRIG_SUPPORT (0x3FFF0194UL)
+#define VI_ATTR_TCPIP_ADDR (0xBFFF0195UL)
+#define VI_ATTR_TCPIP_HOSTNAME (0xBFFF0196UL)
+#define VI_ATTR_TCPIP_PORT (0x3FFF0197UL)
+#define VI_ATTR_TCPIP_DEVICE_NAME (0xBFFF0199UL)
+#define VI_ATTR_TCPIP_NODELAY (0x3FFF019AUL)
+#define VI_ATTR_TCPIP_KEEPALIVE (0x3FFF019BUL)
+#define VI_ATTR_4882_COMPLIANT (0x3FFF019FUL)
+#define VI_ATTR_USB_SERIAL_NUM (0xBFFF01A0UL)
+#define VI_ATTR_USB_INTFC_NUM (0x3FFF01A1UL)
+#define VI_ATTR_USB_PROTOCOL (0x3FFF01A7UL)
+#define VI_ATTR_USB_MAX_INTR_SIZE (0x3FFF01AFUL)
+#define VI_ATTR_PXI_DEV_NUM (0x3FFF0201UL)
+#define VI_ATTR_PXI_FUNC_NUM (0x3FFF0202UL)
+#define VI_ATTR_PXI_BUS_NUM (0x3FFF0205UL)
+#define VI_ATTR_PXI_CHASSIS (0x3FFF0206UL)
+#define VI_ATTR_PXI_SLOTPATH (0xBFFF0207UL)
+#define VI_ATTR_PXI_SLOT_LBUS_LEFT (0x3FFF0208UL)
+#define VI_ATTR_PXI_SLOT_LBUS_RIGHT (0x3FFF0209UL)
+#define VI_ATTR_PXI_TRIG_BUS (0x3FFF020AUL)
+#define VI_ATTR_PXI_STAR_TRIG_BUS (0x3FFF020BUL)
+#define VI_ATTR_PXI_STAR_TRIG_LINE (0x3FFF020CUL)
+#define VI_ATTR_PXI_MEM_TYPE_BAR0 (0x3FFF0211UL)
+#define VI_ATTR_PXI_MEM_TYPE_BAR1 (0x3FFF0212UL)
+#define VI_ATTR_PXI_MEM_TYPE_BAR2 (0x3FFF0213UL)
+#define VI_ATTR_PXI_MEM_TYPE_BAR3 (0x3FFF0214UL)
+#define VI_ATTR_PXI_MEM_TYPE_BAR4 (0x3FFF0215UL)
+#define VI_ATTR_PXI_MEM_TYPE_BAR5 (0x3FFF0216UL)
+#define VI_ATTR_PXI_MEM_BASE_BAR0 (0x3FFF0221UL)
+#define VI_ATTR_PXI_MEM_BASE_BAR1 (0x3FFF0222UL)
+#define VI_ATTR_PXI_MEM_BASE_BAR2 (0x3FFF0223UL)
+#define VI_ATTR_PXI_MEM_BASE_BAR3 (0x3FFF0224UL)
+#define VI_ATTR_PXI_MEM_BASE_BAR4 (0x3FFF0225UL)
+#define VI_ATTR_PXI_MEM_BASE_BAR5 (0x3FFF0226UL)
+#define VI_ATTR_PXI_MEM_SIZE_BAR0 (0x3FFF0231UL)
+#define VI_ATTR_PXI_MEM_SIZE_BAR1 (0x3FFF0232UL)
+#define VI_ATTR_PXI_MEM_SIZE_BAR2 (0x3FFF0233UL)
+#define VI_ATTR_PXI_MEM_SIZE_BAR3 (0x3FFF0234UL)
+#define VI_ATTR_PXI_MEM_SIZE_BAR4 (0x3FFF0235UL)
+#define VI_ATTR_PXI_MEM_SIZE_BAR5 (0x3FFF0236UL)
+#define VI_ATTR_PXI_IS_EXPRESS (0x3FFF0240UL)
+#define VI_ATTR_PXI_SLOT_LWIDTH (0x3FFF0241UL)
+#define VI_ATTR_PXI_MAX_LWIDTH (0x3FFF0242UL)
+#define VI_ATTR_PXI_ACTUAL_LWIDTH (0x3FFF0243UL)
+#define VI_ATTR_PXI_DSTAR_BUS (0x3FFF0244UL)
+#define VI_ATTR_PXI_DSTAR_SET (0x3FFF0245UL)
+
+#define VI_ATTR_JOB_ID (0x3FFF4006UL)
+#define VI_ATTR_EVENT_TYPE (0x3FFF4010UL)
+#define VI_ATTR_SIGP_STATUS_ID (0x3FFF4011UL)
+#define VI_ATTR_RECV_TRIG_ID (0x3FFF4012UL)
+#define VI_ATTR_INTR_STATUS_ID (0x3FFF4023UL)
+#define VI_ATTR_STATUS (0x3FFF4025UL)
+#define VI_ATTR_RET_COUNT_32 (0x3FFF4026UL)
+#define VI_ATTR_BUFFER (0x3FFF4027UL)
+#define VI_ATTR_RECV_INTR_LEVEL (0x3FFF4041UL)
+#define VI_ATTR_OPER_NAME (0xBFFF4042UL)
+#define VI_ATTR_GPIB_RECV_CIC_STATE (0x3FFF4193UL)
+#define VI_ATTR_RECV_TCPIP_ADDR (0xBFFF4198UL)
+#define VI_ATTR_USB_RECV_INTR_SIZE (0x3FFF41B0UL)
+#define VI_ATTR_USB_RECV_INTR_DATA (0xBFFF41B1UL)
+
+/*- Attributes (platform dependent size) ------------------------------------*/
+
+#if defined(_VI_INT64_UINT64_DEFINED) && defined(_VISA_ENV_IS_64_BIT)
+#define VI_ATTR_USER_DATA_64 (0x3FFF000AUL)
+#define VI_ATTR_RET_COUNT_64 (0x3FFF4028UL)
+#define VI_ATTR_USER_DATA (VI_ATTR_USER_DATA_64)
+#define VI_ATTR_RET_COUNT (VI_ATTR_RET_COUNT_64)
+#else
+#define VI_ATTR_USER_DATA (VI_ATTR_USER_DATA_32)
+#define VI_ATTR_RET_COUNT (VI_ATTR_RET_COUNT_32)
+#endif
+
+#if defined(_VI_INT64_UINT64_DEFINED)
+#define VI_ATTR_WIN_BASE_ADDR_64 (0x3FFF009BUL)
+#define VI_ATTR_WIN_SIZE_64 (0x3FFF009CUL)
+#define VI_ATTR_MEM_BASE_64 (0x3FFF00D0UL)
+#define VI_ATTR_MEM_SIZE_64 (0x3FFF00D1UL)
+#endif
+#if defined(_VI_INT64_UINT64_DEFINED) && defined(_VISA_ENV_IS_64_BIT)
+#define VI_ATTR_WIN_BASE_ADDR (VI_ATTR_WIN_BASE_ADDR_64)
+#define VI_ATTR_WIN_SIZE (VI_ATTR_WIN_SIZE_64)
+#define VI_ATTR_MEM_BASE (VI_ATTR_MEM_BASE_64)
+#define VI_ATTR_MEM_SIZE (VI_ATTR_MEM_SIZE_64)
+#else
+#define VI_ATTR_WIN_BASE_ADDR (VI_ATTR_WIN_BASE_ADDR_32)
+#define VI_ATTR_WIN_SIZE (VI_ATTR_WIN_SIZE_32)
+#define VI_ATTR_MEM_BASE (VI_ATTR_MEM_BASE_32)
+#define VI_ATTR_MEM_SIZE (VI_ATTR_MEM_SIZE_32)
+#endif
+
+/*- Event Types -------------------------------------------------------------*/
+
+#define VI_EVENT_IO_COMPLETION (0x3FFF2009UL)
+#define VI_EVENT_TRIG (0xBFFF200AUL)
+#define VI_EVENT_SERVICE_REQ (0x3FFF200BUL)
+#define VI_EVENT_CLEAR (0x3FFF200DUL)
+#define VI_EVENT_EXCEPTION (0xBFFF200EUL)
+#define VI_EVENT_GPIB_CIC (0x3FFF2012UL)
+#define VI_EVENT_GPIB_TALK (0x3FFF2013UL)
+#define VI_EVENT_GPIB_LISTEN (0x3FFF2014UL)
+#define VI_EVENT_VXI_VME_SYSFAIL (0x3FFF201DUL)
+#define VI_EVENT_VXI_VME_SYSRESET (0x3FFF201EUL)
+#define VI_EVENT_VXI_SIGP (0x3FFF2020UL)
+#define VI_EVENT_VXI_VME_INTR (0xBFFF2021UL)
+#define VI_EVENT_PXI_INTR (0x3FFF2022UL)
+#define VI_EVENT_TCPIP_CONNECT (0x3FFF2036UL)
+#define VI_EVENT_USB_INTR (0x3FFF2037UL)
+
+#define VI_ALL_ENABLED_EVENTS (0x3FFF7FFFUL)
+
+/*- Completion and Error Codes ----------------------------------------------*/
+
+#define VI_SUCCESS_EVENT_EN (0x3FFF0002L) /* 3FFF0002, 1073676290 */
+#define VI_SUCCESS_EVENT_DIS (0x3FFF0003L) /* 3FFF0003, 1073676291 */
+#define VI_SUCCESS_QUEUE_EMPTY (0x3FFF0004L) /* 3FFF0004, 1073676292 */
+#define VI_SUCCESS_TERM_CHAR (0x3FFF0005L) /* 3FFF0005, 1073676293 */
+#define VI_SUCCESS_MAX_CNT (0x3FFF0006L) /* 3FFF0006, 1073676294 */
+#define VI_SUCCESS_DEV_NPRESENT (0x3FFF007DL) /* 3FFF007D, 1073676413 */
+#define VI_SUCCESS_TRIG_MAPPED (0x3FFF007EL) /* 3FFF007E, 1073676414 */
+#define VI_SUCCESS_QUEUE_NEMPTY (0x3FFF0080L) /* 3FFF0080, 1073676416 */
+#define VI_SUCCESS_NCHAIN (0x3FFF0098L) /* 3FFF0098, 1073676440 */
+#define VI_SUCCESS_NESTED_SHARED (0x3FFF0099L) /* 3FFF0099, 1073676441 */
+#define VI_SUCCESS_NESTED_EXCLUSIVE (0x3FFF009AL) /* 3FFF009A, 1073676442 */
+#define VI_SUCCESS_SYNC (0x3FFF009BL) /* 3FFF009B, 1073676443 */
+
+#define VI_WARN_QUEUE_OVERFLOW (0x3FFF000CL) /* 3FFF000C, 1073676300 */
+#define VI_WARN_CONFIG_NLOADED (0x3FFF0077L) /* 3FFF0077, 1073676407 */
+#define VI_WARN_NULL_OBJECT (0x3FFF0082L) /* 3FFF0082, 1073676418 */
+#define VI_WARN_NSUP_ATTR_STATE (0x3FFF0084L) /* 3FFF0084, 1073676420 */
+#define VI_WARN_UNKNOWN_STATUS (0x3FFF0085L) /* 3FFF0085, 1073676421 */
+#define VI_WARN_NSUP_BUF (0x3FFF0088L) /* 3FFF0088, 1073676424 */
+#define VI_WARN_EXT_FUNC_NIMPL (0x3FFF00A9L) /* 3FFF00A9, 1073676457 */
+
+#define VI_ERROR_SYSTEM_ERROR (_VI_ERROR+0x3FFF0000L) /* BFFF0000, -1073807360 */
+#define VI_ERROR_INV_OBJECT (_VI_ERROR+0x3FFF000EL) /* BFFF000E, -1073807346 */
+#define VI_ERROR_RSRC_LOCKED (_VI_ERROR+0x3FFF000FL) /* BFFF000F, -1073807345 */
+#define VI_ERROR_INV_EXPR (_VI_ERROR+0x3FFF0010L) /* BFFF0010, -1073807344 */
+#define VI_ERROR_RSRC_NFOUND (_VI_ERROR+0x3FFF0011L) /* BFFF0011, -1073807343 */
+#define VI_ERROR_INV_RSRC_NAME (_VI_ERROR+0x3FFF0012L) /* BFFF0012, -1073807342 */
+#define VI_ERROR_INV_ACC_MODE (_VI_ERROR+0x3FFF0013L) /* BFFF0013, -1073807341 */
+#define VI_ERROR_TMO (_VI_ERROR+0x3FFF0015L) /* BFFF0015, -1073807339 */
+#define VI_ERROR_CLOSING_FAILED (_VI_ERROR+0x3FFF0016L) /* BFFF0016, -1073807338 */
+#define VI_ERROR_INV_DEGREE (_VI_ERROR+0x3FFF001BL) /* BFFF001B, -1073807333 */
+#define VI_ERROR_INV_JOB_ID (_VI_ERROR+0x3FFF001CL) /* BFFF001C, -1073807332 */
+#define VI_ERROR_NSUP_ATTR (_VI_ERROR+0x3FFF001DL) /* BFFF001D, -1073807331 */
+#define VI_ERROR_NSUP_ATTR_STATE (_VI_ERROR+0x3FFF001EL) /* BFFF001E, -1073807330 */
+#define VI_ERROR_ATTR_READONLY (_VI_ERROR+0x3FFF001FL) /* BFFF001F, -1073807329 */
+#define VI_ERROR_INV_LOCK_TYPE (_VI_ERROR+0x3FFF0020L) /* BFFF0020, -1073807328 */
+#define VI_ERROR_INV_ACCESS_KEY (_VI_ERROR+0x3FFF0021L) /* BFFF0021, -1073807327 */
+#define VI_ERROR_INV_EVENT (_VI_ERROR+0x3FFF0026L) /* BFFF0026, -1073807322 */
+#define VI_ERROR_INV_MECH (_VI_ERROR+0x3FFF0027L) /* BFFF0027, -1073807321 */
+#define VI_ERROR_HNDLR_NINSTALLED (_VI_ERROR+0x3FFF0028L) /* BFFF0028, -1073807320 */
+#define VI_ERROR_INV_HNDLR_REF (_VI_ERROR+0x3FFF0029L) /* BFFF0029, -1073807319 */
+#define VI_ERROR_INV_CONTEXT (_VI_ERROR+0x3FFF002AL) /* BFFF002A, -1073807318 */
+#define VI_ERROR_QUEUE_OVERFLOW (_VI_ERROR+0x3FFF002DL) /* BFFF002D, -1073807315 */
+#define VI_ERROR_NENABLED (_VI_ERROR+0x3FFF002FL) /* BFFF002F, -1073807313 */
+#define VI_ERROR_ABORT (_VI_ERROR+0x3FFF0030L) /* BFFF0030, -1073807312 */
+#define VI_ERROR_RAW_WR_PROT_VIOL (_VI_ERROR+0x3FFF0034L) /* BFFF0034, -1073807308 */
+#define VI_ERROR_RAW_RD_PROT_VIOL (_VI_ERROR+0x3FFF0035L) /* BFFF0035, -1073807307 */
+#define VI_ERROR_OUTP_PROT_VIOL (_VI_ERROR+0x3FFF0036L) /* BFFF0036, -1073807306 */
+#define VI_ERROR_INP_PROT_VIOL (_VI_ERROR+0x3FFF0037L) /* BFFF0037, -1073807305 */
+#define VI_ERROR_BERR (_VI_ERROR+0x3FFF0038L) /* BFFF0038, -1073807304 */
+#define VI_ERROR_IN_PROGRESS (_VI_ERROR+0x3FFF0039L) /* BFFF0039, -1073807303 */
+#define VI_ERROR_INV_SETUP (_VI_ERROR+0x3FFF003AL) /* BFFF003A, -1073807302 */
+#define VI_ERROR_QUEUE_ERROR (_VI_ERROR+0x3FFF003BL) /* BFFF003B, -1073807301 */
+#define VI_ERROR_ALLOC (_VI_ERROR+0x3FFF003CL) /* BFFF003C, -1073807300 */
+#define VI_ERROR_INV_MASK (_VI_ERROR+0x3FFF003DL) /* BFFF003D, -1073807299 */
+#define VI_ERROR_IO (_VI_ERROR+0x3FFF003EL) /* BFFF003E, -1073807298 */
+#define VI_ERROR_INV_FMT (_VI_ERROR+0x3FFF003FL) /* BFFF003F, -1073807297 */
+#define VI_ERROR_NSUP_FMT (_VI_ERROR+0x3FFF0041L) /* BFFF0041, -1073807295 */
+#define VI_ERROR_LINE_IN_USE (_VI_ERROR+0x3FFF0042L) /* BFFF0042, -1073807294 */
+#define VI_ERROR_NSUP_MODE (_VI_ERROR+0x3FFF0046L) /* BFFF0046, -1073807290 */
+#define VI_ERROR_SRQ_NOCCURRED (_VI_ERROR+0x3FFF004AL) /* BFFF004A, -1073807286 */
+#define VI_ERROR_INV_SPACE (_VI_ERROR+0x3FFF004EL) /* BFFF004E, -1073807282 */
+#define VI_ERROR_INV_OFFSET (_VI_ERROR+0x3FFF0051L) /* BFFF0051, -1073807279 */
+#define VI_ERROR_INV_WIDTH (_VI_ERROR+0x3FFF0052L) /* BFFF0052, -1073807278 */
+#define VI_ERROR_NSUP_OFFSET (_VI_ERROR+0x3FFF0054L) /* BFFF0054, -1073807276 */
+#define VI_ERROR_NSUP_VAR_WIDTH (_VI_ERROR+0x3FFF0055L) /* BFFF0055, -1073807275 */
+#define VI_ERROR_WINDOW_NMAPPED (_VI_ERROR+0x3FFF0057L) /* BFFF0057, -1073807273 */
+#define VI_ERROR_RESP_PENDING (_VI_ERROR+0x3FFF0059L) /* BFFF0059, -1073807271 */
+#define VI_ERROR_NLISTENERS (_VI_ERROR+0x3FFF005FL) /* BFFF005F, -1073807265 */
+#define VI_ERROR_NCIC (_VI_ERROR+0x3FFF0060L) /* BFFF0060, -1073807264 */
+#define VI_ERROR_NSYS_CNTLR (_VI_ERROR+0x3FFF0061L) /* BFFF0061, -1073807263 */
+#define VI_ERROR_NSUP_OPER (_VI_ERROR+0x3FFF0067L) /* BFFF0067, -1073807257 */
+#define VI_ERROR_INTR_PENDING (_VI_ERROR+0x3FFF0068L) /* BFFF0068, -1073807256 */
+#define VI_ERROR_ASRL_PARITY (_VI_ERROR+0x3FFF006AL) /* BFFF006A, -1073807254 */
+#define VI_ERROR_ASRL_FRAMING (_VI_ERROR+0x3FFF006BL) /* BFFF006B, -1073807253 */
+#define VI_ERROR_ASRL_OVERRUN (_VI_ERROR+0x3FFF006CL) /* BFFF006C, -1073807252 */
+#define VI_ERROR_TRIG_NMAPPED (_VI_ERROR+0x3FFF006EL) /* BFFF006E, -1073807250 */
+#define VI_ERROR_NSUP_ALIGN_OFFSET (_VI_ERROR+0x3FFF0070L) /* BFFF0070, -1073807248 */
+#define VI_ERROR_USER_BUF (_VI_ERROR+0x3FFF0071L) /* BFFF0071, -1073807247 */
+#define VI_ERROR_RSRC_BUSY (_VI_ERROR+0x3FFF0072L) /* BFFF0072, -1073807246 */
+#define VI_ERROR_NSUP_WIDTH (_VI_ERROR+0x3FFF0076L) /* BFFF0076, -1073807242 */
+#define VI_ERROR_INV_PARAMETER (_VI_ERROR+0x3FFF0078L) /* BFFF0078, -1073807240 */
+#define VI_ERROR_INV_PROT (_VI_ERROR+0x3FFF0079L) /* BFFF0079, -1073807239 */
+#define VI_ERROR_INV_SIZE (_VI_ERROR+0x3FFF007BL) /* BFFF007B, -1073807237 */
+#define VI_ERROR_WINDOW_MAPPED (_VI_ERROR+0x3FFF0080L) /* BFFF0080, -1073807232 */
+#define VI_ERROR_NIMPL_OPER (_VI_ERROR+0x3FFF0081L) /* BFFF0081, -1073807231 */
+#define VI_ERROR_INV_LENGTH (_VI_ERROR+0x3FFF0083L) /* BFFF0083, -1073807229 */
+#define VI_ERROR_INV_MODE (_VI_ERROR+0x3FFF0091L) /* BFFF0091, -1073807215 */
+#define VI_ERROR_SESN_NLOCKED (_VI_ERROR+0x3FFF009CL) /* BFFF009C, -1073807204 */
+#define VI_ERROR_MEM_NSHARED (_VI_ERROR+0x3FFF009DL) /* BFFF009D, -1073807203 */
+#define VI_ERROR_LIBRARY_NFOUND (_VI_ERROR+0x3FFF009EL) /* BFFF009E, -1073807202 */
+#define VI_ERROR_NSUP_INTR (_VI_ERROR+0x3FFF009FL) /* BFFF009F, -1073807201 */
+#define VI_ERROR_INV_LINE (_VI_ERROR+0x3FFF00A0L) /* BFFF00A0, -1073807200 */
+#define VI_ERROR_FILE_ACCESS (_VI_ERROR+0x3FFF00A1L) /* BFFF00A1, -1073807199 */
+#define VI_ERROR_FILE_IO (_VI_ERROR+0x3FFF00A2L) /* BFFF00A2, -1073807198 */
+#define VI_ERROR_NSUP_LINE (_VI_ERROR+0x3FFF00A3L) /* BFFF00A3, -1073807197 */
+#define VI_ERROR_NSUP_MECH (_VI_ERROR+0x3FFF00A4L) /* BFFF00A4, -1073807196 */
+#define VI_ERROR_INTF_NUM_NCONFIG (_VI_ERROR+0x3FFF00A5L) /* BFFF00A5, -1073807195 */
+#define VI_ERROR_CONN_LOST (_VI_ERROR+0x3FFF00A6L) /* BFFF00A6, -1073807194 */
+#define VI_ERROR_MACHINE_NAVAIL (_VI_ERROR+0x3FFF00A7L) /* BFFF00A7, -1073807193 */
+#define VI_ERROR_NPERMISSION (_VI_ERROR+0x3FFF00A8L) /* BFFF00A8, -1073807192 */
+
+/*- Other VISA Definitions --------------------------------------------------*/
+
+#define VI_VERSION_MAJOR(ver) ((((ViVersion)ver) & 0xFFF00000UL) >> 20)
+#define VI_VERSION_MINOR(ver) ((((ViVersion)ver) & 0x000FFF00UL) >> 8)
+#define VI_VERSION_SUBMINOR(ver) ((((ViVersion)ver) & 0x000000FFUL) )
+
+#define VI_FIND_BUFLEN (256)
+
+#define VI_INTF_GPIB (1)
+#define VI_INTF_VXI (2)
+#define VI_INTF_GPIB_VXI (3)
+#define VI_INTF_ASRL (4)
+#define VI_INTF_PXI (5)
+#define VI_INTF_TCPIP (6)
+#define VI_INTF_USB (7)
+
+#define VI_PROT_NORMAL (1)
+#define VI_PROT_FDC (2)
+#define VI_PROT_HS488 (3)
+#define VI_PROT_4882_STRS (4)
+#define VI_PROT_USBTMC_VENDOR (5)
+
+#define VI_FDC_NORMAL (1)
+#define VI_FDC_STREAM (2)
+
+#define VI_LOCAL_SPACE (0)
+#define VI_A16_SPACE (1)
+#define VI_A24_SPACE (2)
+#define VI_A32_SPACE (3)
+#define VI_A64_SPACE (4)
+#define VI_PXI_ALLOC_SPACE (9)
+#define VI_PXI_CFG_SPACE (10)
+#define VI_PXI_BAR0_SPACE (11)
+#define VI_PXI_BAR1_SPACE (12)
+#define VI_PXI_BAR2_SPACE (13)
+#define VI_PXI_BAR3_SPACE (14)
+#define VI_PXI_BAR4_SPACE (15)
+#define VI_PXI_BAR5_SPACE (16)
+#define VI_OPAQUE_SPACE (0xFFFF)
+
+#define VI_UNKNOWN_LA (-1)
+#define VI_UNKNOWN_SLOT (-1)
+#define VI_UNKNOWN_LEVEL (-1)
+#define VI_UNKNOWN_CHASSIS (-1)
+
+#define VI_QUEUE (1)
+#define VI_HNDLR (2)
+#define VI_SUSPEND_HNDLR (4)
+#define VI_ALL_MECH (0xFFFF)
+
+#define VI_ANY_HNDLR (0)
+
+#define VI_TRIG_ALL (-2)
+#define VI_TRIG_SW (-1)
+#define VI_TRIG_TTL0 (0)
+#define VI_TRIG_TTL1 (1)
+#define VI_TRIG_TTL2 (2)
+#define VI_TRIG_TTL3 (3)
+#define VI_TRIG_TTL4 (4)
+#define VI_TRIG_TTL5 (5)
+#define VI_TRIG_TTL6 (6)
+#define VI_TRIG_TTL7 (7)
+#define VI_TRIG_ECL0 (8)
+#define VI_TRIG_ECL1 (9)
+#define VI_TRIG_PANEL_IN (27)
+#define VI_TRIG_PANEL_OUT (28)
+
+#define VI_TRIG_PROT_DEFAULT (0)
+#define VI_TRIG_PROT_ON (1)
+#define VI_TRIG_PROT_OFF (2)
+#define VI_TRIG_PROT_SYNC (5)
+#define VI_TRIG_PROT_RESERVE (6)
+#define VI_TRIG_PROT_UNRESERVE (7)
+
+#define VI_READ_BUF (1)
+#define VI_WRITE_BUF (2)
+#define VI_READ_BUF_DISCARD (4)
+#define VI_WRITE_BUF_DISCARD (8)
+#define VI_IO_IN_BUF (16)
+#define VI_IO_OUT_BUF (32)
+#define VI_IO_IN_BUF_DISCARD (64)
+#define VI_IO_OUT_BUF_DISCARD (128)
+
+#define VI_FLUSH_ON_ACCESS (1)
+#define VI_FLUSH_WHEN_FULL (2)
+#define VI_FLUSH_DISABLE (3)
+
+#define VI_NMAPPED (1)
+#define VI_USE_OPERS (2)
+#define VI_DEREF_ADDR (3)
+#define VI_DEREF_ADDR_BYTE_SWAP (4)
+
+#define VI_TMO_IMMEDIATE (0L)
+#define VI_TMO_INFINITE (0xFFFFFFFFUL)
+
+#define VI_NO_LOCK (0)
+#define VI_EXCLUSIVE_LOCK (1)
+#define VI_SHARED_LOCK (2)
+#define VI_LOAD_CONFIG (4)
+
+#define VI_NO_SEC_ADDR (0xFFFF)
+
+#define VI_ASRL_PAR_NONE (0)
+#define VI_ASRL_PAR_ODD (1)
+#define VI_ASRL_PAR_EVEN (2)
+#define VI_ASRL_PAR_MARK (3)
+#define VI_ASRL_PAR_SPACE (4)
+
+#define VI_ASRL_STOP_ONE (10)
+#define VI_ASRL_STOP_ONE5 (15)
+#define VI_ASRL_STOP_TWO (20)
+
+#define VI_ASRL_FLOW_NONE (0)
+#define VI_ASRL_FLOW_XON_XOFF (1)
+#define VI_ASRL_FLOW_RTS_CTS (2)
+#define VI_ASRL_FLOW_DTR_DSR (4)
+
+#define VI_ASRL_END_NONE (0)
+#define VI_ASRL_END_LAST_BIT (1)
+#define VI_ASRL_END_TERMCHAR (2)
+#define VI_ASRL_END_BREAK (3)
+
+#define VI_STATE_ASSERTED (1)
+#define VI_STATE_UNASSERTED (0)
+#define VI_STATE_UNKNOWN (-1)
+
+#define VI_BIG_ENDIAN (0)
+#define VI_LITTLE_ENDIAN (1)
+
+#define VI_DATA_PRIV (0)
+#define VI_DATA_NPRIV (1)
+#define VI_PROG_PRIV (2)
+#define VI_PROG_NPRIV (3)
+#define VI_BLCK_PRIV (4)
+#define VI_BLCK_NPRIV (5)
+#define VI_D64_PRIV (6)
+#define VI_D64_NPRIV (7)
+
+#define VI_WIDTH_8 (1)
+#define VI_WIDTH_16 (2)
+#define VI_WIDTH_32 (4)
+#define VI_WIDTH_64 (8)
+
+#define VI_GPIB_REN_DEASSERT (0)
+#define VI_GPIB_REN_ASSERT (1)
+#define VI_GPIB_REN_DEASSERT_GTL (2)
+#define VI_GPIB_REN_ASSERT_ADDRESS (3)
+#define VI_GPIB_REN_ASSERT_LLO (4)
+#define VI_GPIB_REN_ASSERT_ADDRESS_LLO (5)
+#define VI_GPIB_REN_ADDRESS_GTL (6)
+
+#define VI_GPIB_ATN_DEASSERT (0)
+#define VI_GPIB_ATN_ASSERT (1)
+#define VI_GPIB_ATN_DEASSERT_HANDSHAKE (2)
+#define VI_GPIB_ATN_ASSERT_IMMEDIATE (3)
+
+#define VI_GPIB_HS488_DISABLED (0)
+#define VI_GPIB_HS488_NIMPL (-1)
+
+#define VI_GPIB_UNADDRESSED (0)
+#define VI_GPIB_TALKER (1)
+#define VI_GPIB_LISTENER (2)
+
+#define VI_VXI_CMD16 (0x0200)
+#define VI_VXI_CMD16_RESP16 (0x0202)
+#define VI_VXI_RESP16 (0x0002)
+#define VI_VXI_CMD32 (0x0400)
+#define VI_VXI_CMD32_RESP16 (0x0402)
+#define VI_VXI_CMD32_RESP32 (0x0404)
+#define VI_VXI_RESP32 (0x0004)
+
+#define VI_ASSERT_SIGNAL (-1)
+#define VI_ASSERT_USE_ASSIGNED (0)
+#define VI_ASSERT_IRQ1 (1)
+#define VI_ASSERT_IRQ2 (2)
+#define VI_ASSERT_IRQ3 (3)
+#define VI_ASSERT_IRQ4 (4)
+#define VI_ASSERT_IRQ5 (5)
+#define VI_ASSERT_IRQ6 (6)
+#define VI_ASSERT_IRQ7 (7)
+
+#define VI_UTIL_ASSERT_SYSRESET (1)
+#define VI_UTIL_ASSERT_SYSFAIL (2)
+#define VI_UTIL_DEASSERT_SYSFAIL (3)
+
+#define VI_VXI_CLASS_MEMORY (0)
+#define VI_VXI_CLASS_EXTENDED (1)
+#define VI_VXI_CLASS_MESSAGE (2)
+#define VI_VXI_CLASS_REGISTER (3)
+#define VI_VXI_CLASS_OTHER (4)
+
+#define VI_PXI_ADDR_NONE (0)
+#define VI_PXI_ADDR_MEM (1)
+#define VI_PXI_ADDR_IO (2)
+#define VI_PXI_ADDR_CFG (3)
+
+#define VI_TRIG_UNKNOWN (-1)
+
+#define VI_PXI_LBUS_UNKNOWN (-1)
+#define VI_PXI_LBUS_NONE (0)
+#define VI_PXI_LBUS_STAR_TRIG_BUS_0 (1000)
+#define VI_PXI_LBUS_STAR_TRIG_BUS_1 (1001)
+#define VI_PXI_LBUS_STAR_TRIG_BUS_2 (1002)
+#define VI_PXI_LBUS_STAR_TRIG_BUS_3 (1003)
+#define VI_PXI_LBUS_STAR_TRIG_BUS_4 (1004)
+#define VI_PXI_LBUS_STAR_TRIG_BUS_5 (1005)
+#define VI_PXI_LBUS_STAR_TRIG_BUS_6 (1006)
+#define VI_PXI_LBUS_STAR_TRIG_BUS_7 (1007)
+#define VI_PXI_LBUS_STAR_TRIG_BUS_8 (1008)
+#define VI_PXI_LBUS_STAR_TRIG_BUS_9 (1009)
+#define VI_PXI_STAR_TRIG_CONTROLLER (1413)
+
+/*- Backward Compatibility Macros -------------------------------------------*/
+
+#define viGetDefaultRM(vi) viOpenDefaultRM(vi)
+#define VI_ERROR_INV_SESSION (VI_ERROR_INV_OBJECT)
+#define VI_INFINITE (VI_TMO_INFINITE)
+#define VI_NORMAL (VI_PROT_NORMAL)
+#define VI_FDC (VI_PROT_FDC)
+#define VI_HS488 (VI_PROT_HS488)
+#define VI_ASRL488 (VI_PROT_4882_STRS)
+#define VI_ASRL_IN_BUF (VI_IO_IN_BUF)
+#define VI_ASRL_OUT_BUF (VI_IO_OUT_BUF)
+#define VI_ASRL_IN_BUF_DISCARD (VI_IO_IN_BUF_DISCARD)
+#define VI_ASRL_OUT_BUF_DISCARD (VI_IO_OUT_BUF_DISCARD)
+
+/*- National Instruments ----------------------------------------------------*/
+
+#define VI_INTF_RIO (8)
+#define VI_INTF_FIREWIRE (9)
+
+#define VI_ATTR_SYNC_MXI_ALLOW_EN (0x3FFF0161UL) /* ViBoolean, read/write */
+
+/* This is for VXI SERVANT resources */
+
+#define VI_EVENT_VXI_DEV_CMD (0xBFFF200FUL)
+#define VI_ATTR_VXI_DEV_CMD_TYPE (0x3FFF4037UL) /* ViInt16, read-only */
+#define VI_ATTR_VXI_DEV_CMD_VALUE (0x3FFF4038UL) /* ViUInt32, read-only */
+
+#define VI_VXI_DEV_CMD_TYPE_16 (16)
+#define VI_VXI_DEV_CMD_TYPE_32 (32)
+
+ViStatus _VI_FUNC viVxiServantResponse(ViSession vi, ViInt16 mode, ViUInt32 resp);
+/* mode values include VI_VXI_RESP16, VI_VXI_RESP32, and the next 2 values */
+#define VI_VXI_RESP_NONE (0)
+#define VI_VXI_RESP_PROT_ERROR (-1)
+
+/* This allows extended Serial support on Win32 and on NI ENET Serial products */
+
+#define VI_ATTR_ASRL_DISCARD_NULL (0x3FFF00B0UL)
+#define VI_ATTR_ASRL_CONNECTED (0x3FFF01BBUL)
+#define VI_ATTR_ASRL_BREAK_STATE (0x3FFF01BCUL)
+#define VI_ATTR_ASRL_BREAK_LEN (0x3FFF01BDUL)
+#define VI_ATTR_ASRL_ALLOW_TRANSMIT (0x3FFF01BEUL)
+#define VI_ATTR_ASRL_WIRE_MODE (0x3FFF01BFUL)
+
+#define VI_ASRL_WIRE_485_4 (0)
+#define VI_ASRL_WIRE_485_2_DTR_ECHO (1)
+#define VI_ASRL_WIRE_485_2_DTR_CTRL (2)
+#define VI_ASRL_WIRE_485_2_AUTO (3)
+#define VI_ASRL_WIRE_232_DTE (128)
+#define VI_ASRL_WIRE_232_DCE (129)
+#define VI_ASRL_WIRE_232_AUTO (130)
+
+#define VI_EVENT_ASRL_BREAK (0x3FFF2023UL)
+#define VI_EVENT_ASRL_CTS (0x3FFF2029UL)
+#define VI_EVENT_ASRL_DSR (0x3FFF202AUL)
+#define VI_EVENT_ASRL_DCD (0x3FFF202CUL)
+#define VI_EVENT_ASRL_RI (0x3FFF202EUL)
+#define VI_EVENT_ASRL_CHAR (0x3FFF2035UL)
+#define VI_EVENT_ASRL_TERMCHAR (0x3FFF2024UL)
+
+/* This is for fast viPeek/viPoke macros */
+
+#if defined(NIVISA_PEEKPOKE)
+
+#if defined(NIVISA_PEEKPOKE_SUPP)
+#undef NIVISA_PEEKPOKE_SUPP
+#endif
+
+#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)) && !defined(_NI_mswin16_)
+/* This macro is supported for all Win32 compilers, including CVI. */
+#define NIVISA_PEEKPOKE_SUPP
+#elif (defined(_WINDOWS) || defined(_Windows)) && !defined(_CVI_) && !defined(_NI_mswin16_)
+/* This macro is supported for Borland and Microsoft compilers on Win16, but not CVI. */
+#define NIVISA_PEEKPOKE_SUPP
+#elif defined(_CVI_) && defined(_NI_sparc_)
+/* This macro is supported for Solaris 1 and 2, from CVI only. */
+#define NIVISA_PEEKPOKE_SUPP
+#else
+/* This macro is not supported on other platforms. */
+#endif
+
+#if defined(NIVISA_PEEKPOKE_SUPP)
+
+extern ViBoolean NI_viImplVISA1;
+ViStatus _VI_FUNC NI_viOpenDefaultRM (ViPSession vi);
+#define viOpenDefaultRM(vi) NI_viOpenDefaultRM(vi)
+
+#define viPeek8(vi,addr,val) \
+ { \
+ if ((NI_viImplVISA1) && (*((ViPUInt32)(vi)))) \
+ { \
+ do (*((ViPUInt8)(val)) = *((volatile ViUInt8 _VI_PTR)(addr))); \
+ while (**((volatile ViUInt8 _VI_PTR _VI_PTR)(vi)) & 0x10); \
+ } \
+ else \
+ { \
+ (viPeek8)((vi),(addr),(val)); \
+ } \
+ }
+
+#define viPoke8(vi,addr,val) \
+ { \
+ if ((NI_viImplVISA1) && (*((ViPUInt32)(vi)))) \
+ { \
+ do (*((volatile ViUInt8 _VI_PTR)(addr)) = ((ViUInt8)(val))); \
+ while (**((volatile ViUInt8 _VI_PTR _VI_PTR)(vi)) & 0x10); \
+ } \
+ else \
+ { \
+ (viPoke8)((vi),(addr),(val)); \
+ } \
+ }
+
+#define viPeek16(vi,addr,val) \
+ { \
+ if ((NI_viImplVISA1) && (*((ViPUInt32)(vi)))) \
+ { \
+ do (*((ViPUInt16)(val)) = *((volatile ViUInt16 _VI_PTR)(addr))); \
+ while (**((volatile ViUInt8 _VI_PTR _VI_PTR)(vi)) & 0x10); \
+ } \
+ else \
+ { \
+ (viPeek16)((vi),(addr),(val)); \
+ } \
+ }
+
+#define viPoke16(vi,addr,val) \
+ { \
+ if ((NI_viImplVISA1) && (*((ViPUInt32)(vi)))) \
+ { \
+ do (*((volatile ViUInt16 _VI_PTR)(addr)) = ((ViUInt16)(val))); \
+ while (**((volatile ViUInt8 _VI_PTR _VI_PTR)(vi)) & 0x10); \
+ } \
+ else \
+ { \
+ (viPoke16)((vi),(addr),(val)); \
+ } \
+ }
+
+#define viPeek32(vi,addr,val) \
+ { \
+ if ((NI_viImplVISA1) && (*((ViPUInt32)(vi)))) \
+ { \
+ do (*((ViPUInt32)(val)) = *((volatile ViUInt32 _VI_PTR)(addr))); \
+ while (**((volatile ViUInt8 _VI_PTR _VI_PTR)(vi)) & 0x10); \
+ } \
+ else \
+ { \
+ (viPeek32)((vi),(addr),(val)); \
+ } \
+ }
+
+#define viPoke32(vi,addr,val) \
+ { \
+ if ((NI_viImplVISA1) && (*((ViPUInt32)(vi)))) \
+ { \
+ do (*((volatile ViUInt32 _VI_PTR)(addr)) = ((ViUInt32)(val))); \
+ while (**((volatile ViUInt8 _VI_PTR _VI_PTR)(vi)) & 0x10); \
+ } \
+ else \
+ { \
+ (viPoke32)((vi),(addr),(val)); \
+ } \
+ }
+
+#endif
+
+#endif
+
+#if defined(NIVISA_PXI) || defined(PXISAVISA_PXI)
+
+#if 0
+/* The following 2 attributes were incorrectly implemented in earlier
+ versions of NI-VISA. You should now query VI_ATTR_MANF_ID or
+ VI_ATTR_MODEL_CODE. Those attributes contain sub-vendor information
+ when it exists. To get both the actual primary and subvendor codes
+ from the device, you should call viIn16 using VI_PXI_CFG_SPACE. */
+#define VI_ATTR_PXI_SUB_MANF_ID (0x3FFF0203UL)
+#define VI_ATTR_PXI_SUB_MODEL_CODE (0x3FFF0204UL)
+#endif
+
+#define VI_ATTR_PXI_SRC_TRIG_BUS (0x3FFF020DUL)
+#define VI_ATTR_PXI_DEST_TRIG_BUS (0x3FFF020EUL)
+
+#define VI_ATTR_PXI_RECV_INTR_SEQ (0x3FFF4240UL)
+#define VI_ATTR_PXI_RECV_INTR_DATA (0x3FFF4241UL)
+
+#endif
+
+#if defined(NIVISA_USB)
+
+#define VI_ATTR_USB_BULK_OUT_PIPE (0x3FFF01A2UL)
+#define VI_ATTR_USB_BULK_IN_PIPE (0x3FFF01A3UL)
+#define VI_ATTR_USB_INTR_IN_PIPE (0x3FFF01A4UL)
+#define VI_ATTR_USB_CLASS (0x3FFF01A5UL)
+#define VI_ATTR_USB_SUBCLASS (0x3FFF01A6UL)
+#define VI_ATTR_USB_ALT_SETTING (0x3FFF01A8UL)
+#define VI_ATTR_USB_END_IN (0x3FFF01A9UL)
+#define VI_ATTR_USB_NUM_INTFCS (0x3FFF01AAUL)
+#define VI_ATTR_USB_NUM_PIPES (0x3FFF01ABUL)
+#define VI_ATTR_USB_BULK_OUT_STATUS (0x3FFF01ACUL)
+#define VI_ATTR_USB_BULK_IN_STATUS (0x3FFF01ADUL)
+#define VI_ATTR_USB_INTR_IN_STATUS (0x3FFF01AEUL)
+#define VI_ATTR_USB_CTRL_PIPE (0x3FFF01B0UL)
+
+#define VI_USB_PIPE_STATE_UNKNOWN (-1)
+#define VI_USB_PIPE_READY (0)
+#define VI_USB_PIPE_STALLED (1)
+
+#define VI_USB_END_NONE (0)
+#define VI_USB_END_SHORT (4)
+#define VI_USB_END_SHORT_OR_COUNT (5)
+
+#endif
+
+#define VI_ATTR_FIREWIRE_DEST_UPPER_OFFSET (0x3FFF01F0UL)
+#define VI_ATTR_FIREWIRE_SRC_UPPER_OFFSET (0x3FFF01F1UL)
+#define VI_ATTR_FIREWIRE_WIN_UPPER_OFFSET (0x3FFF01F2UL)
+#define VI_ATTR_FIREWIRE_VENDOR_ID (0x3FFF01F3UL)
+#define VI_ATTR_FIREWIRE_LOWER_CHIP_ID (0x3FFF01F4UL)
+#define VI_ATTR_FIREWIRE_UPPER_CHIP_ID (0x3FFF01F5UL)
+
+#define VI_FIREWIRE_DFLT_SPACE (5)
+
+#if defined(__cplusplus) || defined(__cplusplus__)
+ }
+#endif
+
+#endif
+
+/*- The End -----------------------------------------------------------------*/
diff --git a/aos/externals/allwpilib/hal/lib/Athena/visa/visatype.h b/aos/externals/allwpilib/hal/lib/Athena/visa/visatype.h
new file mode 100644
index 0000000..ef089dd
--- /dev/null
+++ b/aos/externals/allwpilib/hal/lib/Athena/visa/visatype.h
@@ -0,0 +1,201 @@
+/*---------------------------------------------------------------------------*/
+/* Distributed by IVI Foundation Inc. */
+/* */
+/* Do not modify the contents of this file. */
+/*---------------------------------------------------------------------------*/
+/* */
+/* Title : VISATYPE.H */
+/* Date : 04-14-2006 */
+/* Purpose : Fundamental VISA data types and macro definitions */
+/* */
+/*---------------------------------------------------------------------------*/
+
+#ifndef __VISATYPE_HEADER__
+#define __VISATYPE_HEADER__
+
+#if defined(_WIN64)
+#define _VI_FAR
+#define _VI_FUNC __fastcall
+#define _VI_FUNCC __fastcall
+#define _VI_FUNCH __fastcall
+#define _VI_SIGNED signed
+#elif (defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)) && !defined(_NI_mswin16_)
+#define _VI_FAR
+#define _VI_FUNC __stdcall
+#define _VI_FUNCC __cdecl
+#define _VI_FUNCH __stdcall
+#define _VI_SIGNED signed
+#elif defined(_CVI_) && defined(_NI_i386_)
+#define _VI_FAR
+#define _VI_FUNC _pascal
+#define _VI_FUNCC
+#define _VI_FUNCH _pascal
+#define _VI_SIGNED signed
+#elif (defined(_WINDOWS) || defined(_Windows)) && !defined(_NI_mswin16_)
+#define _VI_FAR _far
+#define _VI_FUNC _far _pascal _export
+#define _VI_FUNCC _far _cdecl _export
+#define _VI_FUNCH _far _pascal
+#define _VI_SIGNED signed
+#elif (defined(hpux) || defined(__hpux)) && (defined(__cplusplus) || defined(__cplusplus__))
+#define _VI_FAR
+#define _VI_FUNC
+#define _VI_FUNCC
+#define _VI_FUNCH
+#define _VI_SIGNED
+#else
+#define _VI_FAR
+#define _VI_FUNC
+#define _VI_FUNCC
+#define _VI_FUNCH
+#define _VI_SIGNED signed
+#endif
+
+#define _VI_ERROR (-2147483647L-1) /* 0x80000000 */
+#define _VI_PTR _VI_FAR *
+
+/*- VISA Types --------------------------------------------------------------*/
+
+#ifndef _VI_INT64_UINT64_DEFINED
+#if defined(_WIN64) || ((defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)) && !defined(_NI_mswin16_))
+#if (defined(_MSC_VER) && (_MSC_VER >= 1200)) || (defined(_CVI_) && (_CVI_ >= 700)) || (defined(__BORLANDC__) && (__BORLANDC__ >= 0x0520))
+typedef unsigned __int64 ViUInt64;
+typedef _VI_SIGNED __int64 ViInt64;
+#define _VI_INT64_UINT64_DEFINED
+#if defined(_WIN64)
+#define _VISA_ENV_IS_64_BIT
+#else
+/* This is a 32-bit OS, not a 64-bit OS */
+#endif
+#endif
+#elif defined(__GNUC__) && (__GNUC__ >= 3)
+#include <limits.h>
+#include <sys/types.h>
+typedef u_int64_t ViUInt64;
+typedef int64_t ViInt64;
+#define _VI_INT64_UINT64_DEFINED
+#if defined(LONG_MAX) && (LONG_MAX > 0x7FFFFFFFL)
+#define _VISA_ENV_IS_64_BIT
+#else
+/* This is a 32-bit OS, not a 64-bit OS */
+#endif
+#else
+/* This platform does not support 64-bit types */
+#endif
+#endif
+
+#if defined(_VI_INT64_UINT64_DEFINED)
+typedef ViUInt64 _VI_PTR ViPUInt64;
+typedef ViUInt64 _VI_PTR ViAUInt64;
+typedef ViInt64 _VI_PTR ViPInt64;
+typedef ViInt64 _VI_PTR ViAInt64;
+#endif
+
+#if defined(LONG_MAX) && (LONG_MAX > 0x7FFFFFFFL)
+typedef unsigned int ViUInt32;
+typedef _VI_SIGNED int ViInt32;
+#else
+typedef unsigned long ViUInt32;
+typedef _VI_SIGNED long ViInt32;
+#endif
+
+typedef ViUInt32 _VI_PTR ViPUInt32;
+typedef ViUInt32 _VI_PTR ViAUInt32;
+typedef ViInt32 _VI_PTR ViPInt32;
+typedef ViInt32 _VI_PTR ViAInt32;
+
+typedef unsigned short ViUInt16;
+typedef ViUInt16 _VI_PTR ViPUInt16;
+typedef ViUInt16 _VI_PTR ViAUInt16;
+
+typedef _VI_SIGNED short ViInt16;
+typedef ViInt16 _VI_PTR ViPInt16;
+typedef ViInt16 _VI_PTR ViAInt16;
+
+typedef unsigned char ViUInt8;
+typedef ViUInt8 _VI_PTR ViPUInt8;
+typedef ViUInt8 _VI_PTR ViAUInt8;
+
+typedef _VI_SIGNED char ViInt8;
+typedef ViInt8 _VI_PTR ViPInt8;
+typedef ViInt8 _VI_PTR ViAInt8;
+
+typedef char ViChar;
+typedef ViChar _VI_PTR ViPChar;
+typedef ViChar _VI_PTR ViAChar;
+
+typedef unsigned char ViByte;
+typedef ViByte _VI_PTR ViPByte;
+typedef ViByte _VI_PTR ViAByte;
+
+typedef void _VI_PTR ViAddr;
+typedef ViAddr _VI_PTR ViPAddr;
+typedef ViAddr _VI_PTR ViAAddr;
+
+typedef float ViReal32;
+typedef ViReal32 _VI_PTR ViPReal32;
+typedef ViReal32 _VI_PTR ViAReal32;
+
+typedef double ViReal64;
+typedef ViReal64 _VI_PTR ViPReal64;
+typedef ViReal64 _VI_PTR ViAReal64;
+
+typedef ViPByte ViBuf;
+typedef ViPByte ViPBuf;
+typedef ViPByte _VI_PTR ViABuf;
+
+typedef ViPChar ViString;
+typedef ViPChar ViPString;
+typedef ViPChar _VI_PTR ViAString;
+
+typedef ViString ViRsrc;
+typedef ViString ViPRsrc;
+typedef ViString _VI_PTR ViARsrc;
+
+typedef ViUInt16 ViBoolean;
+typedef ViBoolean _VI_PTR ViPBoolean;
+typedef ViBoolean _VI_PTR ViABoolean;
+
+typedef ViInt32 ViStatus;
+typedef ViStatus _VI_PTR ViPStatus;
+typedef ViStatus _VI_PTR ViAStatus;
+
+typedef ViUInt32 ViVersion;
+typedef ViVersion _VI_PTR ViPVersion;
+typedef ViVersion _VI_PTR ViAVersion;
+
+typedef ViUInt32 ViObject;
+typedef ViObject _VI_PTR ViPObject;
+typedef ViObject _VI_PTR ViAObject;
+
+typedef ViObject ViSession;
+typedef ViSession _VI_PTR ViPSession;
+typedef ViSession _VI_PTR ViASession;
+
+typedef ViUInt32 ViAttr;
+
+#ifndef _VI_CONST_STRING_DEFINED
+typedef const ViChar * ViConstString;
+#define _VI_CONST_STRING_DEFINED
+#endif
+
+/*- Completion and Error Codes ----------------------------------------------*/
+
+#define VI_SUCCESS (0L)
+
+/*- Other VISA Definitions --------------------------------------------------*/
+
+#define VI_NULL (0)
+
+#define VI_TRUE (1)
+#define VI_FALSE (0)
+
+/*- Backward Compatibility Macros -------------------------------------------*/
+
+#define VISAFN _VI_FUNC
+#define ViPtr _VI_PTR
+
+#endif
+
+/*- The End -----------------------------------------------------------------*/
+
diff --git a/aos/externals/allwpilib/ni-libraries/genlinks b/aos/externals/allwpilib/ni-libraries/genlinks
new file mode 100755
index 0000000..6931dbc
--- /dev/null
+++ b/aos/externals/allwpilib/ni-libraries/genlinks
@@ -0,0 +1,11 @@
+#!/bin/bash
+
+# this file should generate symlinks (POSIX, TODO) or LD scripts (POSIX or windows) to enable standard version linking
+
+for lib in lib*.so.*; do
+ libout="${lib%.*}"
+ rm -rf $libout
+ echo "OUTPUT_FORMAT(elf32-littlearm)" > $libout
+ echo "GROUP ( $lib )" >> $libout
+ echo "Generated LD link $libout -> $lib"
+done
diff --git a/aos/externals/allwpilib/ni-libraries/libFRC_NetworkCommunication.so b/aos/externals/allwpilib/ni-libraries/libFRC_NetworkCommunication.so
new file mode 100644
index 0000000..a24218e
--- /dev/null
+++ b/aos/externals/allwpilib/ni-libraries/libFRC_NetworkCommunication.so
@@ -0,0 +1,2 @@
+OUTPUT_FORMAT(elf32-littlearm)
+GROUP ( libFRC_NetworkCommunication.so.1 )
diff --git a/aos/externals/allwpilib/ni-libraries/libFRC_NetworkCommunication.so.1 b/aos/externals/allwpilib/ni-libraries/libFRC_NetworkCommunication.so.1
new file mode 100644
index 0000000..e1ec38a
--- /dev/null
+++ b/aos/externals/allwpilib/ni-libraries/libFRC_NetworkCommunication.so.1
@@ -0,0 +1,2 @@
+OUTPUT_FORMAT(elf32-littlearm)
+GROUP ( libFRC_NetworkCommunication.so.1.5 )
diff --git a/aos/externals/allwpilib/ni-libraries/libFRC_NetworkCommunication.so.1.5 b/aos/externals/allwpilib/ni-libraries/libFRC_NetworkCommunication.so.1.5
new file mode 100644
index 0000000..845c759
--- /dev/null
+++ b/aos/externals/allwpilib/ni-libraries/libFRC_NetworkCommunication.so.1.5
@@ -0,0 +1,2 @@
+OUTPUT_FORMAT(elf32-littlearm)
+GROUP ( libFRC_NetworkCommunication.so.1.5.0 )
diff --git a/aos/externals/allwpilib/ni-libraries/libFRC_NetworkCommunication.so.1.5.0 b/aos/externals/allwpilib/ni-libraries/libFRC_NetworkCommunication.so.1.5.0
new file mode 100755
index 0000000..503911b
--- /dev/null
+++ b/aos/externals/allwpilib/ni-libraries/libFRC_NetworkCommunication.so.1.5.0
Binary files differ
diff --git a/aos/externals/allwpilib/ni-libraries/libFRC_NetworkCommunicationLV.so b/aos/externals/allwpilib/ni-libraries/libFRC_NetworkCommunicationLV.so
new file mode 100644
index 0000000..c7c2653
--- /dev/null
+++ b/aos/externals/allwpilib/ni-libraries/libFRC_NetworkCommunicationLV.so
@@ -0,0 +1,2 @@
+OUTPUT_FORMAT(elf32-littlearm)
+GROUP ( libFRC_NetworkCommunicationLV.so.1 )
diff --git a/aos/externals/allwpilib/ni-libraries/libFRC_NetworkCommunicationLV.so.1 b/aos/externals/allwpilib/ni-libraries/libFRC_NetworkCommunicationLV.so.1
new file mode 100644
index 0000000..1254b6d
--- /dev/null
+++ b/aos/externals/allwpilib/ni-libraries/libFRC_NetworkCommunicationLV.so.1
@@ -0,0 +1,2 @@
+OUTPUT_FORMAT(elf32-littlearm)
+GROUP ( libFRC_NetworkCommunicationLV.so.1.5 )
diff --git a/aos/externals/allwpilib/ni-libraries/libFRC_NetworkCommunicationLV.so.1.5 b/aos/externals/allwpilib/ni-libraries/libFRC_NetworkCommunicationLV.so.1.5
new file mode 100644
index 0000000..30022db
--- /dev/null
+++ b/aos/externals/allwpilib/ni-libraries/libFRC_NetworkCommunicationLV.so.1.5
@@ -0,0 +1,2 @@
+OUTPUT_FORMAT(elf32-littlearm)
+GROUP ( libFRC_NetworkCommunicationLV.so.1.5.0 )
diff --git a/aos/externals/allwpilib/ni-libraries/libFRC_NetworkCommunicationLV.so.1.5.0 b/aos/externals/allwpilib/ni-libraries/libFRC_NetworkCommunicationLV.so.1.5.0
new file mode 100755
index 0000000..f54d997
--- /dev/null
+++ b/aos/externals/allwpilib/ni-libraries/libFRC_NetworkCommunicationLV.so.1.5.0
Binary files differ
diff --git a/aos/externals/allwpilib/ni-libraries/libGCBase_gcc-4.4-arm_v2_3.so b/aos/externals/allwpilib/ni-libraries/libGCBase_gcc-4.4-arm_v2_3.so
new file mode 100755
index 0000000..3bcda8d
--- /dev/null
+++ b/aos/externals/allwpilib/ni-libraries/libGCBase_gcc-4.4-arm_v2_3.so
Binary files differ
diff --git a/aos/externals/allwpilib/ni-libraries/libGenApi_gcc-4.4-arm_v2_3.so b/aos/externals/allwpilib/ni-libraries/libGenApi_gcc-4.4-arm_v2_3.so
new file mode 100755
index 0000000..7e6a6df
--- /dev/null
+++ b/aos/externals/allwpilib/ni-libraries/libGenApi_gcc-4.4-arm_v2_3.so
Binary files differ
diff --git a/aos/externals/allwpilib/ni-libraries/libLog_gcc-4.4-arm_v2_3.so b/aos/externals/allwpilib/ni-libraries/libLog_gcc-4.4-arm_v2_3.so
new file mode 100755
index 0000000..6e8719b
--- /dev/null
+++ b/aos/externals/allwpilib/ni-libraries/libLog_gcc-4.4-arm_v2_3.so
Binary files differ
diff --git a/aos/externals/allwpilib/ni-libraries/libMathParser_gcc-4.4-arm_v2_3.so b/aos/externals/allwpilib/ni-libraries/libMathParser_gcc-4.4-arm_v2_3.so
new file mode 100755
index 0000000..e4d1279
--- /dev/null
+++ b/aos/externals/allwpilib/ni-libraries/libMathParser_gcc-4.4-arm_v2_3.so
Binary files differ
diff --git a/aos/externals/allwpilib/ni-libraries/libNiFpga.so b/aos/externals/allwpilib/ni-libraries/libNiFpga.so
new file mode 100644
index 0000000..c506470
--- /dev/null
+++ b/aos/externals/allwpilib/ni-libraries/libNiFpga.so
@@ -0,0 +1,2 @@
+OUTPUT_FORMAT(elf32-littlearm)
+GROUP ( libNiFpga.so.14 )
diff --git a/aos/externals/allwpilib/ni-libraries/libNiFpga.so.14 b/aos/externals/allwpilib/ni-libraries/libNiFpga.so.14
new file mode 100644
index 0000000..3b66e3e
--- /dev/null
+++ b/aos/externals/allwpilib/ni-libraries/libNiFpga.so.14
@@ -0,0 +1,2 @@
+OUTPUT_FORMAT(elf32-littlearm)
+GROUP ( libNiFpga.so.14.0 )
diff --git a/aos/externals/allwpilib/ni-libraries/libNiFpga.so.14.0 b/aos/externals/allwpilib/ni-libraries/libNiFpga.so.14.0
new file mode 100644
index 0000000..aa4e287
--- /dev/null
+++ b/aos/externals/allwpilib/ni-libraries/libNiFpga.so.14.0
@@ -0,0 +1,2 @@
+OUTPUT_FORMAT(elf32-littlearm)
+GROUP ( libNiFpga.so.14.0.0 )
diff --git a/aos/externals/allwpilib/ni-libraries/libNiFpga.so.14.0.0 b/aos/externals/allwpilib/ni-libraries/libNiFpga.so.14.0.0
new file mode 100755
index 0000000..1f3a237
--- /dev/null
+++ b/aos/externals/allwpilib/ni-libraries/libNiFpga.so.14.0.0
Binary files differ
diff --git a/aos/externals/allwpilib/ni-libraries/libNiFpgaLv.so b/aos/externals/allwpilib/ni-libraries/libNiFpgaLv.so
new file mode 100644
index 0000000..88b5a24
--- /dev/null
+++ b/aos/externals/allwpilib/ni-libraries/libNiFpgaLv.so
@@ -0,0 +1,2 @@
+OUTPUT_FORMAT(elf32-littlearm)
+GROUP ( libNiFpgaLv.so.14 )
diff --git a/aos/externals/allwpilib/ni-libraries/libNiFpgaLv.so.14 b/aos/externals/allwpilib/ni-libraries/libNiFpgaLv.so.14
new file mode 100644
index 0000000..3adae09
--- /dev/null
+++ b/aos/externals/allwpilib/ni-libraries/libNiFpgaLv.so.14
@@ -0,0 +1,2 @@
+OUTPUT_FORMAT(elf32-littlearm)
+GROUP ( libNiFpgaLv.so.14.0 )
diff --git a/aos/externals/allwpilib/ni-libraries/libNiFpgaLv.so.14.0 b/aos/externals/allwpilib/ni-libraries/libNiFpgaLv.so.14.0
new file mode 100644
index 0000000..db0b28c
--- /dev/null
+++ b/aos/externals/allwpilib/ni-libraries/libNiFpgaLv.so.14.0
@@ -0,0 +1,2 @@
+OUTPUT_FORMAT(elf32-littlearm)
+GROUP ( libNiFpgaLv.so.14.0.0 )
diff --git a/aos/externals/allwpilib/ni-libraries/libNiFpgaLv.so.14.0.0 b/aos/externals/allwpilib/ni-libraries/libNiFpgaLv.so.14.0.0
new file mode 100755
index 0000000..3efee8f
--- /dev/null
+++ b/aos/externals/allwpilib/ni-libraries/libNiFpgaLv.so.14.0.0
Binary files differ
diff --git a/aos/externals/allwpilib/ni-libraries/libNiRioSrv.so b/aos/externals/allwpilib/ni-libraries/libNiRioSrv.so
new file mode 100644
index 0000000..98b55b1
--- /dev/null
+++ b/aos/externals/allwpilib/ni-libraries/libNiRioSrv.so
@@ -0,0 +1,2 @@
+OUTPUT_FORMAT(elf32-littlearm)
+GROUP ( libNiRioSrv.so.14 )
diff --git a/aos/externals/allwpilib/ni-libraries/libNiRioSrv.so.14 b/aos/externals/allwpilib/ni-libraries/libNiRioSrv.so.14
new file mode 100644
index 0000000..ef98d56
--- /dev/null
+++ b/aos/externals/allwpilib/ni-libraries/libNiRioSrv.so.14
@@ -0,0 +1,2 @@
+OUTPUT_FORMAT(elf32-littlearm)
+GROUP ( libNiRioSrv.so.14.0 )
diff --git a/aos/externals/allwpilib/ni-libraries/libNiRioSrv.so.14.0 b/aos/externals/allwpilib/ni-libraries/libNiRioSrv.so.14.0
new file mode 100644
index 0000000..7ee207d
--- /dev/null
+++ b/aos/externals/allwpilib/ni-libraries/libNiRioSrv.so.14.0
@@ -0,0 +1,2 @@
+OUTPUT_FORMAT(elf32-littlearm)
+GROUP ( libNiRioSrv.so.14.0.0 )
diff --git a/aos/externals/allwpilib/ni-libraries/libNiRioSrv.so.14.0.0 b/aos/externals/allwpilib/ni-libraries/libNiRioSrv.so.14.0.0
new file mode 100755
index 0000000..4917af8
--- /dev/null
+++ b/aos/externals/allwpilib/ni-libraries/libNiRioSrv.so.14.0.0
Binary files differ
diff --git a/aos/externals/allwpilib/ni-libraries/libRoboRIO_FRC_ChipObject.so b/aos/externals/allwpilib/ni-libraries/libRoboRIO_FRC_ChipObject.so
new file mode 100644
index 0000000..b44dc03
--- /dev/null
+++ b/aos/externals/allwpilib/ni-libraries/libRoboRIO_FRC_ChipObject.so
@@ -0,0 +1,2 @@
+OUTPUT_FORMAT(elf32-littlearm)
+GROUP ( libRoboRIO_FRC_ChipObject.so.1 )
diff --git a/aos/externals/allwpilib/ni-libraries/libRoboRIO_FRC_ChipObject.so.1 b/aos/externals/allwpilib/ni-libraries/libRoboRIO_FRC_ChipObject.so.1
new file mode 100644
index 0000000..fff4f97
--- /dev/null
+++ b/aos/externals/allwpilib/ni-libraries/libRoboRIO_FRC_ChipObject.so.1
@@ -0,0 +1,2 @@
+OUTPUT_FORMAT(elf32-littlearm)
+GROUP ( libRoboRIO_FRC_ChipObject.so.1.2 )
diff --git a/aos/externals/allwpilib/ni-libraries/libRoboRIO_FRC_ChipObject.so.1.2 b/aos/externals/allwpilib/ni-libraries/libRoboRIO_FRC_ChipObject.so.1.2
new file mode 100644
index 0000000..467fa0e
--- /dev/null
+++ b/aos/externals/allwpilib/ni-libraries/libRoboRIO_FRC_ChipObject.so.1.2
@@ -0,0 +1,2 @@
+OUTPUT_FORMAT(elf32-littlearm)
+GROUP ( libRoboRIO_FRC_ChipObject.so.1.2.0 )
diff --git a/aos/externals/allwpilib/ni-libraries/libRoboRIO_FRC_ChipObject.so.1.2.0 b/aos/externals/allwpilib/ni-libraries/libRoboRIO_FRC_ChipObject.so.1.2.0
new file mode 100755
index 0000000..316bdce
--- /dev/null
+++ b/aos/externals/allwpilib/ni-libraries/libRoboRIO_FRC_ChipObject.so.1.2.0
Binary files differ
diff --git a/aos/externals/allwpilib/ni-libraries/libi2c.so b/aos/externals/allwpilib/ni-libraries/libi2c.so
new file mode 100644
index 0000000..673ca37
--- /dev/null
+++ b/aos/externals/allwpilib/ni-libraries/libi2c.so
@@ -0,0 +1,2 @@
+OUTPUT_FORMAT(elf32-littlearm)
+GROUP ( libi2c.so.1 )
diff --git a/aos/externals/allwpilib/ni-libraries/libi2c.so.1 b/aos/externals/allwpilib/ni-libraries/libi2c.so.1
new file mode 100644
index 0000000..e10758b
--- /dev/null
+++ b/aos/externals/allwpilib/ni-libraries/libi2c.so.1
@@ -0,0 +1,2 @@
+OUTPUT_FORMAT(elf32-littlearm)
+GROUP ( libi2c.so.1.0 )
diff --git a/aos/externals/allwpilib/ni-libraries/libi2c.so.1.0 b/aos/externals/allwpilib/ni-libraries/libi2c.so.1.0
new file mode 100644
index 0000000..68c5890
--- /dev/null
+++ b/aos/externals/allwpilib/ni-libraries/libi2c.so.1.0
@@ -0,0 +1,2 @@
+OUTPUT_FORMAT(elf32-littlearm)
+GROUP ( libi2c.so.1.0.0 )
diff --git a/aos/externals/allwpilib/ni-libraries/libi2c.so.1.0.0 b/aos/externals/allwpilib/ni-libraries/libi2c.so.1.0.0
new file mode 100755
index 0000000..34740ca
--- /dev/null
+++ b/aos/externals/allwpilib/ni-libraries/libi2c.so.1.0.0
Binary files differ
diff --git a/aos/externals/allwpilib/ni-libraries/liblog4cpp_gcc-4.4-arm_v2_3.so b/aos/externals/allwpilib/ni-libraries/liblog4cpp_gcc-4.4-arm_v2_3.so
new file mode 100755
index 0000000..46f6780
--- /dev/null
+++ b/aos/externals/allwpilib/ni-libraries/liblog4cpp_gcc-4.4-arm_v2_3.so
Binary files differ
diff --git a/aos/externals/allwpilib/ni-libraries/libni_emb.so b/aos/externals/allwpilib/ni-libraries/libni_emb.so
new file mode 100644
index 0000000..93678d5
--- /dev/null
+++ b/aos/externals/allwpilib/ni-libraries/libni_emb.so
@@ -0,0 +1,2 @@
+OUTPUT_FORMAT(elf32-littlearm)
+GROUP ( libni_emb.so.7 )
diff --git a/aos/externals/allwpilib/ni-libraries/libni_emb.so.7 b/aos/externals/allwpilib/ni-libraries/libni_emb.so.7
new file mode 100644
index 0000000..aa703b7
--- /dev/null
+++ b/aos/externals/allwpilib/ni-libraries/libni_emb.so.7
@@ -0,0 +1,2 @@
+OUTPUT_FORMAT(elf32-littlearm)
+GROUP ( libni_emb.so.7.0 )
diff --git a/aos/externals/allwpilib/ni-libraries/libni_emb.so.7.0 b/aos/externals/allwpilib/ni-libraries/libni_emb.so.7.0
new file mode 100644
index 0000000..da52c36
--- /dev/null
+++ b/aos/externals/allwpilib/ni-libraries/libni_emb.so.7.0
@@ -0,0 +1,2 @@
+OUTPUT_FORMAT(elf32-littlearm)
+GROUP ( libni_emb.so.7.0.0 )
diff --git a/aos/externals/allwpilib/ni-libraries/libni_emb.so.7.0.0 b/aos/externals/allwpilib/ni-libraries/libni_emb.so.7.0.0
new file mode 100755
index 0000000..363b852
--- /dev/null
+++ b/aos/externals/allwpilib/ni-libraries/libni_emb.so.7.0.0
Binary files differ
diff --git a/aos/externals/allwpilib/ni-libraries/libni_rtlog.so b/aos/externals/allwpilib/ni-libraries/libni_rtlog.so
new file mode 100644
index 0000000..3045681
--- /dev/null
+++ b/aos/externals/allwpilib/ni-libraries/libni_rtlog.so
@@ -0,0 +1,2 @@
+OUTPUT_FORMAT(elf32-littlearm)
+GROUP ( libni_rtlog.so.2 )
diff --git a/aos/externals/allwpilib/ni-libraries/libni_rtlog.so.2 b/aos/externals/allwpilib/ni-libraries/libni_rtlog.so.2
new file mode 100644
index 0000000..f7fc193
--- /dev/null
+++ b/aos/externals/allwpilib/ni-libraries/libni_rtlog.so.2
@@ -0,0 +1,2 @@
+OUTPUT_FORMAT(elf32-littlearm)
+GROUP ( libni_rtlog.so.2.3 )
diff --git a/aos/externals/allwpilib/ni-libraries/libni_rtlog.so.2.3 b/aos/externals/allwpilib/ni-libraries/libni_rtlog.so.2.3
new file mode 100644
index 0000000..f673867
--- /dev/null
+++ b/aos/externals/allwpilib/ni-libraries/libni_rtlog.so.2.3
@@ -0,0 +1,2 @@
+OUTPUT_FORMAT(elf32-littlearm)
+GROUP ( libni_rtlog.so.2.3.0 )
diff --git a/aos/externals/allwpilib/ni-libraries/libni_rtlog.so.2.3.0 b/aos/externals/allwpilib/ni-libraries/libni_rtlog.so.2.3.0
new file mode 100755
index 0000000..2d6d6ae
--- /dev/null
+++ b/aos/externals/allwpilib/ni-libraries/libni_rtlog.so.2.3.0
Binary files differ
diff --git a/aos/externals/allwpilib/ni-libraries/libniimaqdx.so b/aos/externals/allwpilib/ni-libraries/libniimaqdx.so
new file mode 100644
index 0000000..ed97ce7
--- /dev/null
+++ b/aos/externals/allwpilib/ni-libraries/libniimaqdx.so
@@ -0,0 +1,2 @@
+OUTPUT_FORMAT(elf32-littlearm)
+GROUP ( libniimaqdx.so.14 )
diff --git a/aos/externals/allwpilib/ni-libraries/libniimaqdx.so.14 b/aos/externals/allwpilib/ni-libraries/libniimaqdx.so.14
new file mode 100644
index 0000000..6edea92
--- /dev/null
+++ b/aos/externals/allwpilib/ni-libraries/libniimaqdx.so.14
@@ -0,0 +1,2 @@
+OUTPUT_FORMAT(elf32-littlearm)
+GROUP ( libniimaqdx.so.14.0 )
diff --git a/aos/externals/allwpilib/ni-libraries/libniimaqdx.so.14.0 b/aos/externals/allwpilib/ni-libraries/libniimaqdx.so.14.0
new file mode 100644
index 0000000..12b642e
--- /dev/null
+++ b/aos/externals/allwpilib/ni-libraries/libniimaqdx.so.14.0
@@ -0,0 +1,2 @@
+OUTPUT_FORMAT(elf32-littlearm)
+GROUP ( libniimaqdx.so.14.0.0 )
diff --git a/aos/externals/allwpilib/ni-libraries/libniimaqdx.so.14.0.0 b/aos/externals/allwpilib/ni-libraries/libniimaqdx.so.14.0.0
new file mode 100755
index 0000000..27cd188
--- /dev/null
+++ b/aos/externals/allwpilib/ni-libraries/libniimaqdx.so.14.0.0
Binary files differ
diff --git a/aos/externals/allwpilib/ni-libraries/libnirio_emb_can.so b/aos/externals/allwpilib/ni-libraries/libnirio_emb_can.so
new file mode 100644
index 0000000..eeb8133
--- /dev/null
+++ b/aos/externals/allwpilib/ni-libraries/libnirio_emb_can.so
@@ -0,0 +1,2 @@
+OUTPUT_FORMAT(elf32-littlearm)
+GROUP ( libnirio_emb_can.so.14 )
diff --git a/aos/externals/allwpilib/ni-libraries/libnirio_emb_can.so.14 b/aos/externals/allwpilib/ni-libraries/libnirio_emb_can.so.14
new file mode 100644
index 0000000..c0548d7
--- /dev/null
+++ b/aos/externals/allwpilib/ni-libraries/libnirio_emb_can.so.14
@@ -0,0 +1,2 @@
+OUTPUT_FORMAT(elf32-littlearm)
+GROUP ( libnirio_emb_can.so.14.0 )
diff --git a/aos/externals/allwpilib/ni-libraries/libnirio_emb_can.so.14.0 b/aos/externals/allwpilib/ni-libraries/libnirio_emb_can.so.14.0
new file mode 100644
index 0000000..b4e836f
--- /dev/null
+++ b/aos/externals/allwpilib/ni-libraries/libnirio_emb_can.so.14.0
@@ -0,0 +1,2 @@
+OUTPUT_FORMAT(elf32-littlearm)
+GROUP ( libnirio_emb_can.so.14.0.0 )
diff --git a/aos/externals/allwpilib/ni-libraries/libnirio_emb_can.so.14.0.0 b/aos/externals/allwpilib/ni-libraries/libnirio_emb_can.so.14.0.0
new file mode 100755
index 0000000..3e04ea8
--- /dev/null
+++ b/aos/externals/allwpilib/ni-libraries/libnirio_emb_can.so.14.0.0
Binary files differ
diff --git a/aos/externals/allwpilib/ni-libraries/libnivision.so b/aos/externals/allwpilib/ni-libraries/libnivision.so
new file mode 100644
index 0000000..67c0c57
--- /dev/null
+++ b/aos/externals/allwpilib/ni-libraries/libnivision.so
@@ -0,0 +1,2 @@
+OUTPUT_FORMAT(elf32-littlearm)
+GROUP ( libnivision.so.14 )
diff --git a/aos/externals/allwpilib/ni-libraries/libnivision.so.14 b/aos/externals/allwpilib/ni-libraries/libnivision.so.14
new file mode 100644
index 0000000..4306944
--- /dev/null
+++ b/aos/externals/allwpilib/ni-libraries/libnivision.so.14
@@ -0,0 +1,2 @@
+OUTPUT_FORMAT(elf32-littlearm)
+GROUP ( libnivision.so.14.0 )
diff --git a/aos/externals/allwpilib/ni-libraries/libnivision.so.14.0 b/aos/externals/allwpilib/ni-libraries/libnivision.so.14.0
new file mode 100644
index 0000000..3a4d263
--- /dev/null
+++ b/aos/externals/allwpilib/ni-libraries/libnivision.so.14.0
@@ -0,0 +1,2 @@
+OUTPUT_FORMAT(elf32-littlearm)
+GROUP ( libnivision.so.14.0.0 )
diff --git a/aos/externals/allwpilib/ni-libraries/libnivision.so.14.0.0 b/aos/externals/allwpilib/ni-libraries/libnivision.so.14.0.0
new file mode 100755
index 0000000..a0a4810
--- /dev/null
+++ b/aos/externals/allwpilib/ni-libraries/libnivision.so.14.0.0
Binary files differ
diff --git a/aos/externals/allwpilib/ni-libraries/libnivissvc.so b/aos/externals/allwpilib/ni-libraries/libnivissvc.so
new file mode 100644
index 0000000..e0bbd48
--- /dev/null
+++ b/aos/externals/allwpilib/ni-libraries/libnivissvc.so
@@ -0,0 +1,2 @@
+OUTPUT_FORMAT(elf32-littlearm)
+GROUP ( libnivissvc.so.14 )
diff --git a/aos/externals/allwpilib/ni-libraries/libnivissvc.so.14 b/aos/externals/allwpilib/ni-libraries/libnivissvc.so.14
new file mode 100644
index 0000000..205cc35
--- /dev/null
+++ b/aos/externals/allwpilib/ni-libraries/libnivissvc.so.14
@@ -0,0 +1,2 @@
+OUTPUT_FORMAT(elf32-littlearm)
+GROUP ( libnivissvc.so.14.0 )
diff --git a/aos/externals/allwpilib/ni-libraries/libnivissvc.so.14.0 b/aos/externals/allwpilib/ni-libraries/libnivissvc.so.14.0
new file mode 100644
index 0000000..26ffd42
--- /dev/null
+++ b/aos/externals/allwpilib/ni-libraries/libnivissvc.so.14.0
@@ -0,0 +1,2 @@
+OUTPUT_FORMAT(elf32-littlearm)
+GROUP ( libnivissvc.so.14.0.0 )
diff --git a/aos/externals/allwpilib/ni-libraries/libnivissvc.so.14.0.0 b/aos/externals/allwpilib/ni-libraries/libnivissvc.so.14.0.0
new file mode 100755
index 0000000..480578d
--- /dev/null
+++ b/aos/externals/allwpilib/ni-libraries/libnivissvc.so.14.0.0
Binary files differ
diff --git a/aos/externals/allwpilib/ni-libraries/libspi.so b/aos/externals/allwpilib/ni-libraries/libspi.so
new file mode 100644
index 0000000..e77606f
--- /dev/null
+++ b/aos/externals/allwpilib/ni-libraries/libspi.so
@@ -0,0 +1,2 @@
+OUTPUT_FORMAT(elf32-littlearm)
+GROUP ( libspi.so.1 )
diff --git a/aos/externals/allwpilib/ni-libraries/libspi.so.1 b/aos/externals/allwpilib/ni-libraries/libspi.so.1
new file mode 100644
index 0000000..5aa0632
--- /dev/null
+++ b/aos/externals/allwpilib/ni-libraries/libspi.so.1
@@ -0,0 +1,2 @@
+OUTPUT_FORMAT(elf32-littlearm)
+GROUP ( libspi.so.1.0 )
diff --git a/aos/externals/allwpilib/ni-libraries/libspi.so.1.0 b/aos/externals/allwpilib/ni-libraries/libspi.so.1.0
new file mode 100644
index 0000000..9f016df
--- /dev/null
+++ b/aos/externals/allwpilib/ni-libraries/libspi.so.1.0
@@ -0,0 +1,2 @@
+OUTPUT_FORMAT(elf32-littlearm)
+GROUP ( libspi.so.1.0.0 )
diff --git a/aos/externals/allwpilib/ni-libraries/libspi.so.1.0.0 b/aos/externals/allwpilib/ni-libraries/libspi.so.1.0.0
new file mode 100755
index 0000000..5f69f31
--- /dev/null
+++ b/aos/externals/allwpilib/ni-libraries/libspi.so.1.0.0
Binary files differ
diff --git a/aos/externals/allwpilib/ni-libraries/libvisa.so b/aos/externals/allwpilib/ni-libraries/libvisa.so
new file mode 100755
index 0000000..4ca711c
--- /dev/null
+++ b/aos/externals/allwpilib/ni-libraries/libvisa.so
Binary files differ
diff --git a/aos/externals/allwpilib/ni-libraries/libwpi.so b/aos/externals/allwpilib/ni-libraries/libwpi.so
new file mode 100644
index 0000000..429ff80
--- /dev/null
+++ b/aos/externals/allwpilib/ni-libraries/libwpi.so
@@ -0,0 +1,3 @@
+/* GNU ld script */
+OUTPUT_FORMAT(elf32-littlearm)
+INPUT ( -lwpi_2015 )
diff --git a/aos/externals/allwpilib/ni-libraries/libwpi_2015.so b/aos/externals/allwpilib/ni-libraries/libwpi_2015.so
new file mode 100644
index 0000000..3589af2
--- /dev/null
+++ b/aos/externals/allwpilib/ni-libraries/libwpi_2015.so
@@ -0,0 +1,3 @@
+/* GNU ld script */
+OUTPUT_FORMAT(elf32-littlearm)
+GROUP ( AS_NEEDED ( -lwpilib_nonshared -lHALAthena -lNetworkTables -lFRC_NetworkCommunication -li2c -lni_emb -lNiFpgaLv -lNiFpga -lnirio_emb_can -lNiRioSrv -lni_rtlog -lRoboRIO_FRC_ChipObject -lspi -lvisa -ldl -lpthread -lrt -lGCBase_gcc-4.4-arm_v2_3 -lGenApi_gcc-4.4-arm_v2_3 -lLog_gcc-4.4-arm_v2_3 -lMathParser_gcc-4.4-arm_v2_3 -llog4cpp_gcc-4.4-arm_v2_3 -lniimaqdx -lnivision -lnivissvc ) )
diff --git a/aos/externals/allwpilib/sync.sh b/aos/externals/allwpilib/sync.sh
new file mode 100755
index 0000000..8e05233
--- /dev/null
+++ b/aos/externals/allwpilib/sync.sh
@@ -0,0 +1,20 @@
+#!/bin/bash -e
+
+# This script copies the correct directories over from an allwpilib git
+# repository (which needs to have a working tree).
+# THIS WILL ERASE ANY LOCAL CHANGES TO THE WPILIB SOURCES!
+
+# Takes 1 argument: the folder to copy from.
+
+cd $(dirname $0)
+
+SOURCE=$1
+DIRS="wpilibc hal ni-libraries"
+
+[ -n "${SOURCE}" ] || ( echo "Need a directory to sync from!" ; exit 1 )
+[ -d "${SOURCE}" ] || ( echo "${SOURCE} is not a directory. Aborting!" ; exit 1 )
+
+for dir in ${DIRS}; do
+ [ -d ${dir} ] && rm -r ${dir}
+ cp -r ${SOURCE}/${dir} ./
+done
diff --git a/aos/externals/allwpilib/wpilibc/CMakeLists.txt b/aos/externals/allwpilib/wpilibc/CMakeLists.txt
new file mode 100644
index 0000000..de4a7ef
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/CMakeLists.txt
@@ -0,0 +1,5 @@
+cmake_minimum_required(VERSION 2.8)
+project(WPILibC)
+
+add_subdirectory(wpilibC++)
+add_subdirectory(wpilibC++Devices)
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++/CMakeLists.txt b/aos/externals/allwpilib/wpilibc/wpilibC++/CMakeLists.txt
new file mode 100644
index 0000000..4af3fc9
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++/CMakeLists.txt
@@ -0,0 +1,5 @@
+cmake_minimum_required(VERSION 2.8)
+project(WPILibC++)
+
+INSTALL(DIRECTORY include DESTINATION ${CMAKE_INSTALL_PREFIX} COMPONENT headers)
+
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++/include/Base.h b/aos/externals/allwpilib/wpilibc/wpilibC++/include/Base.h
new file mode 100644
index 0000000..74073e8
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++/include/Base.h
@@ -0,0 +1,12 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+#pragma once
+
+// A macro to disallow the copy constructor and operator= functions
+// This should be used in the private: declarations for a class
+#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
+ TypeName(const TypeName&) = delete; \
+ void operator=(const TypeName&) = delete
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++/include/Controller.h b/aos/externals/allwpilib/wpilibc/wpilibC++/include/Controller.h
new file mode 100644
index 0000000..99eba88
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++/include/Controller.h
@@ -0,0 +1,34 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+#pragma once
+
+#include <stdint.h>
+#include <cmath>
+#include <pthread.h>
+#include <semaphore.h>
+
+/**
+ * Interface for Controllers
+ * Common interface for controllers. Controllers run control loops, the most common
+ * are PID controllers and their variants, but this includes anything that is controlling
+ * an actuator in a separate thread.
+ */
+class Controller
+{
+public:
+ virtual ~Controller() {};
+
+ /**
+ * Allows the control loop to run
+ */
+ virtual void Enable() = 0;
+
+ /**
+ * Stops the control loop from running until explicitly re-enabled by calling enable()
+ */
+ virtual void Disable() = 0;
+};
+
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++/include/Error.h b/aos/externals/allwpilib/wpilibc/wpilibC++/include/Error.h
new file mode 100644
index 0000000..4fbe0f0
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++/include/Error.h
@@ -0,0 +1,54 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+#pragma once
+
+#include "Base.h"
+#include <string>
+#include <stdint.h>
+
+// Forward declarations
+class ErrorBase;
+
+/**
+ * Error object represents a library error.
+ */
+class Error
+{
+public:
+ typedef int32_t Code;
+
+ Error();
+ ~Error();
+ void Clone(Error &error);
+ Code GetCode() const;
+ const char *GetMessage() const;
+ const char *GetFilename() const;
+ const char *GetFunction() const;
+ uint32_t GetLineNumber() const;
+ const ErrorBase* GetOriginatingObject() const;
+ double GetTimestamp() const;
+ void Clear();
+ void Set(Code code, const char* contextMessage, const char* filename, const char *function,
+ uint32_t lineNumber, const ErrorBase* originatingObject);
+ static void EnableSuspendOnError(bool enable)
+ {
+ m_suspendOnErrorEnabled = enable;
+ }
+
+private:
+ void Report();
+
+ Code m_code;
+ std::string m_message;
+ std::string m_filename;
+ std::string m_function;
+ uint32_t m_lineNumber;
+ const ErrorBase* m_originatingObject;
+ double m_timestamp;
+
+ static bool m_suspendOnErrorEnabled;
+ DISALLOW_COPY_AND_ASSIGN(Error);
+};
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++/include/ErrorBase.h b/aos/externals/allwpilib/wpilibc/wpilibC++/include/ErrorBase.h
new file mode 100644
index 0000000..94e9164
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++/include/ErrorBase.h
@@ -0,0 +1,64 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+#pragma once
+
+#include "Base.h"
+#include "Error.h"
+#include "HAL/cpp/Synchronized.hpp"
+
+#define wpi_setErrnoErrorWithContext(context) (this->SetErrnoError((context), __FILE__, __FUNCTION__, __LINE__))
+#define wpi_setErrnoError() (wpi_setErrnoErrorWithContext(""))
+#define wpi_setImaqErrorWithContext(code, context) (this->SetImaqError((code), (context), __FILE__, __FUNCTION__, __LINE__))
+#define wpi_setErrorWithContext(code, context) (this->SetError((code), (context), __FILE__, __FUNCTION__, __LINE__))
+#define wpi_setError(code) (wpi_setErrorWithContext(code, ""))
+#define wpi_setStaticErrorWithContext(object, code, context) (object->SetError((code), (context), __FILE__, __FUNCTION__, __LINE__))
+#define wpi_setStaticError(object, code) (wpi_setStaticErrorWithContext(object, code, ""))
+#define wpi_setGlobalErrorWithContext(code, context) (ErrorBase::SetGlobalError((code), (context), __FILE__, __FUNCTION__, __LINE__))
+#define wpi_setGlobalError(code) (wpi_setGlobalErrorWithContext(code, ""))
+#define wpi_setWPIErrorWithContext(error, context) (this->SetWPIError((wpi_error_s_##error), (wpi_error_value_##error), (context), __FILE__, __FUNCTION__, __LINE__))
+#define wpi_setWPIError(error) (wpi_setWPIErrorWithContext(error, ""))
+#define wpi_setStaticWPIErrorWithContext(object, error, context) (object->SetWPIError((wpi_error_s_##error), (context), __FILE__, __FUNCTION__, __LINE__))
+#define wpi_setStaticWPIError(object, error) (wpi_setStaticWPIErrorWithContext(object, error, ""))
+#define wpi_setGlobalWPIErrorWithContext(error, context) (ErrorBase::SetGlobalWPIError((wpi_error_s_##error), (context), __FILE__, __FUNCTION__, __LINE__))
+#define wpi_setGlobalWPIError(error) (wpi_setGlobalWPIErrorWithContext(error, ""))
+
+/**
+ * Base class for most objects.
+ * ErrorBase is the base class for most objects since it holds the generated error
+ * for that object. In addition, there is a single instance of a global error object
+ */
+class ErrorBase
+{
+//TODO: Consider initializing instance variables and cleanup in destructor
+public:
+ virtual ~ErrorBase();
+ virtual Error& GetError();
+ virtual const Error& GetError() const;
+ virtual void SetErrnoError(const char *contextMessage, const char* filename,
+ const char* function, uint32_t lineNumber) const;
+ virtual void SetImaqError(int success, const char *contextMessage, const char* filename,
+ const char* function, uint32_t lineNumber) const;
+ virtual void SetError(Error::Code code, const char *contextMessage, const char* filename,
+ const char* function, uint32_t lineNumber) const;
+ virtual void SetWPIError(const char *errorMessage, Error::Code code, const char *contextMessage,
+ const char* filename, const char* function, uint32_t lineNumber) const;
+ virtual void CloneError(ErrorBase *rhs) const;
+ virtual void ClearError() const;
+ virtual bool StatusIsFatal() const;
+ static void SetGlobalError(Error::Code code, const char *contextMessage, const char* filename,
+ const char* function, uint32_t lineNumber);
+ static void SetGlobalWPIError(const char *errorMessage, const char *contextMessage,
+ const char* filename, const char* function, uint32_t lineNumber);
+ static Error& GetGlobalError();
+protected:
+ mutable Error m_error;
+ // TODO: Replace globalError with a global list of all errors.
+ static Mutex _globalErrorMutex;
+ static Error _globalError;
+ ErrorBase();
+private:
+ DISALLOW_COPY_AND_ASSIGN(ErrorBase);
+};
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++/include/GenericHID.h b/aos/externals/allwpilib/wpilibc/wpilibC++/include/GenericHID.h
new file mode 100644
index 0000000..cb8dfde
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++/include/GenericHID.h
@@ -0,0 +1,38 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+#pragma once
+
+#include <stdint.h>
+
+/** GenericHID Interface
+ */
+class GenericHID
+{
+public:
+ enum JoystickHand
+ {
+ kLeftHand = 0,
+ kRightHand = 1
+ };
+
+ virtual ~GenericHID()
+ {
+ }
+
+ virtual float GetX(JoystickHand hand = kRightHand) = 0;
+ virtual float GetY(JoystickHand hand = kRightHand) = 0;
+ virtual float GetZ() = 0;
+ virtual float GetTwist() = 0;
+ virtual float GetThrottle() = 0;
+ virtual float GetRawAxis(uint32_t axis) = 0;
+
+ virtual bool GetTrigger(JoystickHand hand = kRightHand) = 0;
+ virtual bool GetTop(JoystickHand hand = kRightHand) = 0;
+ virtual bool GetBumper(JoystickHand hand = kRightHand) = 0;
+ virtual bool GetRawButton(uint32_t button) = 0;
+
+ virtual int GetPOV(uint32_t pov = 0) = 0;
+};
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++/include/HLUsageReporting.h b/aos/externals/allwpilib/wpilibc/wpilibC++/include/HLUsageReporting.h
new file mode 100644
index 0000000..359b149
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++/include/HLUsageReporting.h
@@ -0,0 +1,25 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+#pragma once
+
+class HLUsageReportingInterface
+{
+public:
+ virtual ~HLUsageReportingInterface() {};
+ virtual void ReportScheduler() = 0;
+ virtual void ReportSmartDashboard() = 0;
+};
+
+class HLUsageReporting
+{
+private:
+ static HLUsageReportingInterface* impl;
+
+public:
+ static void SetImplementation(HLUsageReportingInterface* i);
+ static void ReportScheduler();
+ static void ReportSmartDashboard();
+};
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++/include/Notifier.h b/aos/externals/allwpilib/wpilibc/wpilibC++/include/Notifier.h
new file mode 100644
index 0000000..9579e67
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++/include/Notifier.h
@@ -0,0 +1,45 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+#pragma once
+
+#include "ErrorBase.h"
+#include "Task.h"
+#include "HAL/cpp/Synchronized.hpp"
+
+typedef void (*TimerEventHandler)(void *param);
+
+class Notifier : public ErrorBase
+{
+public:
+ Notifier(TimerEventHandler handler, void *param = NULL);
+ virtual ~Notifier();
+ void StartSingle(double delay);
+ void StartPeriodic(double period);
+ void Stop();
+private:
+ static Notifier *timerQueueHead;
+ static ReentrantMutex queueSemaphore;
+ static void* m_notifier;
+ static int refcount;
+
+ static void ProcessQueue(uint32_t mask, void *params); // process the timer queue on a timer event
+ static void UpdateAlarm(); // update the FPGA alarm since the queue has changed
+ void InsertInQueue(bool reschedule); // insert this Notifier in the timer queue
+ void DeleteFromQueue(); // delete this Notifier from the timer queue
+ TimerEventHandler m_handler; // address of the handler
+ void *m_param; // a parameter to pass to the handler
+ double m_period; // the relative time (either periodic or single)
+ double m_expirationTime; // absolute expiration time for the current event
+ Notifier *m_nextEvent; // next Nofifier event
+ bool m_periodic; // true if this is a periodic event
+ bool m_queued; // indicates if this entry is queued
+ SEMAPHORE_ID m_handlerSemaphore; // held by interrupt manager task while handler call is in progress
+
+ DISALLOW_COPY_AND_ASSIGN(Notifier);
+
+ static Task *task;
+ static void Run();
+};
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++/include/PIDOutput.h b/aos/externals/allwpilib/wpilibc/wpilibC++/include/PIDOutput.h
new file mode 100644
index 0000000..a2e2c95
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++/include/PIDOutput.h
@@ -0,0 +1,20 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+#pragma once
+
+#include "Base.h"
+
+/**
+ * PIDOutput interface is a generic output for the PID class.
+ * PWMs use this class.
+ * Users implement this interface to allow for a PIDController to
+ * read directly from the inputs
+ */
+class PIDOutput
+{
+public:
+ virtual void PIDWrite(float output) = 0;
+};
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++/include/PIDSource.h b/aos/externals/allwpilib/wpilibc/wpilibC++/include/PIDSource.h
new file mode 100644
index 0000000..e81bd28
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++/include/PIDSource.h
@@ -0,0 +1,18 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+#pragma once
+
+/**
+ * PIDSource interface is a generic sensor source for the PID class.
+ * All sensors that can be used with the PID class will implement the PIDSource that
+ * returns a standard value that will be used in the PID code.
+ */
+class PIDSource
+{
+public:
+ enum PIDSourceParameter {kDistance, kRate, kAngle};
+ virtual double PIDGet() = 0;
+};
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++/include/Resource.h b/aos/externals/allwpilib/wpilibc/wpilibC++/include/Resource.h
new file mode 100644
index 0000000..4eb9427
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++/include/Resource.h
@@ -0,0 +1,40 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+#pragma once
+
+#include "ErrorBase.h"
+#include "HAL/cpp/Synchronized.hpp"
+#include <stdint.h>
+
+/**
+ * The Resource class is a convenient way to track allocated resources.
+ * It tracks them as indicies in the range [0 .. elements - 1].
+ * E.g. the library uses this to track hardware channel allocation.
+ *
+ * The Resource class does not allocate the hardware channels or other
+ * resources; it just tracks which indices were marked in use by
+ * Allocate and not yet freed by Free.
+ */
+class Resource : public ErrorBase
+{
+public:
+ virtual ~Resource();
+ static void CreateResourceObject(Resource **r, uint32_t elements);
+ uint32_t Allocate(const char *resourceDesc);
+ uint32_t Allocate(uint32_t index, const char *resourceDesc);
+ void Free(uint32_t index);
+
+private:
+ explicit Resource(uint32_t size);
+
+ bool *m_isAllocated;
+ ReentrantMutex m_allocateLock;
+ uint32_t m_size;
+
+ static ReentrantMutex m_createLock;
+
+ DISALLOW_COPY_AND_ASSIGN(Resource);
+};
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++/include/RobotState.h b/aos/externals/allwpilib/wpilibc/wpilibC++/include/RobotState.h
new file mode 100644
index 0000000..ee25493
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++/include/RobotState.h
@@ -0,0 +1,33 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+#pragma once
+
+class RobotStateInterface
+{
+public:
+ virtual ~RobotStateInterface() {};
+ virtual bool IsDisabled() = 0;
+ virtual bool IsEnabled() = 0;
+ virtual bool IsOperatorControl() = 0;
+ virtual bool IsAutonomous() = 0;
+ virtual bool IsTest() = 0;
+};
+
+class RobotState
+{
+private:
+ static RobotStateInterface* impl;
+
+public:
+ static void SetImplementation(RobotStateInterface* i);
+ static bool IsDisabled();
+ static bool IsEnabled();
+ static bool IsOperatorControl();
+ static bool IsAutonomous();
+ static bool IsTest();
+};
+
+
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++/include/SensorBase.h b/aos/externals/allwpilib/wpilibc/wpilibC++/include/SensorBase.h
new file mode 100644
index 0000000..65e16ec
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++/include/SensorBase.h
@@ -0,0 +1,63 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+#pragma once
+
+#include "ErrorBase.h"
+#include <stdio.h>
+#include "Base.h"
+
+class DigitalGlitchFilter;
+
+/**
+ * Base class for all sensors.
+ * Stores most recent status information as well as containing utility functions for checking
+ * channels and error processing.
+ */
+class SensorBase : public ErrorBase
+{
+public:
+ SensorBase();
+ virtual ~SensorBase();
+ static void DeleteSingletons();
+
+ static uint32_t GetDefaultSolenoidModule()
+ {
+ return 0;
+ }
+
+ static bool CheckSolenoidModule(uint8_t moduleNumber);
+ static bool CheckDigitalChannel(uint32_t channel);
+ static bool CheckRelayChannel(uint32_t channel);
+ static bool CheckPWMChannel(uint32_t channel);
+ static bool CheckAnalogInput(uint32_t channel);
+ static bool CheckAnalogOutput(uint32_t channel);
+ static bool CheckSolenoidChannel(uint32_t channel);
+ static bool CheckPDPChannel(uint32_t channel);
+
+ static const uint32_t kDigitalChannels = 26;
+ static const uint32_t kAnalogInputs = 8;
+ static const uint32_t kAnalogOutputs = 2;
+ static const uint32_t kSolenoidChannels = 8;
+ static const uint32_t kSolenoidModules = 2;
+ static const uint32_t kPwmChannels = 20;
+ static const uint32_t kRelayChannels = 8;
+ static const uint32_t kPDPChannels = 16;
+ static const uint32_t kChassisSlots = 8;
+
+protected:
+ void AddToSingletonList();
+
+ static void* m_digital_ports[kDigitalChannels];
+ static void* m_relay_ports[kRelayChannels];
+ static void* m_pwm_ports[kPwmChannels];
+
+private:
+ DISALLOW_COPY_AND_ASSIGN(SensorBase);
+ static SensorBase *m_singletonList;
+ SensorBase *m_nextSingleton;
+
+ friend class DigitalGlitchFilter;
+};
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++/include/Task.h b/aos/externals/allwpilib/wpilibc/wpilibC++/include/Task.h
new file mode 100644
index 0000000..ca1754a
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++/include/Task.h
@@ -0,0 +1,52 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+#pragma once
+
+#include "ErrorBase.h"
+#include "HAL/Task.hpp"
+
+/**
+ * WPI task is a wrapper for the native Task object.
+ * All WPILib tasks are managed by a static task manager for simplified cleanup.
+ **/
+class Task : public ErrorBase
+{
+public:
+ static const uint32_t kDefaultPriority = 101;
+
+ Task(const char* name, FUNCPTR function, int32_t priority = kDefaultPriority,
+ uint32_t stackSize = 20000);
+ virtual ~Task();
+
+ bool Start(uint32_t arg0 = 0, uint32_t arg1 = 0, uint32_t arg2 = 0, uint32_t arg3 = 0,
+ uint32_t arg4 = 0, uint32_t arg5 = 0, uint32_t arg6 = 0, uint32_t arg7 = 0,
+ uint32_t arg8 = 0, uint32_t arg9 = 0);
+ bool Restart();
+ bool Stop();
+
+ bool IsReady();
+ bool IsSuspended();
+
+ bool Suspend();
+ bool Resume();
+
+ bool Verify();
+
+ int32_t GetPriority();
+ bool SetPriority(int32_t priority);
+ const char* GetName();
+ TASK GetID();
+
+private:
+ FUNCPTR m_function;
+ char* m_taskName;
+ TASK m_taskID;
+ uint32_t m_stackSize;
+ int m_priority;
+ bool HandleError(STATUS results);
+ DISALLOW_COPY_AND_ASSIGN(Task);
+};
+
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++/include/Timer.h b/aos/externals/allwpilib/wpilibc/wpilibC++/include/Timer.h
new file mode 100644
index 0000000..fc3f9af
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++/include/Timer.h
@@ -0,0 +1,48 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+#pragma once
+
+#include "HAL/cpp/Synchronized.hpp"
+#include "Base.h"
+
+typedef void (*TimerInterruptHandler)(void *param);
+
+void Wait(double seconds);
+double GetClock();
+double GetTime();
+
+/**
+ * Timer objects measure accumulated time in seconds.
+ * The timer object functions like a stopwatch. It can be started, stopped, and cleared. When the
+ * timer is running its value counts up in seconds. When stopped, the timer holds the current
+ * value. The implementation simply records the time when started and subtracts the current time
+ * whenever the value is requested.
+ */
+class Timer
+{
+public:
+ Timer();
+ virtual ~Timer();
+ double Get();
+ void Reset();
+ void Start();
+ void Stop();
+ bool HasPeriodPassed(double period);
+
+ static double GetFPGATimestamp();
+ static double GetPPCTimestamp();
+ static double GetMatchTime();
+
+ // The time, in seconds, at which the 32-bit FPGA timestamp rolls over to 0
+ static constexpr double kRolloverTime = (1ll << 32) / 1e6;
+
+private:
+ double m_startTime;
+ double m_accumulatedTime;
+ bool m_running;
+ Mutex m_lock;
+ DISALLOW_COPY_AND_ASSIGN(Timer);
+};
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++/include/Utility.h b/aos/externals/allwpilib/wpilibc/wpilibC++/include/Utility.h
new file mode 100644
index 0000000..f081489
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++/include/Utility.h
@@ -0,0 +1,34 @@
+/*---------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*---------------------------------------------------------------------------*/
+#pragma once
+
+/** @file
+ * Contains global utility functions
+ */
+
+#include <stdint.h>
+#include <string>
+
+#define wpi_assert(condition) wpi_assert_impl(condition, #condition, NULL, __FILE__, __LINE__, __FUNCTION__)
+#define wpi_assertWithMessage(condition, message) wpi_assert_impl(condition, #condition, message, __FILE__, __LINE__, __FUNCTION__)
+
+#define wpi_assertEqual(a, b) wpi_assertEqual_impl(a, b, #a, #b, NULL, __FILE__, __LINE__, __FUNCTION__)
+#define wpi_assertEqualWithMessage(a, b, message) wpi_assertEqual_impl(a, b, #a, #b, message, __FILE__, __LINE__, __FUNCTION__)
+
+#define wpi_assertNotEqual(a, b) wpi_assertNotEqual_impl(a, b, #a, #b, NULL, __FILE__, __LINE__, __FUNCTION__)
+#define wpi_assertNotEqualWithMessage(a, b, message) wpi_assertNotEqual_impl(a, b, #a, #b, message, __FILE__, __LINE__, __FUNCTION__)
+
+bool wpi_assert_impl(bool conditionValue, const char *conditionText, const char *message, const char *fileName, uint32_t lineNumber, const char *funcName);
+bool wpi_assertEqual_impl(int valueA, int valueB, const char *valueAString, const char *valueBString, const char *message, const char *fileName,uint32_t lineNumber, const char *funcName);
+bool wpi_assertNotEqual_impl(int valueA, int valueB, const char *valueAString, const char *valueBString, const char *message, const char *fileName,uint32_t lineNumber, const char *funcName);
+
+void wpi_suspendOnAssertEnabled(bool enabled);
+
+uint16_t GetFPGAVersion();
+uint32_t GetFPGARevision();
+uint32_t GetFPGATime();
+bool GetUserButton();
+std::string GetStackTrace(uint32_t offset);
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++/include/WPIErrors.h b/aos/externals/allwpilib/wpilibc/wpilibC++/include/WPIErrors.h
new file mode 100644
index 0000000..9e37715
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++/include/WPIErrors.h
@@ -0,0 +1,78 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+#pragma once
+
+#ifdef WPI_ERRORS_DEFINE_STRINGS
+#define S(label, offset, message) const char *wpi_error_s_##label = message ; \
+ const int32_t wpi_error_value_##label = offset
+#else
+#define S(label, offset, message) extern const char *wpi_error_s_##label ; \
+ const int32_t wpi_error_value_##label = offset
+#endif
+
+/*
+ * Fatal errors
+ */
+S(ModuleIndexOutOfRange, -1, "Allocating module that is out of range or not found");
+S(ChannelIndexOutOfRange, -1, "Allocating channel that is out of range");
+S(NotAllocated, -2, "Attempting to free unallocated resource");
+S(ResourceAlreadyAllocated, -3, "Attempted to reuse an allocated resource");
+S(NoAvailableResources, -4, "No available resources to allocate");
+S(NullParameter, -5, "A pointer parameter to a method is NULL");
+S(Timeout, -6, "A timeout has been exceeded");
+S(CompassManufacturerError, -7, "Compass manufacturer doesn't match HiTechnic");
+S(CompassTypeError, -8, "Compass type doesn't match expected type for HiTechnic compass");
+S(IncompatibleMode, -9, "The object is in an incompatible mode");
+S(AnalogTriggerLimitOrderError, -10, "AnalogTrigger limits error. Lower limit > Upper Limit");
+S(AnalogTriggerPulseOutputError, -11, "Attempted to read AnalogTrigger pulse output.");
+S(TaskError, -12, "Task can't be started");
+S(TaskIDError, -13, "Task error: Invalid ID.");
+S(TaskDeletedError, -14, "Task error: Task already deleted.");
+S(TaskOptionsError, -15, "Task error: Invalid options.");
+S(TaskMemoryError, -16, "Task can't be started due to insufficient memory.");
+S(TaskPriorityError, -17, "Task error: Invalid priority [1-255].");
+S(DriveUninitialized, -18, "RobotDrive not initialized for the C interface");
+S(CompressorNonMatching, -19, "Compressor slot/channel doesn't match previous instance");
+S(CompressorAlreadyDefined, -20, "Creating a second compressor instance");
+S(CompressorUndefined, -21, "Using compressor functions without defining compressor");
+S(InconsistentArrayValueAdded, -22, "When packing data into an array to the dashboard, not all values added were of the same type.");
+S(MismatchedComplexTypeClose, -23, "When packing data to the dashboard, a Close for a complex type was called without a matching Open.");
+S(DashboardDataOverflow, -24, "When packing data to the dashboard, too much data was packed and the buffer overflowed.");
+S(DashboardDataCollision, -25, "The same buffer was used for packing data and for printing.");
+S(EnhancedIOMissing, -26, "IO is not attached or Enhanced IO is not enabled.");
+S(LineNotOutput, -27, "Cannot SetDigitalOutput for a line not configured for output.");
+S(ParameterOutOfRange, -28, "A parameter is out of range.");
+S(SPIClockRateTooLow, -29, "SPI clock rate was below the minimum supported");
+S(JaguarVersionError, -30, "Jaguar firmware version error");
+S(JaguarMessageNotFound, -31, "Jaguar message not found");
+S(NetworkTablesReadError, -40, "Error reading NetworkTables socket");
+S(NetworkTablesBufferFull, -41, "Buffer full writing to NetworkTables socket");
+S(NetworkTablesWrongType, -42, "The wrong type was read from the NetworkTables entry");
+S(NetworkTablesCorrupt, -43, "NetworkTables data stream is corrupt");
+S(SmartDashboardMissingKey, -43, "SmartDashboard data does not exist");
+S(CommandIllegalUse, -50, "Illegal use of Command");
+S(UnsupportedInSimulation, -80, "Unsupported in simulation");
+
+/*
+ * Warnings
+ */
+S(SampleRateTooHigh, 1, "Analog module sample rate is too high");
+S(VoltageOutOfRange, 2, "Voltage to convert to raw value is out of range [-10; 10]");
+S(CompressorTaskError, 3, "Compressor task won't start");
+S(LoopTimingError, 4, "Digital module loop timing is not the expected value");
+S(NonBinaryDigitalValue, 5, "Digital output value is not 0 or 1");
+S(IncorrectBatteryChannel, 6, "Battery measurement channel is not correct value");
+S(BadJoystickIndex, 7, "Joystick index is out of range, should be 0-3");
+S(BadJoystickAxis, 8, "Joystick axis or POV is out of range");
+S(InvalidMotorIndex, 9, "Motor index is out of range, should be 0-3");
+S(DriverStationTaskError, 10, "Driver Station task won't start");
+S(EnhancedIOPWMPeriodOutOfRange, 11, "Driver Station Enhanced IO PWM Output period out of range.");
+S(SPIWriteNoMOSI, 12, "Cannot write to SPI port with no MOSI output");
+S(SPIReadNoMISO, 13, "Cannot read from SPI port with no MISO input");
+S(SPIReadNoData, 14, "No data available to read from SPI");
+S(IncompatibleState, 15, "Incompatible State: The operation cannot be completed");
+
+#undef S
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++/include/interfaces/Accelerometer.h b/aos/externals/allwpilib/wpilibc/wpilibC++/include/interfaces/Accelerometer.h
new file mode 100644
index 0000000..609e9bc
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++/include/interfaces/Accelerometer.h
@@ -0,0 +1,52 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2014. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+#pragma once
+
+/**
+ * Interface for 3-axis accelerometers
+ */
+class Accelerometer
+{
+public:
+ virtual ~Accelerometer() {};
+
+ enum Range
+ {
+ kRange_2G = 0,
+ kRange_4G = 1,
+ kRange_8G = 2,
+ kRange_16G = 3
+ };
+
+ /**
+ * Common interface for setting the measuring range of an accelerometer.
+ *
+ * @param range The maximum acceleration, positive or negative, that the
+ * accelerometer will measure. Not all accelerometers support all ranges.
+ */
+ virtual void SetRange(Range range) = 0;
+
+ /**
+ * Common interface for getting the x axis acceleration
+ *
+ * @return The acceleration along the x axis in g-forces
+ */
+ virtual double GetX() = 0;
+
+ /**
+ * Common interface for getting the y axis acceleration
+ *
+ * @return The acceleration along the y axis in g-forces
+ */
+ virtual double GetY() = 0;
+
+ /**
+ * Common interface for getting the z axis acceleration
+ *
+ * @return The acceleration along the z axis in g-forces
+ */
+ virtual double GetZ() = 0;
+};
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++/include/interfaces/Potentiometer.h b/aos/externals/allwpilib/wpilibc/wpilibC++/include/interfaces/Potentiometer.h
new file mode 100644
index 0000000..f5f4567
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++/include/interfaces/Potentiometer.h
@@ -0,0 +1,27 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+
+#ifndef INTERFACES_POTENTIOMETER_H
+#define INTERFACES_POTENTIOMETER_H
+
+#include "PIDSource.h"
+
+/**
+ * Interface for potentiometers.
+ */
+class Potentiometer : public PIDSource
+{
+public:
+ virtual ~Potentiometer() {};
+ /**
+ * Common interface for getting the current value of a potentiometer.
+ *
+ * @return The current set speed. Value is between -1.0 and 1.0.
+ */
+ virtual double Get() = 0;
+};
+
+#endif
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++/src/Error.cpp b/aos/externals/allwpilib/wpilibc/wpilibC++/src/Error.cpp
new file mode 100644
index 0000000..f64c4fe
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++/src/Error.cpp
@@ -0,0 +1,111 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+
+#include "Error.h"
+
+#include <iostream>
+#include <sstream>
+#include <cstring>
+#include <cstdlib>
+#include <stdint.h>
+#include "HAL/Task.hpp"
+
+#include "DriverStation.h"
+#include "Timer.h"
+#include "Utility.h"
+bool Error::m_suspendOnErrorEnabled = false;
+
+Error::Error()
+ : m_code(0)
+ , m_lineNumber(0)
+ , m_originatingObject(NULL)
+ , m_timestamp (0.0)
+{}
+
+Error::~Error()
+{}
+
+void Error::Clone(Error &error)
+{
+ m_code = error.m_code;
+ m_message = error.m_message;
+ m_filename = error.m_filename;
+ m_function = error.m_function;
+ m_lineNumber = error.m_lineNumber;
+ m_originatingObject = error.m_originatingObject;
+ m_timestamp = error.m_timestamp;
+}
+
+Error::Code Error::GetCode() const
+{ return m_code; }
+
+const char * Error::GetMessage() const
+{ return m_message.c_str(); }
+
+const char * Error::GetFilename() const
+{ return m_filename.c_str(); }
+
+const char * Error::GetFunction() const
+{ return m_function.c_str(); }
+
+uint32_t Error::GetLineNumber() const
+{ return m_lineNumber; }
+
+const ErrorBase* Error::GetOriginatingObject() const
+{ return m_originatingObject; }
+
+double Error::GetTimestamp() const
+{ return m_timestamp; }
+
+void Error::Set(Code code, const char* contextMessage, const char* filename, const char* function, uint32_t lineNumber, const ErrorBase* originatingObject)
+{
+ bool report = true;
+
+ if(code == m_code && GetTime() - m_timestamp < 1)
+ {
+ report = false;
+ }
+
+ m_code = code;
+ m_message = contextMessage;
+ m_filename = filename;
+ m_function = function;
+ m_lineNumber = lineNumber;
+ m_originatingObject = originatingObject;
+
+ if(report)
+ {
+ m_timestamp = GetTime();
+ Report();
+ }
+
+ if (m_suspendOnErrorEnabled) suspendTask(0);
+}
+
+void Error::Report()
+{
+ std::stringstream errorStream;
+
+ errorStream << "Error on line " << m_lineNumber << " ";
+ errorStream << "of "<< basename(m_filename.c_str()) << ": ";
+ errorStream << m_message << std::endl;
+ errorStream << GetStackTrace(4);
+
+ std::string error = errorStream.str();
+
+ DriverStation::ReportError(error);
+}
+
+void Error::Clear()
+{
+ m_code = 0;
+ m_message = "";
+ m_filename = "";
+ m_function = "";
+ m_lineNumber = 0;
+ m_originatingObject = NULL;
+ m_timestamp = 0.0;
+}
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++/src/ErrorBase.cpp b/aos/externals/allwpilib/wpilibc/wpilibC++/src/ErrorBase.cpp
new file mode 100644
index 0000000..e4dda4b
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++/src/ErrorBase.cpp
@@ -0,0 +1,205 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+
+#include "ErrorBase.h"
+#include "HAL/cpp/Synchronized.hpp"
+#define WPI_ERRORS_DEFINE_STRINGS
+#include "WPIErrors.h"
+
+#include <errno.h>
+#include <cstring>
+#include <cstdio>
+
+Mutex ErrorBase::_globalErrorMutex;
+Error ErrorBase::_globalError;
+/**
+ * @brief Initialize the instance status to 0 for now.
+ */
+ErrorBase::ErrorBase()
+{}
+
+ErrorBase::~ErrorBase()
+{}
+
+/**
+ * @brief Retrieve the current error.
+ * Get the current error information associated with this sensor.
+ */
+Error& ErrorBase::GetError()
+{
+ return m_error;
+}
+
+const Error& ErrorBase::GetError() const
+{
+ return m_error;
+}
+
+/**
+ * @brief Clear the current error information associated with this sensor.
+ */
+void ErrorBase::ClearError() const
+{
+ m_error.Clear();
+}
+
+/**
+ * @brief Set error information associated with a C library call that set an error to the "errno" global variable.
+ *
+ * @param contextMessage A custom message from the code that set the error.
+ * @param filename Filename of the error source
+ * @param function Function of the error source
+ * @param lineNumber Line number of the error source
+ */
+void ErrorBase::SetErrnoError(const char *contextMessage,
+ const char* filename, const char* function, uint32_t lineNumber) const
+{
+ char err[256];
+ int errNo = errno;
+ if (errNo == 0)
+ {
+ sprintf(err, "OK: %s", contextMessage);
+ }
+ else
+ {
+ snprintf(err, 256, "%s (0x%08X): %s", strerror(errNo), errNo, contextMessage);
+ }
+
+ // Set the current error information for this object.
+ m_error.Set(-1, err, filename, function, lineNumber, this);
+
+ // Update the global error if there is not one already set.
+ ::std::unique_lock<Mutex> mutex(_globalErrorMutex);
+ if (_globalError.GetCode() == 0) {
+ _globalError.Clone(m_error);
+ }
+}
+
+/**
+ * @brief Set the current error information associated from the nivision Imaq API.
+ *
+ * @param success The return from the function
+ * @param contextMessage A custom message from the code that set the error.
+ * @param filename Filename of the error source
+ * @param function Function of the error source
+ * @param lineNumber Line number of the error source
+ */
+void ErrorBase::SetImaqError(int success, const char *contextMessage, const char* filename, const char* function, uint32_t lineNumber) const
+{
+ // If there was an error
+ if (success <= 0) {
+ char err[256];
+ sprintf(err, "%i: %s", success, contextMessage);
+
+ // Set the current error information for this object.
+ m_error.Set(success, err, filename, function, lineNumber, this);
+
+ // Update the global error if there is not one already set.
+ ::std::unique_lock<Mutex> mutex(_globalErrorMutex);
+ if (_globalError.GetCode() == 0) {
+ _globalError.Clone(m_error);
+ }
+ }
+}
+
+/**
+ * @brief Set the current error information associated with this sensor.
+ *
+ * @param code The error code
+ * @param contextMessage A custom message from the code that set the error.
+ * @param filename Filename of the error source
+ * @param function Function of the error source
+ * @param lineNumber Line number of the error source
+ */
+void ErrorBase::SetError(Error::Code code, const char *contextMessage,
+ const char* filename, const char* function, uint32_t lineNumber) const
+{
+ // If there was an error
+ if (code != 0) {
+ // Set the current error information for this object.
+ m_error.Set(code, contextMessage, filename, function, lineNumber, this);
+
+ // Update the global error if there is not one already set.
+ ::std::unique_lock<Mutex> mutex(_globalErrorMutex);
+ if (_globalError.GetCode() == 0) {
+ _globalError.Clone(m_error);
+ }
+ }
+}
+
+/**
+ * @brief Set the current error information associated with this sensor.
+ *
+ * @param errorMessage The error message from WPIErrors.h
+ * @param contextMessage A custom message from the code that set the error.
+ * @param filename Filename of the error source
+ * @param function Function of the error source
+ * @param lineNumber Line number of the error source
+ */
+void ErrorBase::SetWPIError(const char *errorMessage, Error::Code code , const char *contextMessage,
+ const char* filename, const char* function, uint32_t lineNumber) const
+{
+ char err[256];
+ sprintf(err, "%s: %s", errorMessage, contextMessage);
+
+ // Set the current error information for this object.
+ m_error.Set(code, err, filename, function, lineNumber, this);
+
+ // Update the global error if there is not one already set.
+ ::std::unique_lock<Mutex> mutex(_globalErrorMutex);
+ if (_globalError.GetCode() == 0) {
+ _globalError.Clone(m_error);
+ }
+}
+
+void ErrorBase::CloneError(ErrorBase *rhs) const
+{
+ m_error.Clone(rhs->GetError());
+}
+
+/**
+@brief Check if the current error code represents a fatal error.
+
+@return true if the current error is fatal.
+*/
+bool ErrorBase::StatusIsFatal() const
+{
+ return m_error.GetCode() < 0;
+}
+
+void ErrorBase::SetGlobalError(Error::Code code, const char *contextMessage,
+ const char* filename, const char* function, uint32_t lineNumber)
+{
+ // If there was an error
+ if (code != 0) {
+ ::std::unique_lock<Mutex> mutex(_globalErrorMutex);
+
+ // Set the current error information for this object.
+ _globalError.Set(code, contextMessage, filename, function, lineNumber, NULL);
+ }
+}
+
+void ErrorBase::SetGlobalWPIError(const char *errorMessage, const char *contextMessage,
+ const char* filename, const char* function, uint32_t lineNumber)
+{
+ char err[256];
+ sprintf(err, "%s: %s", errorMessage, contextMessage);
+
+ ::std::unique_lock<Mutex> mutex(_globalErrorMutex);
+ if (_globalError.GetCode() != 0) {
+ _globalError.Clear();
+ }
+ _globalError.Set(-1, err, filename, function, lineNumber, NULL);
+}
+
+/**
+ * Retrieve the current global error.
+*/
+Error& ErrorBase::GetGlobalError()
+{
+ ::std::unique_lock<Mutex> mutex(_globalErrorMutex);
+ return _globalError;
+}
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++/src/HLUsageReporting.cpp b/aos/externals/allwpilib/wpilibc/wpilibC++/src/HLUsageReporting.cpp
new file mode 100644
index 0000000..c003298
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++/src/HLUsageReporting.cpp
@@ -0,0 +1,20 @@
+
+#include "HLUsageReporting.h"
+
+HLUsageReportingInterface* HLUsageReporting::impl = 0;
+
+void HLUsageReporting::SetImplementation(HLUsageReportingInterface* i) {
+ impl = i;
+}
+
+void HLUsageReporting::ReportScheduler() {
+ if (impl != 0) {
+ impl->ReportScheduler();
+ }
+}
+
+void HLUsageReporting::ReportSmartDashboard() {
+ if (impl != 0) {
+ impl->ReportSmartDashboard();
+ }
+}
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++/src/Resource.cpp b/aos/externals/allwpilib/wpilibc/wpilibC++/src/Resource.cpp
new file mode 100644
index 0000000..f582155
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++/src/Resource.cpp
@@ -0,0 +1,120 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+
+#include "Resource.h"
+#include "WPIErrors.h"
+#include "ErrorBase.h"
+
+ReentrantMutex Resource::m_createLock;
+
+/**
+ * Allocate storage for a new instance of Resource.
+ * Allocate a bool array of values that will get initialized to indicate that no resources
+ * have been allocated yet. The indicies of the resources are [0 .. elements - 1].
+ */
+Resource::Resource(uint32_t elements)
+{
+ ::std::unique_lock<ReentrantMutex> sync(m_createLock);
+ m_size = elements;
+ m_isAllocated = new bool[m_size];
+ for (uint32_t i=0; i < m_size; i++)
+ {
+ m_isAllocated[i] = false;
+ }
+}
+
+/**
+ * Factory method to create a Resource allocation-tracker *if* needed.
+ *
+ * @param r -- address of the caller's Resource pointer. If *r == NULL, this
+ * will construct a Resource and make *r point to it. If *r != NULL, i.e.
+ * the caller already has a Resource instance, this won't do anything.
+ * @param elements -- the number of elements for this Resource allocator to
+ * track, that is, it will allocate resource numbers in the range
+ * [0 .. elements - 1].
+ */
+/*static*/ void Resource::CreateResourceObject(Resource **r, uint32_t elements)
+{
+ ::std::unique_lock<ReentrantMutex> sync(m_createLock);
+ if (*r == NULL)
+ {
+ *r = new Resource(elements);
+ }
+}
+
+/**
+ * Delete the allocated array or resources.
+ * This happens when the module is unloaded (provided it was statically allocated).
+ */
+Resource::~Resource()
+{
+ delete[] m_isAllocated;
+}
+
+/**
+ * Allocate a resource.
+ * When a resource is requested, mark it allocated. In this case, a free resource value
+ * within the range is located and returned after it is marked allocated.
+ */
+uint32_t Resource::Allocate(const char *resourceDesc)
+{
+ ::std::unique_lock<ReentrantMutex> sync(m_allocateLock);
+ for (uint32_t i=0; i < m_size; i++)
+ {
+ if (!m_isAllocated[i])
+ {
+ m_isAllocated[i] = true;
+ return i;
+ }
+ }
+ wpi_setWPIErrorWithContext(NoAvailableResources, resourceDesc);
+ return ~0ul;
+}
+
+/**
+ * Allocate a specific resource value.
+ * The user requests a specific resource value, i.e. channel number and it is verified
+ * unallocated, then returned.
+ */
+uint32_t Resource::Allocate(uint32_t index, const char *resourceDesc)
+{
+ ::std::unique_lock<ReentrantMutex> sync(m_allocateLock);
+ if (index >= m_size)
+ {
+ wpi_setWPIErrorWithContext(ChannelIndexOutOfRange, resourceDesc);
+ return ~0ul;
+ }
+ if ( m_isAllocated[index] )
+ {
+ wpi_setWPIErrorWithContext(ResourceAlreadyAllocated, resourceDesc);
+ return ~0ul;
+ }
+ m_isAllocated[index] = true;
+ return index;
+}
+
+
+/**
+ * Free an allocated resource.
+ * After a resource is no longer needed, for example a destructor is called for a channel assignment
+ * class, Free will release the resource value so it can be reused somewhere else in the program.
+ */
+void Resource::Free(uint32_t index)
+{
+ ::std::unique_lock<ReentrantMutex> sync(m_allocateLock);
+ if (index == ~0ul) return;
+ if (index >= m_size)
+ {
+ wpi_setWPIError(NotAllocated);
+ return;
+ }
+ if (!m_isAllocated[index])
+ {
+ wpi_setWPIError(NotAllocated);
+ return;
+ }
+ m_isAllocated[index] = false;
+}
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++/src/RobotState.cpp b/aos/externals/allwpilib/wpilibc/wpilibC++/src/RobotState.cpp
new file mode 100644
index 0000000..747000a
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++/src/RobotState.cpp
@@ -0,0 +1,42 @@
+#include "RobotState.h"
+
+RobotStateInterface* RobotState::impl = 0;
+
+void RobotState::SetImplementation(RobotStateInterface* i) {
+ impl = i;
+}
+
+bool RobotState::IsDisabled() {
+ if (impl != nullptr) {
+ return impl->IsDisabled();
+ }
+ return true;
+}
+
+bool RobotState::IsEnabled() {
+ if (impl != nullptr) {
+ return impl->IsEnabled();
+ }
+ return false;
+}
+
+bool RobotState::IsOperatorControl() {
+ if (impl != nullptr) {
+ return impl->IsOperatorControl();
+ }
+ return true;
+}
+
+bool RobotState::IsAutonomous() {
+ if (impl != nullptr) {
+ return impl->IsAutonomous();
+ }
+ return false;
+}
+
+bool RobotState::IsTest() {
+ if (impl != nullptr) {
+ return impl->IsTest();
+ }
+ return false;
+}
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/CMakeLists.txt b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/CMakeLists.txt
new file mode 100644
index 0000000..f73522c
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/CMakeLists.txt
@@ -0,0 +1,13 @@
+cmake_minimum_required(VERSION 2.8)
+project(WPILibC++Devices)
+
+file(GLOB_RECURSE SRC_FILES src/*.cpp)
+include_directories(include/ ${WPILIB_INCLUDES} ${HAL_API_INCLUDES} ${NWT_API_INCLUDES})
+add_library(wpilib_nonshared STATIC ${SRC_FILES} ${COM_SRC_FILES})
+target_link_libraries(wpilib_nonshared HALAthena NetworkTables ${NI_LIBS})
+INSTALL(TARGETS wpilib_nonshared ARCHIVE DESTINATION lib COMPONENT lib)
+INSTALL(DIRECTORY include DESTINATION ${CMAKE_INSTALL_PREFIX} COMPONENT headers)
+# lib/ c m gcc_s ld-linux
+# usr/lib stdc++
+# ni_emb
+# HAL NWT
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/ADXL345_I2C.h b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/ADXL345_I2C.h
new file mode 100644
index 0000000..5cff335
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/ADXL345_I2C.h
@@ -0,0 +1,53 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+#pragma once
+
+#include "interfaces/Accelerometer.h"
+#include "I2C.h"
+
+/**
+ * ADXL345 Accelerometer on I2C.
+ *
+ * This class allows access to a Analog Devices ADXL345 3-axis accelerometer on an I2C bus.
+ * This class assumes the default (not alternate) sensor address of 0x1D (7-bit address).
+ */
+class ADXL345_I2C : public Accelerometer, public I2C
+{
+protected:
+ static const uint8_t kAddress = 0x1D;
+ static const uint8_t kPowerCtlRegister = 0x2D;
+ static const uint8_t kDataFormatRegister = 0x31;
+ static const uint8_t kDataRegister = 0x32;
+ static constexpr double kGsPerLSB = 0.00390625;
+ enum PowerCtlFields {kPowerCtl_Link=0x20, kPowerCtl_AutoSleep=0x10, kPowerCtl_Measure=0x08, kPowerCtl_Sleep=0x04};
+ enum DataFormatFields {kDataFormat_SelfTest=0x80, kDataFormat_SPI=0x40, kDataFormat_IntInvert=0x20,
+ kDataFormat_FullRes=0x08, kDataFormat_Justify=0x04};
+
+public:
+ enum Axes {kAxis_X=0x00, kAxis_Y=0x02, kAxis_Z=0x04};
+ struct AllAxes
+ {
+ double XAxis;
+ double YAxis;
+ double ZAxis;
+ };
+
+public:
+ explicit ADXL345_I2C(Port port, Range range = kRange_2G);
+ virtual ~ADXL345_I2C();
+
+ // Accelerometer interface
+ virtual void SetRange(Range range);
+ virtual double GetX();
+ virtual double GetY();
+ virtual double GetZ();
+
+ virtual double GetAcceleration(Axes axis);
+ virtual AllAxes GetAccelerations();
+
+protected:
+ //I2C* m_i2c;
+};
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/ADXL345_SPI.h b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/ADXL345_SPI.h
new file mode 100644
index 0000000..4cfc64e
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/ADXL345_SPI.h
@@ -0,0 +1,60 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+#pragma once
+
+#include "interfaces/Accelerometer.h"
+#include "SensorBase.h"
+#include "SPI.h"
+
+class DigitalInput;
+class DigitalOutput;
+
+/**
+ * ADXL345 Accelerometer on SPI.
+ *
+ * This class allows access to an Analog Devices ADXL345 3-axis accelerometer via SPI.
+ * This class assumes the sensor is wired in 4-wire SPI mode.
+ */
+class ADXL345_SPI : public Accelerometer, public SensorBase
+{
+protected:
+ static const uint8_t kPowerCtlRegister = 0x2D;
+ static const uint8_t kDataFormatRegister = 0x31;
+ static const uint8_t kDataRegister = 0x32;
+ static constexpr double kGsPerLSB = 0.00390625;
+ enum SPIAddressFields {kAddress_Read=0x80, kAddress_MultiByte=0x40};
+ enum PowerCtlFields {kPowerCtl_Link=0x20, kPowerCtl_AutoSleep=0x10, kPowerCtl_Measure=0x08, kPowerCtl_Sleep=0x04};
+ enum DataFormatFields {kDataFormat_SelfTest=0x80, kDataFormat_SPI=0x40, kDataFormat_IntInvert=0x20,
+ kDataFormat_FullRes=0x08, kDataFormat_Justify=0x04};
+
+public:
+ enum Axes {kAxis_X=0x00, kAxis_Y=0x02, kAxis_Z=0x04};
+ struct AllAxes
+ {
+ double XAxis;
+ double YAxis;
+ double ZAxis;
+ };
+
+public:
+ ADXL345_SPI(SPI::Port port, Range range=kRange_2G);
+ virtual ~ADXL345_SPI();
+
+ // Accelerometer interface
+ virtual void SetRange(Range range);
+ virtual double GetX();
+ virtual double GetY();
+ virtual double GetZ();
+
+ virtual double GetAcceleration(Axes axis);
+ virtual AllAxes GetAccelerations();
+
+protected:
+ void Init(Range range);
+
+ SPI* m_spi;
+ SPI::Port m_port;
+};
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/AnalogAccelerometer.h b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/AnalogAccelerometer.h
new file mode 100644
index 0000000..8d7a3f0
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/AnalogAccelerometer.h
@@ -0,0 +1,36 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+#pragma once
+
+#include "AnalogInput.h"
+#include "SensorBase.h"
+#include "PIDSource.h"
+
+/**
+ * Handle operation of an analog accelerometer.
+ * The accelerometer reads acceleration directly through the sensor. Many sensors have
+ * multiple axis and can be treated as multiple devices. Each is calibrated by finding
+ * the center value over a period of time.
+ */
+class AnalogAccelerometer : public SensorBase, public PIDSource {
+public:
+ explicit AnalogAccelerometer(int32_t channel);
+ explicit AnalogAccelerometer(AnalogInput *channel);
+ virtual ~AnalogAccelerometer();
+
+ float GetAcceleration();
+ void SetSensitivity(float sensitivity);
+ void SetZero(float zero);
+ double PIDGet();
+
+private:
+ void InitAccelerometer();
+
+ AnalogInput *m_AnalogInput;
+ float m_voltsPerG;
+ float m_zeroGVoltage;
+ bool m_allocatedChannel;
+};
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/AnalogInput.h b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/AnalogInput.h
new file mode 100644
index 0000000..8e10808
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/AnalogInput.h
@@ -0,0 +1,68 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+#pragma once
+
+#include "HAL/HAL.hpp"
+#include "SensorBase.h"
+#include "PIDSource.h"
+
+/**
+ * Analog input class.
+ *
+ * Connected to each analog channel is an averaging and oversampling engine. This engine accumulates
+ * the specified ( by SetAverageBits() and SetOversampleBits() ) number of samples before returning a new
+ * value. This is not a sliding window average. The only difference between the oversampled samples and
+ * the averaged samples is that the oversampled samples are simply accumulated effectively increasing the
+ * resolution, while the averaged samples are divided by the number of samples to retain the resolution,
+ * but get more stable values.
+ */
+class AnalogInput : public SensorBase, public PIDSource
+{
+public:
+ static const uint8_t kAccumulatorModuleNumber = 1;
+ static const uint32_t kAccumulatorNumChannels = 2;
+ static const uint32_t kAccumulatorChannels[kAccumulatorNumChannels];
+
+ explicit AnalogInput(uint32_t channel);
+ virtual ~AnalogInput();
+
+ int16_t GetValue();
+ int32_t GetAverageValue();
+
+ float GetVoltage();
+ float GetAverageVoltage();
+
+ uint32_t GetChannel();
+
+ void SetAverageBits(uint32_t bits);
+ uint32_t GetAverageBits();
+ void SetOversampleBits(uint32_t bits);
+ uint32_t GetOversampleBits();
+
+ uint32_t GetLSBWeight();
+ int32_t GetOffset();
+
+ bool IsAccumulatorChannel();
+ void InitAccumulator();
+ void SetAccumulatorInitialValue(int64_t value);
+ void ResetAccumulator();
+ void SetAccumulatorCenter(int32_t center);
+ void SetAccumulatorDeadband(int32_t deadband);
+ int64_t GetAccumulatorValue();
+ uint32_t GetAccumulatorCount();
+ void GetAccumulatorOutput(int64_t *value, uint32_t *count);
+
+ static void SetSampleRate(float samplesPerSecond);
+ static float GetSampleRate();
+
+ double PIDGet();
+
+private:
+ void InitAnalogInput(uint32_t channel);
+ uint32_t m_channel;
+ void* m_port;
+ int64_t m_accumulatorOffset;
+};
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/AnalogOutput.h b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/AnalogOutput.h
new file mode 100644
index 0000000..14abcb2
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/AnalogOutput.h
@@ -0,0 +1,29 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2014. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+
+#pragma once
+
+#include "HAL/HAL.hpp"
+#include "SensorBase.h"
+
+/**
+ * MXP analog output class.
+ */
+class AnalogOutput : public SensorBase
+{
+public:
+ explicit AnalogOutput(uint32_t channel);
+ virtual ~AnalogOutput();
+
+ void SetVoltage(float voltage);
+ float GetVoltage();
+
+protected:
+ void InitAnalogOutput(uint32_t channel);
+ uint32_t m_channel;
+ void* m_port;
+};
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/AnalogPotentiometer.h b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/AnalogPotentiometer.h
new file mode 100644
index 0000000..8b8cd76
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/AnalogPotentiometer.h
@@ -0,0 +1,64 @@
+
+#include "AnalogInput.h"
+#include "interfaces/Potentiometer.h"
+
+/**
+ * Class for reading analog potentiometers. Analog potentiometers read
+ * in an analog voltage that corresponds to a position. The position is
+ * in whichever units you choose, by way of the scaling and offset
+ * constants passed to the constructor.
+ *
+ * @author Alex Henning
+ * @author Colby Skeggs (rail voltage)
+ */
+class AnalogPotentiometer : public Potentiometer {
+public:
+ /**
+ * AnalogPotentiometer constructor.
+ *
+ * Use the fullRange and offset values so that the output produces
+ * meaningful values. I.E: you have a 270 degree potentiometer and
+ * you want the output to be degrees with the halfway point as 0
+ * degrees. The fullRange value is 270.0(degrees) and the offset is
+ * -135.0 since the halfway point after scaling is 135 degrees.
+ *
+ * This will calculate the result from the fullRange times the
+ * fraction of the supply voltage, plus the offset.
+ *
+ * @param channel The analog channel this potentiometer is plugged into.
+ * @param fullRange The scaling to multiply the voltage by to get a meaningful unit.
+ * @param offset The offset to add to the scaled value for controlling the zero value
+ */
+ explicit AnalogPotentiometer(int channel, double fullRange = 1.0, double offset = 0.0);
+
+ explicit AnalogPotentiometer(AnalogInput *input, double fullRange = 1.0, double offset = 0.0);
+
+ explicit AnalogPotentiometer(AnalogInput &input, double fullRange = 1.0, double offset = 0.0);
+
+ virtual ~AnalogPotentiometer();
+
+ /**
+ * Get the current reading of the potentiomer.
+ *
+ * @return The current position of the potentiometer.
+ */
+ virtual double Get();
+
+
+ /**
+ * Implement the PIDSource interface.
+ *
+ * @return The current reading.
+ */
+ virtual double PIDGet();
+
+private:
+ double m_fullRange, m_offset;
+ AnalogInput* m_analog_input;
+ bool m_init_analog_input;
+
+ /**
+ * Common initialization code called by all constructors.
+ */
+ void initPot(AnalogInput *input, double fullRange, double offset);
+};
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/AnalogTrigger.h b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/AnalogTrigger.h
new file mode 100644
index 0000000..9b18a33
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/AnalogTrigger.h
@@ -0,0 +1,36 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+#pragma once
+
+#include "HAL/HAL.hpp"
+#include "AnalogTriggerOutput.h"
+#include "SensorBase.h"
+
+class AnalogInput;
+
+class AnalogTrigger : public SensorBase
+{
+ friend class AnalogTriggerOutput;
+public:
+ explicit AnalogTrigger(int32_t channel);
+ explicit AnalogTrigger(AnalogInput *channel);
+ virtual ~AnalogTrigger();
+
+ void SetLimitsVoltage(float lower, float upper);
+ void SetLimitsRaw(int32_t lower, int32_t upper);
+ void SetAveraged(bool useAveragedValue);
+ void SetFiltered(bool useFilteredValue);
+ uint32_t GetIndex();
+ bool GetInWindow();
+ bool GetTriggerState();
+ AnalogTriggerOutput *CreateOutput(AnalogTriggerType type);
+
+private:
+ void InitTrigger(uint32_t channel);
+
+ uint8_t m_index;
+ void* m_trigger;
+};
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/AnalogTriggerOutput.h b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/AnalogTriggerOutput.h
new file mode 100644
index 0000000..8ab701e
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/AnalogTriggerOutput.h
@@ -0,0 +1,57 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+#pragma once
+
+#include "DigitalSource.h"
+
+class AnalogTrigger;
+
+/**
+ * Class to represent a specific output from an analog trigger.
+ * This class is used to get the current output value and also as a DigitalSource
+ * to provide routing of an output to digital subsystems on the FPGA such as
+ * Counter, Encoder, and Interrupt.
+ *
+ * The TriggerState output indicates the primary output value of the trigger. If the analog
+ * signal is less than the lower limit, the output is false. If the analog value is greater
+ * than the upper limit, then the output is true. If the analog value is in between, then
+ * the trigger output state maintains its most recent value.
+ *
+ * The InWindow output indicates whether or not the analog signal is inside the range defined
+ * by the limits.
+ *
+ * The RisingPulse and FallingPulse outputs detect an instantaneous transition from above the
+ * upper limit to below the lower limit, and vise versa. These pulses represent a rollover
+ * condition of a sensor and can be routed to an up / down couter or to interrupts. Because
+ * the outputs generate a pulse, they cannot be read directly. To help ensure that a rollover
+ * condition is not missed, there is an average rejection filter available that operates on the
+ * upper 8 bits of a 12 bit number and selects the nearest outlyer of 3 samples. This will reject
+ * a sample that is (due to averaging or sampling) errantly between the two limits. This filter
+ * will fail if more than one sample in a row is errantly in between the two limits. You may see
+ * this problem if attempting to use this feature with a mechanical rollover sensor, such as a
+ * 360 degree no-stop potentiometer without signal conditioning, because the rollover transition
+ * is not sharp / clean enough. Using the averaging engine may help with this, but rotational speeds of
+ * the sensor will then be limited.
+ */
+class AnalogTriggerOutput : public DigitalSource
+{
+ friend class AnalogTrigger;
+public:
+
+ virtual ~AnalogTriggerOutput();
+ virtual bool Get();
+
+ // DigitalSource interface
+ virtual uint32_t GetChannelForRouting();
+ virtual uint32_t GetModuleForRouting();
+ virtual bool GetAnalogTriggerForRouting();
+protected:
+ AnalogTriggerOutput(AnalogTrigger *trigger, AnalogTriggerType outputType);
+
+private:
+ AnalogTrigger *m_trigger;
+ AnalogTriggerType m_outputType;
+};
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/BuiltInAccelerometer.h b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/BuiltInAccelerometer.h
new file mode 100644
index 0000000..563df99
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/BuiltInAccelerometer.h
@@ -0,0 +1,27 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2014. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+#pragma once
+
+#include "interfaces/Accelerometer.h"
+#include "SensorBase.h"
+
+/**
+ * Built-in accelerometer.
+ *
+ * This class allows access to the RoboRIO's internal accelerometer.
+ */
+class BuiltInAccelerometer : public Accelerometer, public SensorBase
+{
+public:
+ BuiltInAccelerometer(Range range = kRange_8G);
+ virtual ~BuiltInAccelerometer();
+
+ // Accelerometer interface
+ virtual void SetRange(Range range);
+ virtual double GetX();
+ virtual double GetY();
+ virtual double GetZ();
+};
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/CAN/can_proto.h b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/CAN/can_proto.h
new file mode 100644
index 0000000..5113b02
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/CAN/can_proto.h
@@ -0,0 +1,428 @@
+//*****************************************************************************
+//
+// can_proto.h - Definitions for the CAN protocol used to communicate with the
+// BDC motor controller.
+//
+// Copyright (c) 2008 Texas Instruments Incorporated. All rights reserved.
+// TI Information - Selective Disclosure
+//
+//*****************************************************************************
+
+#ifndef __CAN_PROTO_H__
+#define __CAN_PROTO_H__
+
+//*****************************************************************************
+//
+// The masks of the fields that are used in the message identifier.
+//
+//*****************************************************************************
+#define CAN_MSGID_FULL_M 0x1fffffff
+#define CAN_MSGID_DEVNO_M 0x0000003f
+#define CAN_MSGID_API_M 0x0000ffc0
+#define CAN_MSGID_MFR_M 0x00ff0000
+#define CAN_MSGID_DTYPE_M 0x1f000000
+#define CAN_MSGID_DEVNO_S 0
+#define CAN_MSGID_API_S 6
+#define CAN_MSGID_MFR_S 16
+#define CAN_MSGID_DTYPE_S 24
+
+//*****************************************************************************
+//
+// The Reserved device number values in the Message Id.
+//
+//*****************************************************************************
+#define CAN_MSGID_DEVNO_BCAST 0x00000000
+
+//*****************************************************************************
+//
+// The Reserved system control API numbers in the Message Id.
+//
+//*****************************************************************************
+#define CAN_MSGID_API_SYSHALT 0x00000000
+#define CAN_MSGID_API_SYSRST 0x00000040
+#define CAN_MSGID_API_DEVASSIGN 0x00000080
+#define CAN_MSGID_API_DEVQUERY 0x000000c0
+#define CAN_MSGID_API_HEARTBEAT 0x00000140
+#define CAN_MSGID_API_SYNC 0x00000180
+#define CAN_MSGID_API_UPDATE 0x000001c0
+#define CAN_MSGID_API_FIRMVER 0x00000200
+#define CAN_MSGID_API_ENUMERATE 0x00000240
+#define CAN_MSGID_API_SYSRESUME 0x00000280
+
+//*****************************************************************************
+//
+// The 32 bit values associated with the CAN_MSGID_API_STATUS request.
+//
+//*****************************************************************************
+#define CAN_STATUS_CODE_M 0x0000ffff
+#define CAN_STATUS_MFG_M 0x00ff0000
+#define CAN_STATUS_DTYPE_M 0x1f000000
+#define CAN_STATUS_CODE_S 0
+#define CAN_STATUS_MFG_S 16
+#define CAN_STATUS_DTYPE_S 24
+
+//*****************************************************************************
+//
+// The Reserved manufacturer identifiers in the Message Id.
+//
+//*****************************************************************************
+#define CAN_MSGID_MFR_NI 0x00010000
+#define CAN_MSGID_MFR_LM 0x00020000
+#define CAN_MSGID_MFR_DEKA 0x00030000
+
+//*****************************************************************************
+//
+// The Reserved device type identifiers in the Message Id.
+//
+//*****************************************************************************
+#define CAN_MSGID_DTYPE_BCAST 0x00000000
+#define CAN_MSGID_DTYPE_ROBOT 0x01000000
+#define CAN_MSGID_DTYPE_MOTOR 0x02000000
+#define CAN_MSGID_DTYPE_RELAY 0x03000000
+#define CAN_MSGID_DTYPE_GYRO 0x04000000
+#define CAN_MSGID_DTYPE_ACCEL 0x05000000
+#define CAN_MSGID_DTYPE_USONIC 0x06000000
+#define CAN_MSGID_DTYPE_GEART 0x07000000
+#define CAN_MSGID_DTYPE_UPDATE 0x1f000000
+
+//*****************************************************************************
+//
+// LM Motor Control API Classes API Class and ID masks.
+//
+//*****************************************************************************
+#define CAN_MSGID_API_CLASS_M 0x0000fc00
+#define CAN_MSGID_API_ID_M 0x000003c0
+
+//*****************************************************************************
+//
+// LM Motor Control API Classes in the Message Id for non-broadcast.
+// These are the upper 6 bits of the API field, the lower 4 bits determine
+// the APIId.
+//
+//*****************************************************************************
+#define CAN_API_MC_VOLTAGE 0x00000000
+#define CAN_API_MC_SPD 0x00000400
+#define CAN_API_MC_VCOMP 0x00000800
+#define CAN_API_MC_POS 0x00000c00
+#define CAN_API_MC_ICTRL 0x00001000
+#define CAN_API_MC_STATUS 0x00001400
+#define CAN_API_MC_PSTAT 0x00001800
+#define CAN_API_MC_CFG 0x00001c00
+#define CAN_API_MC_ACK 0x00002000
+
+//*****************************************************************************
+//
+// The Stellaris Motor Class Control Voltage API definitions.
+//
+//*****************************************************************************
+#define LM_API_VOLT (CAN_MSGID_MFR_LM | CAN_MSGID_DTYPE_MOTOR | \
+ CAN_API_MC_VOLTAGE)
+#define LM_API_VOLT_EN (LM_API_VOLT | (0 << CAN_MSGID_API_S))
+#define LM_API_VOLT_DIS (LM_API_VOLT | (1 << CAN_MSGID_API_S))
+#define LM_API_VOLT_SET (LM_API_VOLT | (2 << CAN_MSGID_API_S))
+#define LM_API_VOLT_SET_RAMP (LM_API_VOLT | (3 << CAN_MSGID_API_S))
+//##### FIRST BEGIN #####
+#define LM_API_VOLT_T_EN (LM_API_VOLT | (4 << CAN_MSGID_API_S))
+#define LM_API_VOLT_T_SET (LM_API_VOLT | (5 << CAN_MSGID_API_S))
+#define LM_API_VOLT_T_SET_NO_ACK \
+ (LM_API_VOLT | (7 << CAN_MSGID_API_S))
+//##### FIRST END #####
+#define LM_API_VOLT_SET_NO_ACK (LM_API_VOLT | (8 << CAN_MSGID_API_S))
+
+//*****************************************************************************
+//
+// The Stellaris Motor Class Control API definitions for LM_API_VOLT_SET_RAMP.
+//
+//*****************************************************************************
+#define LM_API_VOLT_RAMP_DIS 0
+
+//*****************************************************************************
+//
+// The Stellaris Motor Class Control API definitions for CAN_MSGID_API_SYNC.
+//
+//*****************************************************************************
+#define LM_API_SYNC_PEND_NOW 0
+
+//*****************************************************************************
+//
+// The Stellaris Motor Class Speed Control API definitions.
+//
+//*****************************************************************************
+#define LM_API_SPD (CAN_MSGID_MFR_LM | CAN_MSGID_DTYPE_MOTOR | \
+ CAN_API_MC_SPD)
+#define LM_API_SPD_EN (LM_API_SPD | (0 << CAN_MSGID_API_S))
+#define LM_API_SPD_DIS (LM_API_SPD | (1 << CAN_MSGID_API_S))
+#define LM_API_SPD_SET (LM_API_SPD | (2 << CAN_MSGID_API_S))
+#define LM_API_SPD_PC (LM_API_SPD | (3 << CAN_MSGID_API_S))
+#define LM_API_SPD_IC (LM_API_SPD | (4 << CAN_MSGID_API_S))
+#define LM_API_SPD_DC (LM_API_SPD | (5 << CAN_MSGID_API_S))
+#define LM_API_SPD_REF (LM_API_SPD | (6 << CAN_MSGID_API_S))
+//##### FIRST BEGIN #####
+#define LM_API_SPD_T_EN (LM_API_SPD | (7 << CAN_MSGID_API_S))
+#define LM_API_SPD_T_SET (LM_API_SPD | (8 << CAN_MSGID_API_S))
+#define LM_API_SPD_T_SET_NO_ACK (LM_API_SPD | (10 << CAN_MSGID_API_S))
+//##### FIRST END #####
+#define LM_API_SPD_SET_NO_ACK (LM_API_SPD | (11 << CAN_MSGID_API_S))
+
+//*****************************************************************************
+//
+// The Stellaris Motor Control Voltage Compensation Control API definitions.
+//
+//*****************************************************************************
+#define LM_API_VCOMP (CAN_MSGID_MFR_LM | CAN_MSGID_DTYPE_MOTOR | \
+ CAN_API_MC_VCOMP)
+#define LM_API_VCOMP_EN (LM_API_VCOMP | (0 << CAN_MSGID_API_S))
+#define LM_API_VCOMP_DIS (LM_API_VCOMP | (1 << CAN_MSGID_API_S))
+#define LM_API_VCOMP_SET (LM_API_VCOMP | (2 << CAN_MSGID_API_S))
+#define LM_API_VCOMP_IN_RAMP (LM_API_VCOMP | (3 << CAN_MSGID_API_S))
+#define LM_API_VCOMP_COMP_RAMP (LM_API_VCOMP | (4 << CAN_MSGID_API_S))
+//##### FIRST BEGIN #####
+#define LM_API_VCOMP_T_EN (LM_API_VCOMP | (5 << CAN_MSGID_API_S))
+#define LM_API_VCOMP_T_SET (LM_API_VCOMP | (6 << CAN_MSGID_API_S))
+#define LM_API_VCOMP_T_SET_NO_ACK \
+ (LM_API_VCOMP | (8 << CAN_MSGID_API_S))
+//##### FIRST END #####
+#define LM_API_VCOMP_SET_NO_ACK (LM_API_VCOMP | (9 << CAN_MSGID_API_S))
+
+//*****************************************************************************
+//
+// The Stellaris Motor Class Position Control API definitions.
+//
+//*****************************************************************************
+#define LM_API_POS (CAN_MSGID_MFR_LM | CAN_MSGID_DTYPE_MOTOR | \
+ CAN_API_MC_POS)
+#define LM_API_POS_EN (LM_API_POS | (0 << CAN_MSGID_API_S))
+#define LM_API_POS_DIS (LM_API_POS | (1 << CAN_MSGID_API_S))
+#define LM_API_POS_SET (LM_API_POS | (2 << CAN_MSGID_API_S))
+#define LM_API_POS_PC (LM_API_POS | (3 << CAN_MSGID_API_S))
+#define LM_API_POS_IC (LM_API_POS | (4 << CAN_MSGID_API_S))
+#define LM_API_POS_DC (LM_API_POS | (5 << CAN_MSGID_API_S))
+#define LM_API_POS_REF (LM_API_POS | (6 << CAN_MSGID_API_S))
+//##### FIRST BEGIN #####
+#define LM_API_POS_T_EN (LM_API_POS | (7 << CAN_MSGID_API_S))
+#define LM_API_POS_T_SET (LM_API_POS | (8 << CAN_MSGID_API_S))
+#define LM_API_POS_T_SET_NO_ACK (LM_API_POS | (10 << CAN_MSGID_API_S))
+//##### FIRST END #####
+#define LM_API_POS_SET_NO_ACK (LM_API_POS | (11 << CAN_MSGID_API_S))
+
+//*****************************************************************************
+//
+// The Stellaris Motor Class Current Control API definitions.
+//
+//*****************************************************************************
+#define LM_API_ICTRL (CAN_MSGID_MFR_LM | CAN_MSGID_DTYPE_MOTOR | \
+ CAN_API_MC_ICTRL)
+#define LM_API_ICTRL_EN (LM_API_ICTRL | (0 << CAN_MSGID_API_S))
+#define LM_API_ICTRL_DIS (LM_API_ICTRL | (1 << CAN_MSGID_API_S))
+#define LM_API_ICTRL_SET (LM_API_ICTRL | (2 << CAN_MSGID_API_S))
+#define LM_API_ICTRL_PC (LM_API_ICTRL | (3 << CAN_MSGID_API_S))
+#define LM_API_ICTRL_IC (LM_API_ICTRL | (4 << CAN_MSGID_API_S))
+#define LM_API_ICTRL_DC (LM_API_ICTRL | (5 << CAN_MSGID_API_S))
+//##### FIRST BEGIN #####
+#define LM_API_ICTRL_T_EN (LM_API_ICTRL | (6 << CAN_MSGID_API_S))
+#define LM_API_ICTRL_T_SET (LM_API_ICTRL | (7 << CAN_MSGID_API_S))
+#define LM_API_ICTRL_T_SET_NO_ACK \
+ (LM_API_ICTRL | (9 << CAN_MSGID_API_S))
+//##### FIRST END #####
+#define LM_API_ICTRL_SET_NO_ACK (LM_API_ICTRL | (10 << CAN_MSGID_API_S))
+
+//*****************************************************************************
+//
+// The Stellaris Motor Class Firmware Update API definitions.
+//
+//*****************************************************************************
+#define LM_API_UPD (CAN_MSGID_MFR_LM | CAN_MSGID_DTYPE_UPDATE)
+#define LM_API_UPD_PING (LM_API_UPD | (0 << CAN_MSGID_API_S))
+#define LM_API_UPD_DOWNLOAD (LM_API_UPD | (1 << CAN_MSGID_API_S))
+#define LM_API_UPD_SEND_DATA (LM_API_UPD | (2 << CAN_MSGID_API_S))
+#define LM_API_UPD_RESET (LM_API_UPD | (3 << CAN_MSGID_API_S))
+#define LM_API_UPD_ACK (LM_API_UPD | (4 << CAN_MSGID_API_S))
+#define LM_API_HWVER (LM_API_UPD | (5 << CAN_MSGID_API_S))
+#define LM_API_UPD_REQUEST (LM_API_UPD | (6 << CAN_MSGID_API_S))
+//##### FIRST BEGIN #####
+#define LM_API_UNTRUST_EN (LM_API_UPD | (11 << CAN_MSGID_API_S))
+#define LM_API_TRUST_EN (LM_API_UPD | (12 << CAN_MSGID_API_S))
+#define LM_API_TRUST_HEARTBEAT (LM_API_UPD | (13 << CAN_MSGID_API_S))
+//##### FIRST END #####
+
+//*****************************************************************************
+//
+// The Stellaris Motor Class Status API definitions.
+//
+//*****************************************************************************
+#define LM_API_STATUS (CAN_MSGID_MFR_LM | CAN_MSGID_DTYPE_MOTOR | \
+ CAN_API_MC_STATUS)
+#define LM_API_STATUS_VOLTOUT (LM_API_STATUS | (0 << CAN_MSGID_API_S))
+#define LM_API_STATUS_VOLTBUS (LM_API_STATUS | (1 << CAN_MSGID_API_S))
+#define LM_API_STATUS_CURRENT (LM_API_STATUS | (2 << CAN_MSGID_API_S))
+#define LM_API_STATUS_TEMP (LM_API_STATUS | (3 << CAN_MSGID_API_S))
+#define LM_API_STATUS_POS (LM_API_STATUS | (4 << CAN_MSGID_API_S))
+#define LM_API_STATUS_SPD (LM_API_STATUS | (5 << CAN_MSGID_API_S))
+#define LM_API_STATUS_LIMIT (LM_API_STATUS | (6 << CAN_MSGID_API_S))
+#define LM_API_STATUS_FAULT (LM_API_STATUS | (7 << CAN_MSGID_API_S))
+#define LM_API_STATUS_POWER (LM_API_STATUS | (8 << CAN_MSGID_API_S))
+#define LM_API_STATUS_CMODE (LM_API_STATUS | (9 << CAN_MSGID_API_S))
+#define LM_API_STATUS_VOUT (LM_API_STATUS | (10 << CAN_MSGID_API_S))
+#define LM_API_STATUS_STKY_FLT (LM_API_STATUS | (11 << CAN_MSGID_API_S))
+#define LM_API_STATUS_FLT_COUNT (LM_API_STATUS | (12 << CAN_MSGID_API_S))
+
+//*****************************************************************************
+//
+// These definitions are used with the byte that is returned from
+// the status request for LM_API_STATUS_LIMIT.
+//
+//*****************************************************************************
+#define LM_STATUS_LIMIT_FWD 0x01
+#define LM_STATUS_LIMIT_REV 0x02
+#define LM_STATUS_LIMIT_SFWD 0x04
+#define LM_STATUS_LIMIT_SREV 0x08
+#define LM_STATUS_LIMIT_STKY_FWD \
+ 0x10
+#define LM_STATUS_LIMIT_STKY_REV \
+ 0x20
+#define LM_STATUS_LIMIT_STKY_SFWD \
+ 0x40
+#define LM_STATUS_LIMIT_STKY_SREV \
+ 0x80
+
+//*****************************************************************************
+//
+// LM Motor Control status codes returned due to the CAN_STATUS_CODE_M field.
+//
+//*****************************************************************************
+#define LM_STATUS_FAULT_ILIMIT 0x01
+#define LM_STATUS_FAULT_TLIMIT 0x02
+#define LM_STATUS_FAULT_VLIMIT 0x04
+
+//*****************************************************************************
+//
+// The Stellaris Motor Class Configuration API definitions.
+//
+//*****************************************************************************
+#define LM_API_CFG (CAN_MSGID_MFR_LM | CAN_MSGID_DTYPE_MOTOR | \
+ CAN_API_MC_CFG)
+#define LM_API_CFG_NUM_BRUSHES (LM_API_CFG | (0 << CAN_MSGID_API_S))
+#define LM_API_CFG_ENC_LINES (LM_API_CFG | (1 << CAN_MSGID_API_S))
+#define LM_API_CFG_POT_TURNS (LM_API_CFG | (2 << CAN_MSGID_API_S))
+#define LM_API_CFG_BRAKE_COAST (LM_API_CFG | (3 << CAN_MSGID_API_S))
+#define LM_API_CFG_LIMIT_MODE (LM_API_CFG | (4 << CAN_MSGID_API_S))
+#define LM_API_CFG_LIMIT_FWD (LM_API_CFG | (5 << CAN_MSGID_API_S))
+#define LM_API_CFG_LIMIT_REV (LM_API_CFG | (6 << CAN_MSGID_API_S))
+#define LM_API_CFG_MAX_VOUT (LM_API_CFG | (7 << CAN_MSGID_API_S))
+#define LM_API_CFG_FAULT_TIME (LM_API_CFG | (8 << CAN_MSGID_API_S))
+
+//*****************************************************************************
+//
+// The Stellaris ACK API definition.
+//
+//*****************************************************************************
+#define LM_API_ACK (CAN_MSGID_MFR_LM | CAN_MSGID_DTYPE_MOTOR | \
+ CAN_API_MC_ACK)
+
+//*****************************************************************************
+//
+// The 8 bit values that can be returned by a call to LM_API_STATUS_HWVER.
+//
+//*****************************************************************************
+#define LM_HWVER_UNKNOWN 0x00
+#define LM_HWVER_JAG_1_0 0x01
+#define LM_HWVER_JAG_2_0 0x02
+
+//*****************************************************************************
+//
+// The 8 bit values that can be returned by a call to LM_API_STATUS_CMODE.
+//
+//*****************************************************************************
+#define LM_STATUS_CMODE_VOLT 0x00
+#define LM_STATUS_CMODE_CURRENT 0x01
+#define LM_STATUS_CMODE_SPEED 0x02
+#define LM_STATUS_CMODE_POS 0x03
+#define LM_STATUS_CMODE_VCOMP 0x04
+
+//*****************************************************************************
+//
+// The values that can specified as the position or speed reference. Not all
+// values are valid for each reference; if an invalid reference is set, then
+// none will be selected.
+//
+//*****************************************************************************
+#define LM_REF_ENCODER 0x00
+#define LM_REF_POT 0x01
+#define LM_REF_INV_ENCODER 0x02
+#define LM_REF_QUAD_ENCODER 0x03
+#define LM_REF_NONE 0xff
+
+//*****************************************************************************
+//
+// The flags that are used to indicate the currently active fault sources.
+//
+//*****************************************************************************
+#define LM_FAULT_CURRENT 0x01
+#define LM_FAULT_TEMP 0x02
+#define LM_FAULT_VBUS 0x04
+#define LM_FAULT_GATE_DRIVE 0x08
+#define LM_FAULT_COMM 0x10
+
+//*****************************************************************************
+//
+// The Stellaris Motor Class Periodic Status API definitions.
+//
+//*****************************************************************************
+#define LM_API_PSTAT (CAN_MSGID_MFR_LM | CAN_MSGID_DTYPE_MOTOR | \
+ CAN_API_MC_PSTAT)
+#define LM_API_PSTAT_PER_EN_S0 (LM_API_PSTAT | (0 << CAN_MSGID_API_S))
+#define LM_API_PSTAT_PER_EN_S1 (LM_API_PSTAT | (1 << CAN_MSGID_API_S))
+#define LM_API_PSTAT_PER_EN_S2 (LM_API_PSTAT | (2 << CAN_MSGID_API_S))
+#define LM_API_PSTAT_PER_EN_S3 (LM_API_PSTAT | (3 << CAN_MSGID_API_S))
+#define LM_API_PSTAT_CFG_S0 (LM_API_PSTAT | (4 << CAN_MSGID_API_S))
+#define LM_API_PSTAT_CFG_S1 (LM_API_PSTAT | (5 << CAN_MSGID_API_S))
+#define LM_API_PSTAT_CFG_S2 (LM_API_PSTAT | (6 << CAN_MSGID_API_S))
+#define LM_API_PSTAT_CFG_S3 (LM_API_PSTAT | (7 << CAN_MSGID_API_S))
+#define LM_API_PSTAT_DATA_S0 (LM_API_PSTAT | (8 << CAN_MSGID_API_S))
+#define LM_API_PSTAT_DATA_S1 (LM_API_PSTAT | (9 << CAN_MSGID_API_S))
+#define LM_API_PSTAT_DATA_S2 (LM_API_PSTAT | (10 << CAN_MSGID_API_S))
+#define LM_API_PSTAT_DATA_S3 (LM_API_PSTAT | (11 << CAN_MSGID_API_S))
+
+//*****************************************************************************
+//
+// The values that can be used to configure the data the Periodic Status
+// Message bytes. Bytes of a multi-byte data values are encoded as
+// little-endian, therefore B0 is the least significant byte.
+//
+//*****************************************************************************
+#define LM_PSTAT_END 0
+#define LM_PSTAT_VOLTOUT_B0 1
+#define LM_PSTAT_VOLTOUT_B1 2
+#define LM_PSTAT_VOLTBUS_B0 3
+#define LM_PSTAT_VOLTBUS_B1 4
+#define LM_PSTAT_CURRENT_B0 5
+#define LM_PSTAT_CURRENT_B1 6
+#define LM_PSTAT_TEMP_B0 7
+#define LM_PSTAT_TEMP_B1 8
+#define LM_PSTAT_POS_B0 9
+#define LM_PSTAT_POS_B1 10
+#define LM_PSTAT_POS_B2 11
+#define LM_PSTAT_POS_B3 12
+#define LM_PSTAT_SPD_B0 13
+#define LM_PSTAT_SPD_B1 14
+#define LM_PSTAT_SPD_B2 15
+#define LM_PSTAT_SPD_B3 16
+#define LM_PSTAT_LIMIT_NCLR 17
+#define LM_PSTAT_LIMIT_CLR 18
+#define LM_PSTAT_FAULT 19
+#define LM_PSTAT_STKY_FLT_NCLR 20
+#define LM_PSTAT_STKY_FLT_CLR 21
+#define LM_PSTAT_VOUT_B0 22
+#define LM_PSTAT_VOUT_B1 23
+#define LM_PSTAT_FLT_COUNT_CURRENT \
+ 24
+#define LM_PSTAT_FLT_COUNT_TEMP 25
+#define LM_PSTAT_FLT_COUNT_VOLTBUS \
+ 26
+#define LM_PSTAT_FLT_COUNT_GATE 27
+#define LM_PSTAT_FLT_COUNT_COMM 28
+#define LM_PSTAT_CANSTS 29
+#define LM_PSTAT_CANERR_B0 30
+#define LM_PSTAT_CANERR_B1 31
+
+#endif // __CAN_PROTO_H__
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/CANJaguar.h b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/CANJaguar.h
new file mode 100644
index 0000000..67d6db3
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/CANJaguar.h
@@ -0,0 +1,208 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2009. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+#pragma once
+
+#include "ErrorBase.h"
+#include "MotorSafety.h"
+#include "Resource.h"
+#include "MotorSafetyHelper.h"
+#include "PIDOutput.h"
+#include "CANSpeedController.h"
+#include "HAL/Semaphore.hpp"
+#include "HAL/HAL.hpp"
+#include "NetworkCommunication/CANSessionMux.h"
+
+#include <utility>
+
+/**
+ * Luminary Micro / Vex Robotics Jaguar Speed Control
+ */
+class CANJaguar : public MotorSafety,
+ public CANSpeedController,
+ public ErrorBase
+{
+public:
+ // The internal PID control loop in the Jaguar runs at 1kHz.
+ static const int32_t kControllerRate = 1000;
+ static constexpr double kApproxBusVoltage = 12.0;
+
+ // Control mode tags
+ /** Sets an encoder as the speed reference only. <br> Passed as the "tag" when setting the control mode.*/
+ static const struct EncoderStruct {} Encoder;
+ /** Sets a quadrature encoder as the position and speed reference. <br> Passed as the "tag" when setting the control mode.*/
+ static const struct QuadEncoderStruct {} QuadEncoder;
+ /** Sets a potentiometer as the position reference only. <br> Passed as the "tag" when setting the control mode. */
+ static const struct PotentiometerStruct {} Potentiometer;
+
+ explicit CANJaguar(uint8_t deviceNumber);
+ virtual ~CANJaguar();
+
+ uint8_t getDeviceNumber() const;
+ uint8_t GetHardwareVersion();
+
+ // PIDOutput interface
+ virtual void PIDWrite(float output);
+
+ // Control mode methods
+ void EnableControl(double encoderInitialPosition = 0.0);
+ void DisableControl();
+
+ void SetPercentMode();
+ void SetPercentMode(EncoderStruct, uint16_t codesPerRev);
+ void SetPercentMode(QuadEncoderStruct, uint16_t codesPerRev);
+ void SetPercentMode(PotentiometerStruct);
+
+ void SetCurrentMode(double p, double i, double d);
+ void SetCurrentMode(EncoderStruct, uint16_t codesPerRev, double p, double i, double d);
+ void SetCurrentMode(QuadEncoderStruct, uint16_t codesPerRev, double p, double i, double d);
+ void SetCurrentMode(PotentiometerStruct, double p, double i, double d);
+
+ void SetSpeedMode(EncoderStruct, uint16_t codesPerRev, double p, double i, double d);
+ void SetSpeedMode(QuadEncoderStruct, uint16_t codesPerRev, double p, double i, double d);
+
+ void SetPositionMode(QuadEncoderStruct, uint16_t codesPerRev, double p, double i, double d);
+ void SetPositionMode(PotentiometerStruct, double p, double i, double d);
+
+ void SetVoltageMode();
+ void SetVoltageMode(EncoderStruct, uint16_t codesPerRev);
+ void SetVoltageMode(QuadEncoderStruct, uint16_t codesPerRev);
+ void SetVoltageMode(PotentiometerStruct);
+
+ // CANSpeedController interface
+ virtual float Get() override;
+ virtual void Set(float value, uint8_t syncGroup=0) override;
+ virtual void Disable() override;
+ virtual void SetP(double p) override;
+ virtual void SetI(double i) override;
+ virtual void SetD(double d) override;
+ virtual void SetPID(double p, double i, double d) override;
+ virtual double GetP() override;
+ virtual double GetI() override;
+ virtual double GetD() override;
+ virtual float GetBusVoltage() override;
+ virtual float GetOutputVoltage() override;
+ virtual float GetOutputCurrent() override;
+ virtual float GetTemperature() override;
+ virtual double GetPosition() override;
+ virtual double GetSpeed() override;
+ virtual bool GetForwardLimitOK() override;
+ virtual bool GetReverseLimitOK() override;
+ virtual uint16_t GetFaults() override;
+ virtual void SetVoltageRampRate(double rampRate) override;
+ virtual uint32_t GetFirmwareVersion() override;
+ virtual void ConfigNeutralMode(NeutralMode mode) override;
+ virtual void ConfigEncoderCodesPerRev(uint16_t codesPerRev) override;
+ virtual void ConfigPotentiometerTurns(uint16_t turns) override;
+ virtual void ConfigSoftPositionLimits(double forwardLimitPosition, double reverseLimitPosition) override;
+ virtual void DisableSoftPositionLimits() override;
+ virtual void ConfigLimitMode(LimitMode mode) override;
+ virtual void ConfigForwardLimit(double forwardLimitPosition) override;
+ virtual void ConfigReverseLimit(double reverseLimitPosition) override;
+ virtual void ConfigMaxOutputVoltage(double voltage) override;
+ virtual void ConfigFaultTime(float faultTime) override;
+ virtual void SetControlMode(ControlMode mode);
+ virtual ControlMode GetControlMode();
+
+ static void UpdateSyncGroup(uint8_t syncGroup);
+
+ void SetExpiration(float timeout);
+ float GetExpiration();
+ bool IsAlive();
+ void StopMotor();
+ bool IsSafetyEnabled();
+ void SetSafetyEnabled(bool enabled);
+ void GetDescription(char *desc);
+ uint8_t GetDeviceID();
+
+protected:
+ // Control mode helpers
+ void SetSpeedReference(uint8_t reference);
+ uint8_t GetSpeedReference();
+
+ void SetPositionReference(uint8_t reference);
+ uint8_t GetPositionReference();
+
+ uint8_t packPercentage(uint8_t *buffer, double value);
+ uint8_t packFXP8_8(uint8_t *buffer, double value);
+ uint8_t packFXP16_16(uint8_t *buffer, double value);
+ uint8_t packint16_t(uint8_t *buffer, int16_t value);
+ uint8_t packint32_t(uint8_t *buffer, int32_t value);
+ double unpackPercentage(uint8_t *buffer);
+ double unpackFXP8_8(uint8_t *buffer);
+ double unpackFXP16_16(uint8_t *buffer);
+ int16_t unpackint16_t(uint8_t *buffer);
+ int32_t unpackint32_t(uint8_t *buffer);
+
+ void sendMessage(uint32_t messageID, const uint8_t *data, uint8_t dataSize, int32_t period = CAN_SEND_PERIOD_NO_REPEAT);
+ void requestMessage(uint32_t messageID, int32_t period = CAN_SEND_PERIOD_NO_REPEAT);
+ bool getMessage(uint32_t messageID, uint32_t mask, uint8_t *data, uint8_t *dataSize);
+
+ void setupPeriodicStatus();
+ void updatePeriodicStatus();
+
+ uint8_t m_deviceNumber;
+ float m_value;
+
+ // Parameters/configuration
+ ControlMode m_controlMode;
+ uint8_t m_speedReference;
+ uint8_t m_positionReference;
+ double m_p;
+ double m_i;
+ double m_d;
+ NeutralMode m_neutralMode;
+ uint16_t m_encoderCodesPerRev;
+ uint16_t m_potentiometerTurns;
+ LimitMode m_limitMode;
+ double m_forwardLimit;
+ double m_reverseLimit;
+ double m_maxOutputVoltage;
+ double m_voltageRampRate;
+ float m_faultTime;
+
+ // Which parameters have been verified since they were last set?
+ bool m_controlModeVerified;
+ bool m_speedRefVerified;
+ bool m_posRefVerified;
+ bool m_pVerified;
+ bool m_iVerified;
+ bool m_dVerified;
+ bool m_neutralModeVerified;
+ bool m_encoderCodesPerRevVerified;
+ bool m_potentiometerTurnsVerified;
+ bool m_forwardLimitVerified;
+ bool m_reverseLimitVerified;
+ bool m_limitModeVerified;
+ bool m_maxOutputVoltageVerified;
+ bool m_voltageRampRateVerified;
+ bool m_faultTimeVerified;
+
+ // Status data
+ float m_busVoltage;
+ float m_outputVoltage;
+ float m_outputCurrent;
+ float m_temperature;
+ double m_position;
+ double m_speed;
+ uint8_t m_limits;
+ uint16_t m_faults;
+ uint32_t m_firmwareVersion;
+ uint8_t m_hardwareVersion;
+
+ // Which periodic status messages have we received at least once?
+ bool m_receivedStatusMessage0;
+ bool m_receivedStatusMessage1;
+ bool m_receivedStatusMessage2;
+
+ bool m_controlEnabled;
+
+ void verify();
+
+ MotorSafetyHelper *m_safetyHelper;
+
+private:
+ void InitCANJaguar();
+};
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/CANSpeedController.h b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/CANSpeedController.h
new file mode 100644
index 0000000..16d3088
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/CANSpeedController.h
@@ -0,0 +1,97 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2014. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+#pragma once
+
+#include "SpeedController.h"
+
+/**
+ * Interface for "smart" CAN-based speed controllers.
+ * @see CANJaguar
+ * @see CANTalon
+ */
+class CANSpeedController : public SpeedController
+{
+public:
+ enum ControlMode {
+ kPercentVbus=0,
+ kCurrent=1,
+ kSpeed=2,
+ kPosition=3,
+ kVoltage=4,
+ kFollower=5 // Not supported in Jaguar.
+ };
+
+ enum Faults {
+ kCurrentFault = 1,
+ kTemperatureFault = 2,
+ kBusVoltageFault = 4,
+ kGateDriverFault = 8,
+ /* SRX extensions */
+ kFwdLimitSwitch = 0x10,
+ kRevLimitSwitch = 0x20,
+ kFwdSoftLimit = 0x40,
+ kRevSoftLimit = 0x80,
+ };
+
+ enum Limits {
+ kForwardLimit = 1,
+ kReverseLimit = 2
+ };
+
+ enum NeutralMode {
+ /** Use the NeutralMode that is set by the jumper wire on the CAN device */
+ kNeutralMode_Jumper = 0,
+ /** Stop the motor's rotation by applying a force. */
+ kNeutralMode_Brake = 1,
+ /** Do not attempt to stop the motor. Instead allow it to coast to a stop without applying resistance. */
+ kNeutralMode_Coast = 2
+ };
+
+ enum LimitMode {
+ /** Only use switches for limits */
+ kLimitMode_SwitchInputsOnly = 0,
+ /** Use both switches and soft limits */
+ kLimitMode_SoftPositionLimits = 1,
+ /* SRX extensions */
+ /** Disable switches and disable soft limits */
+ kLimitMode_SrxDisableSwitchInputs = 2,
+ };
+
+ virtual float Get() = 0;
+ virtual void Set(float value, uint8_t syncGroup=0) = 0;
+ virtual void Disable() = 0;
+ virtual void SetP(double p) = 0;
+ virtual void SetI(double i) = 0;
+ virtual void SetD(double d) = 0;
+ virtual void SetPID(double p, double i, double d) = 0;
+ virtual double GetP() = 0;
+ virtual double GetI() = 0;
+ virtual double GetD() = 0;
+ virtual float GetBusVoltage() = 0;
+ virtual float GetOutputVoltage() = 0;
+ virtual float GetOutputCurrent() = 0;
+ virtual float GetTemperature() = 0;
+ virtual double GetPosition() = 0;
+ virtual double GetSpeed() = 0;
+ virtual bool GetForwardLimitOK() = 0;
+ virtual bool GetReverseLimitOK() = 0;
+ virtual uint16_t GetFaults() = 0;
+ virtual void SetVoltageRampRate(double rampRate) = 0;
+ virtual uint32_t GetFirmwareVersion() = 0;
+ virtual void ConfigNeutralMode(NeutralMode mode) = 0;
+ virtual void ConfigEncoderCodesPerRev(uint16_t codesPerRev) = 0;
+ virtual void ConfigPotentiometerTurns(uint16_t turns) = 0;
+ virtual void ConfigSoftPositionLimits(double forwardLimitPosition, double reverseLimitPosition) = 0;
+ virtual void DisableSoftPositionLimits() = 0;
+ virtual void ConfigLimitMode(LimitMode mode) = 0;
+ virtual void ConfigForwardLimit(double forwardLimitPosition) = 0;
+ virtual void ConfigReverseLimit(double reverseLimitPosition) = 0;
+ virtual void ConfigMaxOutputVoltage(double voltage) = 0;
+ virtual void ConfigFaultTime(float faultTime) = 0;
+ // Hold off on interface until we figure out ControlMode enums.
+// virtual void SetControlMode(ControlMode mode) = 0;
+// virtual ControlMode GetControlMode() = 0;
+};
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/CANTalon.h b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/CANTalon.h
new file mode 100644
index 0000000..c720ea0
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/CANTalon.h
@@ -0,0 +1,171 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2014. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+#pragma once
+
+#include "SafePWM.h"
+#include "CANSpeedController.h"
+#include "PIDOutput.h"
+#include "MotorSafetyHelper.h"
+
+class CanTalonSRX;
+
+/**
+ * CTRE Talon SRX Speed Controller with CAN Control
+ */
+class CANTalon : public MotorSafety,
+ public CANSpeedController,
+ public ErrorBase
+{
+public:
+ enum FeedbackDevice {
+ QuadEncoder=0,
+ AnalogPot=2,
+ AnalogEncoder=3,
+ EncRising=4,
+ EncFalling=5
+ };
+ enum StatusFrameRate {
+ StatusFrameRateGeneral=0,
+ StatusFrameRateFeedback=1,
+ StatusFrameRateQuadEncoder=2,
+ StatusFrameRateAnalogTempVbat=3,
+ };
+ explicit CANTalon(int deviceNumber);
+ explicit CANTalon(int deviceNumber,int controlPeriodMs);
+ virtual ~CANTalon();
+
+ // PIDController interface
+ virtual void PIDWrite(float output) override;
+
+ // MotorSafety interface
+ virtual void SetExpiration(float timeout) override;
+ virtual float GetExpiration() override;
+ virtual bool IsAlive() override;
+ virtual void StopMotor() override;
+ virtual void SetSafetyEnabled(bool enabled) override;
+ virtual bool IsSafetyEnabled() override;
+ virtual void GetDescription(char *desc) override;
+
+ // CANSpeedController interface
+ virtual float Get() override;
+ virtual void Set(float value, uint8_t syncGroup=0) override;
+ virtual void Disable() override;
+ virtual void EnableControl();
+ virtual void SetP(double p) override;
+ virtual void SetI(double i) override;
+ virtual void SetD(double d) override;
+ void SetF(double f);
+ void SetIzone(unsigned iz);
+ virtual void SetPID(double p, double i, double d) override;
+ void SetPID(double p, double i, double d, double f);
+ virtual double GetP() override;
+ virtual double GetI() override;
+ virtual double GetD() override;
+ double GetF();
+ virtual float GetBusVoltage() override;
+ virtual float GetOutputVoltage() override;
+ virtual float GetOutputCurrent() override;
+ virtual float GetTemperature() override;
+ void SetPosition(double pos);
+ virtual double GetPosition() override;
+ virtual double GetSpeed() override;
+ virtual int GetClosedLoopError();
+ virtual int GetAnalogIn();
+ virtual int GetAnalogInRaw();
+ virtual int GetAnalogInVel();
+ virtual int GetEncPosition();
+ virtual int GetEncVel();
+ int GetPinStateQuadA();
+ int GetPinStateQuadB();
+ int GetPinStateQuadIdx();
+ int IsFwdLimitSwitchClosed();
+ int IsRevLimitSwitchClosed();
+ int GetNumberOfQuadIdxRises();
+ void SetNumberOfQuadIdxRises(int rises);
+ virtual bool GetForwardLimitOK() override;
+ virtual bool GetReverseLimitOK() override;
+ virtual uint16_t GetFaults() override;
+ uint16_t GetStickyFaults();
+ void ClearStickyFaults();
+ virtual void SetVoltageRampRate(double rampRate) override;
+ virtual uint32_t GetFirmwareVersion() override;
+ virtual void ConfigNeutralMode(NeutralMode mode) override;
+ virtual void ConfigEncoderCodesPerRev(uint16_t codesPerRev) override;
+ virtual void ConfigPotentiometerTurns(uint16_t turns) override;
+ virtual void ConfigSoftPositionLimits(double forwardLimitPosition, double reverseLimitPosition) override;
+ virtual void DisableSoftPositionLimits() override;
+ virtual void ConfigLimitMode(LimitMode mode) override;
+ virtual void ConfigForwardLimit(double forwardLimitPosition) override;
+ virtual void ConfigReverseLimit(double reverseLimitPosition) override;
+ /**
+ * Change the fwd limit switch setting to normally open or closed.
+ * Talon will disable momentarilly if the Talon's current setting
+ * is dissimilar to the caller's requested setting.
+ *
+ * Since Talon saves setting to flash this should only affect
+ * a given Talon initially during robot install.
+ *
+ * @param normallyOpen true for normally open. false for normally closed.
+ */
+ void ConfigFwdLimitSwitchNormallyOpen(bool normallyOpen);
+ /**
+ * Change the rev limit switch setting to normally open or closed.
+ * Talon will disable momentarilly if the Talon's current setting
+ * is dissimilar to the caller's requested setting.
+ *
+ * Since Talon saves setting to flash this should only affect
+ * a given Talon initially during robot install.
+ *
+ * @param normallyOpen true for normally open. false for normally closed.
+ */
+ void ConfigRevLimitSwitchNormallyOpen(bool normallyOpen);
+ virtual void ConfigMaxOutputVoltage(double voltage) override;
+ virtual void ConfigFaultTime(float faultTime) override;
+ virtual void SetControlMode(ControlMode mode);
+ void SetFeedbackDevice(FeedbackDevice device);
+ void SetStatusFrameRateMs(StatusFrameRate stateFrame, int periodMs);
+ virtual ControlMode GetControlMode();
+ void SetSensorDirection(bool reverseSensor);
+ void SetCloseLoopRampRate(double rampRate);
+ void SelectProfileSlot(int slotIdx);
+ int GetIzone();
+ int GetIaccum();
+ void ClearIaccum();
+ int GetBrakeEnableDuringNeutral();
+
+ bool IsControlEnabled();
+ double GetSetpoint();
+private:
+ // Values for various modes as is sent in the CAN packets for the Talon.
+ enum TalonControlMode {
+ kThrottle=0,
+ kFollowerMode=5,
+ kVoltageMode=4,
+ kPositionMode=1,
+ kSpeedMode=2,
+ kCurrentMode=3,
+ kDisabled=15
+ };
+
+ int m_deviceNumber;
+ CanTalonSRX *m_impl;
+ MotorSafetyHelper *m_safetyHelper;
+ int m_profile; // Profile from CANTalon to use. Set to zero until we can actually test this.
+
+ bool m_controlEnabled;
+ ControlMode m_controlMode;
+ TalonControlMode m_sendMode;
+
+ double m_setPoint;
+ static const unsigned int kDelayForSolicitedSignalsUs = 4000;
+ /**
+ * Fixup the sendMode so Set() serializes the correct demand value.
+ * Also fills the modeSelecet in the control frame to disabled.
+ * @param mode Control mode to ultimately enter once user calls Set().
+ * @see Set()
+ */
+ void ApplyControlMode(CANSpeedController::ControlMode mode);
+};
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/Compressor.h b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/Compressor.h
new file mode 100644
index 0000000..ffe6e65
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/Compressor.h
@@ -0,0 +1,49 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2014. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+
+#ifndef Compressor_H_
+#define Compressor_H_
+
+#include "HAL/HAL.hpp"
+#include "SensorBase.h"
+
+/**
+ * PCM compressor
+ */
+class Compressor: public SensorBase {
+public:
+ explicit Compressor(uint8_t pcmID);
+ Compressor();
+ virtual ~Compressor();
+
+ void Start();
+ void Stop();
+ bool Enabled();
+
+ bool GetPressureSwitchValue();
+
+ float GetCompressorCurrent();
+
+ void SetClosedLoopControl(bool on);
+ bool GetClosedLoopControl();
+
+ bool GetCompressorCurrentTooHighFault();
+ bool GetCompressorCurrentTooHighStickyFault();
+ bool GetCompressorShortedStickyFault();
+ bool GetCompressorShortedFault();
+ bool GetCompressorNotConnectedStickyFault();
+ bool GetCompressorNotConnectedFault();
+ void ClearAllPCMStickyFaults();
+
+protected:
+ void *m_pcm_pointer;
+
+private:
+ void InitCompressor(uint8_t module);
+ void SetCompressor(bool on);
+};
+
+#endif /* Compressor_H_ */
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/ControllerPower.h b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/ControllerPower.h
new file mode 100644
index 0000000..654cfb5
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/ControllerPower.h
@@ -0,0 +1,30 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2011. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+
+#ifndef __CONTROLLER_POWER_H__
+#define __CONTROLLER_POWER_H__
+
+
+
+class ControllerPower
+{
+ public:
+ static double GetInputVoltage();
+ static double GetInputCurrent();
+ static double GetVoltage3V3();
+ static double GetCurrent3V3();
+ static bool GetEnabled3V3();
+ static int GetFaultCount3V3();
+ static double GetVoltage5V();
+ static double GetCurrent5V();
+ static bool GetEnabled5V();
+ static int GetFaultCount5V();
+ static double GetVoltage6V();
+ static double GetCurrent6V();
+ static bool GetEnabled6V();
+ static int GetFaultCount6V();
+};
+#endif
\ No newline at end of file
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/Counter.h b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/Counter.h
new file mode 100644
index 0000000..0eace8c
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/Counter.h
@@ -0,0 +1,88 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+#pragma once
+
+#include "HAL/HAL.hpp"
+#include "AnalogTriggerOutput.h"
+#include "CounterBase.h"
+#include "SensorBase.h"
+
+class DigitalGlitchFilter;
+
+/**
+ * Class for counting the number of ticks on a digital input channel.
+ * This is a general purpose class for counting repetitive events. It can return the number
+ * of counts, the period of the most recent cycle, and detect when the signal being counted
+ * has stopped by supplying a maximum cycle time.
+ *
+ * All counters will immediately start counting - Reset() them if you need them
+ * to be zeroed before use.
+ */
+class Counter : public SensorBase, public CounterBase
+{
+public:
+
+ Counter();
+ explicit Counter(int32_t channel);
+ explicit Counter(DigitalSource *source);
+ explicit Counter(DigitalSource &source);
+ explicit Counter(AnalogTrigger *trigger);
+ explicit Counter(AnalogTrigger &trigger);
+ Counter(EncodingType encodingType, DigitalSource *upSource, DigitalSource *downSource,
+ bool inverted);
+ virtual ~Counter();
+
+ void SetUpSource(int32_t channel);
+ void SetUpSource(AnalogTrigger *analogTrigger, AnalogTriggerType triggerType);
+ void SetUpSource(AnalogTrigger &analogTrigger, AnalogTriggerType triggerType);
+ void SetUpSource(DigitalSource *source);
+ void SetUpSource(DigitalSource &source);
+ void SetUpSourceEdge(bool risingEdge, bool fallingEdge);
+ void ClearUpSource();
+
+ void SetDownSource(int32_t channel);
+ void SetDownSource(AnalogTrigger *analogTrigger, AnalogTriggerType triggerType);
+ void SetDownSource(AnalogTrigger &analogTrigger, AnalogTriggerType triggerType);
+ void SetDownSource(DigitalSource *source);
+ void SetDownSource(DigitalSource &source);
+ void SetDownSourceEdge(bool risingEdge, bool fallingEdge);
+ void ClearDownSource();
+
+ void SetUpDownCounterMode();
+ void SetExternalDirectionMode();
+ void SetSemiPeriodMode(bool highSemiPeriod);
+ void SetPulseLengthMode(float threshold);
+
+ void SetReverseDirection(bool reverseDirection);
+
+ // CounterBase interface
+ int32_t Get();
+ void Reset();
+ double GetPeriod();
+ void SetMaxPeriod(double maxPeriod);
+ void SetUpdateWhenEmpty(bool enabled);
+ bool GetStopped();
+ bool GetDirection();
+ void SetSamplesToAverage(int samplesToAverage);
+ int GetSamplesToAverage();
+ uint32_t GetFPGAIndex()
+ {
+ return m_index;
+ }
+
+protected:
+ DigitalSource *m_upSource; ///< What makes the counter count up.
+ DigitalSource *m_downSource; ///< What makes the counter count down.
+ void* m_counter; ///< The FPGA counter object.
+private:
+ void InitCounter(Mode mode = kTwoPulse);
+
+ bool m_allocatedUpSource; ///< Was the upSource allocated locally?
+ bool m_allocatedDownSource; ///< Was the downSource allocated locally?
+ uint32_t m_index; ///< The index of this counter.
+
+ friend class DigitalGlitchFilter;
+};
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/CounterBase.h b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/CounterBase.h
new file mode 100644
index 0000000..f475ddf
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/CounterBase.h
@@ -0,0 +1,35 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+#pragma once
+
+#include <stdint.h>
+
+/**
+ * Interface for counting the number of ticks on a digital input channel.
+ * Encoders, Gear tooth sensors, and counters should all subclass this so it can
+ * be used to build more advanced classes for control and driving.
+ *
+ * All counters will immediately start counting - Reset() them if you need them
+ * to be zeroed before use.
+ */
+class CounterBase
+{
+public:
+ enum EncodingType
+ {
+ k1X,
+ k2X,
+ k4X
+ };
+
+ virtual ~CounterBase() {}
+ virtual int32_t Get() = 0;
+ virtual void Reset() = 0;
+ virtual double GetPeriod() = 0;
+ virtual void SetMaxPeriod(double maxPeriod) = 0;
+ virtual bool GetStopped() = 0;
+ virtual bool GetDirection() = 0;
+};
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/DigitalInput.h b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/DigitalInput.h
new file mode 100644
index 0000000..928d71e
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/DigitalInput.h
@@ -0,0 +1,119 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+#pragma once
+
+#include <array>
+
+#include "DigitalSource.h"
+
+class Encoder;
+class Counter;
+class DigitalGlitchFilter;
+
+/**
+ * Class to read a digital input.
+ * This class will read digital inputs and return the current value on the channel. Other devices
+ * such as encoders, gear tooth sensors, etc. that are implemented elsewhere will automatically
+ * allocate digital inputs and outputs as required. This class is only for devices like switches
+ * etc. that aren't implemented anywhere else.
+ */
+class DigitalInput : public DigitalSource
+{
+public:
+ explicit DigitalInput(uint32_t channel);
+ virtual ~DigitalInput();
+ virtual bool Get();
+ uint32_t GetChannel();
+
+ // Digital Source Interface
+ virtual uint32_t GetChannelForRouting();
+ virtual uint32_t GetModuleForRouting();
+ virtual bool GetAnalogTriggerForRouting();
+
+private:
+ void InitDigitalInput(uint32_t channel);
+ uint32_t m_channel;
+ bool m_lastValue;
+
+ friend DigitalGlitchFilter;
+};
+
+/**
+ * Class to enable glitch filtering on a set of digital inputs.
+ * This class will manage adding and removing digital inputs from a FPGA glitch
+ * filter. The filter lets the user configure the time that an input must remain
+ * high or low before it is classified as high or low.
+ */
+class DigitalGlitchFilter : public ErrorBase {
+ public:
+ DigitalGlitchFilter();
+ ~DigitalGlitchFilter();
+
+ /**
+ * Assigns the DigitalSource to this glitch filter.
+ *
+ * @param input The DigitalSource to add.
+ */
+ void Add(DigitalSource *input);
+
+ /**
+ * Assigns the Encoder to this glitch filter.
+ *
+ * @param input The Encoder to add.
+ */
+ void Add(Encoder *input);
+
+ /**
+ * Assigns the Counter to this glitch filter.
+ *
+ * @param input The Counter to add.
+ */
+ void Add(Counter *input);
+
+ /**
+ * Removes a digital input from this filter.
+ *
+ * Removes the DigitalSource from this glitch filter and re-assigns it to the
+ * default filter.
+ *
+ * @param input The DigitalSource to remove.
+ */
+ void Remove(DigitalSource *input);
+ void Remove(Encoder *input);
+ void Remove(Counter *input);
+
+ /**
+ * Sets the number of cycles that the input must not change state for.
+ *
+ * @param fpga_cycles The number of FPGA cycles.
+ */
+ void SetPeriodCycles(uint32_t fpga_cycles);
+
+ /**
+ * Sets the number of nanoseconds that the input must not change state for.
+ *
+ * @param nanoseconds The number of nanoseconds.
+ */
+ void SetPeriodNanoSeconds(uint64_t nanoseconds);
+
+ /**
+ * Gets the number of cycles that the input must not change state for.
+ *
+ * @return The number of cycles.
+ */
+ uint32_t GetPeriodCycles();
+ /**
+ * Gets the number of nanoseconds that the input must not change state for.
+ *
+ * @return The number of nanoseconds.
+ */
+ uint64_t GetPeriodNanoSeconds();
+
+ private:
+ int channel_index_;
+ static Mutex mutex_;
+ static ::std::array<int, 3> filter_allocated_;
+};
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/DigitalOutput.h b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/DigitalOutput.h
new file mode 100644
index 0000000..dc6012b
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/DigitalOutput.h
@@ -0,0 +1,40 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+#pragma once
+
+#include "DigitalSource.h"
+
+/**
+ * Class to write to digital outputs.
+ * Write values to the digital output channels. Other devices implemented elsewhere will allocate
+ * channels automatically so for those devices it shouldn't be done here.
+ */
+class DigitalOutput : public DigitalSource
+{
+public:
+ explicit DigitalOutput(uint32_t channel);
+ virtual ~DigitalOutput();
+ void Set(uint32_t value);
+ virtual bool Get();
+ uint32_t GetChannel();
+ void Pulse(float length);
+ bool IsPulsing();
+ void SetPWMRate(float rate);
+ void EnablePWM(float initialDutyCycle);
+ void DisablePWM();
+ void UpdateDutyCycle(float dutyCycle);
+
+ // Digital Source Interface
+ virtual uint32_t GetChannelForRouting();
+ virtual uint32_t GetModuleForRouting();
+ virtual bool GetAnalogTriggerForRouting();
+
+private:
+ void InitDigitalOutput(uint32_t channel);
+
+ uint32_t m_channel;
+ void *m_pwmGenerator;
+};
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/DigitalSource.h b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/DigitalSource.h
new file mode 100644
index 0000000..066bc63
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/DigitalSource.h
@@ -0,0 +1,25 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+#pragma once
+
+#include "InterruptableSensorBase.h"
+
+/**
+ * DigitalSource Interface.
+ * The DigitalSource represents all the possible inputs for a counter or a quadrature encoder. The source may be
+ * either a digital input or an analog input. If the caller just provides a channel, then a digital input will be
+ * constructed and freed when finished for the source. The source can either be a digital input or analog trigger
+ * but not both.
+ */
+class DigitalSource : public InterruptableSensorBase
+{
+public:
+ virtual ~DigitalSource();
+ virtual bool Get() = 0;
+ virtual uint32_t GetChannelForRouting() = 0;
+ virtual uint32_t GetModuleForRouting() = 0;
+ virtual bool GetAnalogTriggerForRouting() = 0;
+};
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/DoubleSolenoid.h b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/DoubleSolenoid.h
new file mode 100644
index 0000000..5241f89
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/DoubleSolenoid.h
@@ -0,0 +1,42 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+#pragma once
+
+#include "SolenoidBase.h"
+
+/**
+ * DoubleSolenoid class for running 2 channels of high voltage Digital Output
+ * (PCM).
+ *
+ * The DoubleSolenoid class is typically used for pneumatics solenoids that
+ * have two positions controlled by two separate channels.
+ */
+class DoubleSolenoid : public SolenoidBase
+{
+public:
+ enum Value
+ {
+ kOff,
+ kForward,
+ kReverse
+ };
+
+ explicit DoubleSolenoid(uint32_t forwardChannel, uint32_t reverseChannel);
+ DoubleSolenoid(uint8_t moduleNumber, uint32_t forwardChannel, uint32_t reverseChannel);
+ virtual ~DoubleSolenoid();
+ virtual void Set(Value value);
+ virtual Value Get();
+ bool IsFwdSolenoidBlackListed();
+ bool IsRevSolenoidBlackListed();
+
+private:
+ virtual void InitSolenoid();
+
+ uint32_t m_forwardChannel; ///< The forward channel on the module to control.
+ uint32_t m_reverseChannel; ///< The reverse channel on the module to control.
+ uint8_t m_forwardMask; ///< The mask for the forward channel.
+ uint8_t m_reverseMask; ///< The mask for the reverse channel.
+};
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/DriverStation.h b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/DriverStation.h
new file mode 100644
index 0000000..0397ca3
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/DriverStation.h
@@ -0,0 +1,114 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+#pragma once
+
+#include "SensorBase.h"
+#include "RobotState.h"
+#include "Task.h"
+#include "HAL/HAL.hpp"
+
+struct HALControlWord;
+class AnalogInput;
+
+/**
+ * Provide access to the network communication data to / from the Driver Station.
+ */
+class DriverStation : public SensorBase, public RobotStateInterface
+{
+public:
+ enum Alliance
+ {
+ kRed,
+ kBlue,
+ kInvalid
+ };
+
+ virtual ~DriverStation();
+ static DriverStation *GetInstance();
+ static void ReportError(std::string error);
+
+ static const uint32_t kJoystickPorts = 6;
+
+ float GetStickAxis(uint32_t stick, uint32_t axis);
+ int GetStickPOV(uint32_t stick, uint32_t pov);
+ uint32_t GetStickButtons(uint32_t stick);
+ bool GetStickButton(uint32_t stick, uint8_t button);
+
+ int GetStickAxisCount(uint32_t stick);
+ int GetStickPOVCount(uint32_t stick);
+ int GetStickButtonCount(uint32_t stick);
+
+ bool IsEnabled();
+ bool IsDisabled();
+ bool IsAutonomous();
+ bool IsOperatorControl();
+ bool IsTest();
+ bool IsDSAttached();
+ bool IsNewControlData();
+ bool IsFMSAttached();
+ bool IsSysActive();
+ bool IsSysBrownedOut();
+
+ Alliance GetAlliance();
+ uint32_t GetLocation();
+ void WaitForData();
+ double GetMatchTime();
+ float GetBatteryVoltage();
+
+ /** Only to be used to tell the Driver Station what code you claim to be executing
+ * for diagnostic purposes only
+ * @param entering If true, starting disabled code; if false, leaving disabled code */
+ void InDisabled(bool entering)
+ {
+ m_userInDisabled = entering;
+ }
+ /** Only to be used to tell the Driver Station what code you claim to be executing
+ * for diagnostic purposes only
+ * @param entering If true, starting autonomous code; if false, leaving autonomous code */
+ void InAutonomous(bool entering)
+ {
+ m_userInAutonomous = entering;
+ }
+ /** Only to be used to tell the Driver Station what code you claim to be executing
+ * for diagnostic purposes only
+ * @param entering If true, starting teleop code; if false, leaving teleop code */
+ void InOperatorControl(bool entering)
+ {
+ m_userInTeleop = entering;
+ }
+ /** Only to be used to tell the Driver Station what code you claim to be executing
+ * for diagnostic purposes only
+ * @param entering If true, starting test code; if false, leaving test code */
+ void InTest(bool entering)
+ {
+ m_userInTest = entering;
+ }
+
+protected:
+ DriverStation();
+
+ void GetData();
+private:
+ static void InitTask(DriverStation *ds);
+ static DriverStation *m_instance;
+ void ReportJoystickUnpluggedError(std::string message);
+ void Run();
+
+ HALJoystickAxes m_joystickAxes[kJoystickPorts];
+ HALJoystickPOVs m_joystickPOVs[kJoystickPorts];
+ HALJoystickButtons m_joystickButtons[kJoystickPorts];
+ Task m_task;
+ SEMAPHORE_ID m_newControlData;
+ MULTIWAIT_ID m_packetDataAvailableMultiWait;
+ MUTEX_ID m_packetDataAvailableMutex;
+ MULTIWAIT_ID m_waitForDataSem;
+ MUTEX_ID m_waitForDataMutex;
+ bool m_userInDisabled;
+ bool m_userInAutonomous;
+ bool m_userInTeleop;
+ bool m_userInTest;
+ double m_nextMessageTime;
+};
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/Encoder.h b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/Encoder.h
new file mode 100644
index 0000000..780d524
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/Encoder.h
@@ -0,0 +1,87 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+#pragma once
+
+#include "HAL/HAL.hpp"
+#include "CounterBase.h"
+#include "SensorBase.h"
+#include "Counter.h"
+#include "PIDSource.h"
+
+class DigitalSource;
+class DigitalGlitchFilter;
+
+/**
+ * Class to read quad encoders.
+ * Quadrature encoders are devices that count shaft rotation and can sense direction. The output of
+ * the QuadEncoder class is an integer that can count either up or down, and can go negative for
+ * reverse direction counting. When creating QuadEncoders, a direction is supplied that changes the
+ * sense of the output to make code more readable if the encoder is mounted such that forward movement
+ * generates negative values. Quadrature encoders have two digital outputs, an A Channel and a B Channel
+ * that are out of phase with each other to allow the FPGA to do direction sensing.
+ *
+ * All encoders will immediately start counting - Reset() them if you need them
+ * to be zeroed before use.
+ */
+class Encoder : public SensorBase, public CounterBase, public PIDSource
+{
+public:
+ enum IndexingType { kResetWhileHigh, kResetWhileLow, kResetOnFallingEdge, kResetOnRisingEdge };
+
+ Encoder(uint32_t aChannel, uint32_t bChannel, bool reverseDirection = false,
+ EncodingType encodingType = k4X);
+ Encoder(DigitalSource *aSource, DigitalSource *bSource, bool reverseDirection = false,
+ EncodingType encodingType = k4X);
+ Encoder(DigitalSource &aSource, DigitalSource &bSource, bool reverseDirection = false,
+ EncodingType encodingType = k4X);
+ virtual ~Encoder();
+
+ // CounterBase interface
+ int32_t Get();
+ int32_t GetRaw();
+ int32_t GetEncodingScale();
+ void Reset();
+ double GetPeriod();
+ void SetMaxPeriod(double maxPeriod);
+ bool GetStopped();
+ bool GetDirection();
+ double GetDistance();
+ double GetRate();
+ void SetMinRate(double minRate);
+ void SetDistancePerPulse(double distancePerPulse);
+ void SetReverseDirection(bool reverseDirection);
+ void SetSamplesToAverage(int samplesToAverage);
+ int GetSamplesToAverage();
+ void SetPIDSourceParameter(PIDSourceParameter pidSource);
+ double PIDGet();
+
+ void SetIndexSource(uint32_t channel, IndexingType type = kResetOnRisingEdge);
+ void SetIndexSource(DigitalSource *source, IndexingType type = kResetOnRisingEdge);
+ void SetIndexSource(DigitalSource &source, IndexingType type = kResetOnRisingEdge);
+
+ int32_t GetFPGAIndex()
+ {
+ return m_index;
+ }
+
+private:
+ void InitEncoder(bool _reverseDirection, EncodingType encodingType);
+ double DecodingScaleFactor();
+
+ DigitalSource *m_aSource; // the A phase of the quad encoder
+ DigitalSource *m_bSource; // the B phase of the quad encoder
+ bool m_allocatedASource; // was the A source allocated locally?
+ bool m_allocatedBSource; // was the B source allocated locally?
+ void* m_encoder;
+ int32_t m_index; // The encoder's FPGA index.
+ double m_distancePerPulse; // distance of travel for each encoder tick
+ Counter *m_counter; // Counter object for 1x and 2x encoding
+ EncodingType m_encodingType; // Encoding type
+ int32_t m_encodingScale; // 1x, 2x, or 4x, per the encodingType
+ PIDSourceParameter m_pidSource; // Encoder parameter that sources a PID controller
+
+ friend class DigitalGlitchFilter;
+};
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/GearTooth.h b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/GearTooth.h
new file mode 100644
index 0000000..a30e50d
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/GearTooth.h
@@ -0,0 +1,26 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+#pragma once
+
+#include "Counter.h"
+
+/**
+ * Alias for counter class.
+ * Implement the gear tooth sensor supplied by FIRST. Currently there is no reverse sensing on
+ * the gear tooth sensor, but in future versions we might implement the necessary timing in the
+ * FPGA to sense direction.
+ */
+class GearTooth : public Counter
+{
+public:
+ /// 55 uSec for threshold
+ static constexpr double kGearToothThreshold = 55e-6;
+ GearTooth(uint32_t channel, bool directionSensitive = false);
+ GearTooth(DigitalSource *source, bool directionSensitive = false);
+ GearTooth(DigitalSource &source, bool directionSensitive = false);
+ virtual ~GearTooth();
+ void EnableDirectionSensing(bool directionSensitive);
+};
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/Gyro.h b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/Gyro.h
new file mode 100644
index 0000000..240d983
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/Gyro.h
@@ -0,0 +1,56 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+#pragma once
+
+#include "SensorBase.h"
+#include "PIDSource.h"
+
+class AnalogInput;
+
+/**
+ * Use a rate gyro to return the robots heading relative to a starting position.
+ * The Gyro class tracks the robots heading based on the starting position. As the robot
+ * rotates the new heading is computed by integrating the rate of rotation returned
+ * by the sensor. When the class is instantiated, it does a short calibration routine
+ * where it samples the gyro while at rest to determine the default offset. This is
+ * subtracted from each sample to determine the heading. This gyro class must be used
+ * with a channel that is assigned one of the Analog accumulators from the FPGA. See
+ * AnalogInput for the current accumulator assignments.
+ */
+class Gyro : public SensorBase, public PIDSource
+{
+public:
+ static const uint32_t kOversampleBits = 10;
+ static const uint32_t kAverageBits = 0;
+ static constexpr float kSamplesPerSecond = 50.0;
+ static constexpr float kCalibrationSampleTime = 5.0;
+ static constexpr float kDefaultVoltsPerDegreePerSecond = 0.007;
+
+ explicit Gyro(int32_t channel);
+ explicit Gyro(AnalogInput *channel);
+ explicit Gyro(AnalogInput &channel);
+ virtual ~Gyro();
+ virtual float GetAngle();
+ virtual double GetRate();
+ void SetSensitivity(float voltsPerDegreePerSecond);
+ void SetDeadband(float volts);
+ void SetPIDSourceParameter(PIDSourceParameter pidSource);
+ virtual void Reset();
+ void InitGyro();
+
+ // PIDSource interface
+ double PIDGet();
+
+protected:
+ AnalogInput *m_analog;
+
+private:
+ float m_voltsPerDegreePerSecond;
+ float m_offset;
+ bool m_channelAllocated;
+ uint32_t m_center;
+ PIDSourceParameter m_pidSource;
+};
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/I2C.h b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/I2C.h
new file mode 100644
index 0000000..a90e4c6
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/I2C.h
@@ -0,0 +1,38 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+#pragma once
+
+#include "SensorBase.h"
+
+/**
+ * I2C bus interface class.
+ *
+ * This class is intended to be used by sensor (and other I2C device) drivers.
+ * It probably should not be used directly.
+ *
+ */
+class I2C : SensorBase
+{
+public:
+ enum Port {kOnboard, kMXP};
+
+ I2C(Port port, uint8_t deviceAddress);
+ virtual ~I2C();
+
+ bool Transaction(uint8_t *dataToSend, uint8_t sendSize, uint8_t *dataReceived,
+ uint8_t receiveSize);
+ bool AddressOnly();
+ bool Write(uint8_t registerAddress, uint8_t data);
+ bool WriteBulk(uint8_t* data, uint8_t count);
+ bool Read(uint8_t registerAddress, uint8_t count, uint8_t *data);
+ bool ReadOnly(uint8_t count, uint8_t *buffer);
+ void Broadcast(uint8_t registerAddress, uint8_t data);
+ bool VerifySensor(uint8_t registerAddress, uint8_t count, const uint8_t *expected);
+private:
+
+ Port m_port;
+ uint8_t m_deviceAddress;
+};
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/Internal/HardwareHLReporting.h b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/Internal/HardwareHLReporting.h
new file mode 100644
index 0000000..aa7ebc8
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/Internal/HardwareHLReporting.h
@@ -0,0 +1,9 @@
+
+#include "HLUsageReporting.h"
+
+class HardwareHLReporting : public HLUsageReportingInterface
+{
+public:
+ virtual void ReportScheduler();
+ virtual void ReportSmartDashboard();
+};
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/InterruptableSensorBase.h b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/InterruptableSensorBase.h
new file mode 100644
index 0000000..fb0db4f
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/InterruptableSensorBase.h
@@ -0,0 +1,42 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+#pragma once
+
+#include "HAL/HAL.hpp"
+#include "SensorBase.h"
+#include "Resource.h"
+
+class InterruptableSensorBase : public SensorBase
+{
+public:
+ enum WaitResult {
+ kTimeout = 0x0,
+ kRisingEdge = 0x1,
+ kFallingEdge = 0x100,
+ kBoth = 0x101,
+ };
+
+ InterruptableSensorBase();
+ virtual ~InterruptableSensorBase();
+ virtual uint32_t GetChannelForRouting() = 0;
+ virtual uint32_t GetModuleForRouting() = 0;
+ virtual bool GetAnalogTriggerForRouting() = 0;
+ virtual void RequestInterrupts(InterruptHandlerFunction handler, void *param); ///< Asynchronus handler version.
+ virtual void RequestInterrupts(); ///< Synchronus Wait version.
+ virtual void CancelInterrupts(); ///< Free up the underlying chipobject functions.
+ virtual WaitResult WaitForInterrupt(float timeout, bool ignorePrevious = true); ///< Synchronus version.
+ virtual void EnableInterrupts(); ///< Enable interrupts - after finishing setup.
+ virtual void DisableInterrupts(); ///< Disable, but don't deallocate.
+ virtual double ReadRisingTimestamp();///< Return the timestamp for the rising interrupt that occurred.
+ virtual double ReadFallingTimestamp();///< Return the timestamp for the falling interrupt that occurred.
+ virtual void SetUpSourceEdge(bool risingEdge, bool fallingEdge);
+protected:
+ void* m_interrupt;
+ uint32_t m_interruptIndex;
+ void AllocateInterrupts(bool watcher);
+
+ static Resource *m_interrupts;
+};
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/IterativeRobot.h b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/IterativeRobot.h
new file mode 100644
index 0000000..76a1cc0
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/IterativeRobot.h
@@ -0,0 +1,73 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+#pragma once
+
+#include "Timer.h"
+#include "RobotBase.h"
+
+/**
+ * IterativeRobot implements a specific type of Robot Program framework, extending the RobotBase class.
+ *
+ * The IterativeRobot class is intended to be subclassed by a user creating a robot program.
+ *
+ * This class is intended to implement the "old style" default code, by providing
+ * the following functions which are called by the main loop, StartCompetition(), at the appropriate times:
+ *
+ * RobotInit() -- provide for initialization at robot power-on
+ *
+ * Init() functions -- each of the following functions is called once when the
+ * appropriate mode is entered:
+ * - DisabledInit() -- called only when first disabled
+ * - AutonomousInit() -- called each and every time autonomous is entered from another mode
+ * - TeleopInit() -- called each and every time teleop is entered from another mode
+ * - TestInit() -- called each and every time test is entered from another mode
+ *
+ * Periodic() functions -- each of these functions is called iteratively at the
+ * appropriate periodic rate (aka the "slow loop"). The default period of
+ * the iterative robot is synced to the driver station control packets,
+ * giving a periodic frequency of about 50Hz (50 times per second).
+ * - DisabledPeriodic()
+ * - AutonomousPeriodic()
+ * - TeleopPeriodic()
+ * - TestPeriodic()
+ *
+ */
+
+class IterativeRobot : public RobotBase
+{
+public:
+ /*
+ * The default period for the periodic function calls (seconds)
+ * Setting the period to 0.0 will cause the periodic functions to follow
+ * the Driver Station packet rate of about 50Hz.
+ */
+ static constexpr double kDefaultPeriod = 0.0;
+
+ virtual void StartCompetition();
+
+ virtual void RobotInit();
+ virtual void DisabledInit();
+ virtual void AutonomousInit();
+ virtual void TeleopInit();
+ virtual void TestInit();
+
+ virtual void DisabledPeriodic();
+ virtual void AutonomousPeriodic();
+ virtual void TeleopPeriodic();
+ virtual void TestPeriodic();
+
+protected:
+ virtual void Prestart();
+
+ virtual ~IterativeRobot();
+ IterativeRobot();
+
+private:
+ bool m_disabledInitialized;
+ bool m_autonomousInitialized;
+ bool m_teleopInitialized;
+ bool m_testInitialized;
+};
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/Jaguar.h b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/Jaguar.h
new file mode 100644
index 0000000..1cb7b69
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/Jaguar.h
@@ -0,0 +1,28 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+#pragma once
+
+#include "SafePWM.h"
+#include "SpeedController.h"
+#include "PIDOutput.h"
+
+/**
+ * Luminary Micro / Vex Robotics Jaguar Speed Controller with PWM control
+ */
+class Jaguar : public SafePWM, public SpeedController
+{
+public:
+ explicit Jaguar(uint32_t channel);
+ virtual ~Jaguar();
+ virtual void Set(float value, uint8_t syncGroup = 0);
+ virtual float Get();
+ virtual void Disable();
+
+ virtual void PIDWrite(float output);
+
+private:
+ void InitJaguar();
+};
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/Joystick.h b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/Joystick.h
new file mode 100644
index 0000000..7ee06f6
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/Joystick.h
@@ -0,0 +1,93 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+
+#ifndef JOYSTICK_H_
+#define JOYSTICK_H_
+
+#include <cstdint>
+#include "GenericHID.h"
+#include "ErrorBase.h"
+
+class DriverStation;
+
+/**
+ * Handle input from standard Joysticks connected to the Driver Station.
+ * This class handles standard input that comes from the Driver Station. Each time a value is requested
+ * the most recent value is returned. There is a single class instance for each joystick and the mapping
+ * of ports to hardware buttons depends on the code in the driver station.
+ */
+class Joystick : public GenericHID, public ErrorBase
+{
+public:
+ static const uint32_t kDefaultXAxis = 0;
+ static const uint32_t kDefaultYAxis = 1;
+ static const uint32_t kDefaultZAxis = 2;
+ static const uint32_t kDefaultTwistAxis = 2;
+ static const uint32_t kDefaultThrottleAxis = 3;
+ typedef enum
+ {
+ kXAxis, kYAxis, kZAxis, kTwistAxis, kThrottleAxis, kNumAxisTypes
+ } AxisType;
+ static const uint32_t kDefaultTriggerButton = 1;
+ static const uint32_t kDefaultTopButton = 2;
+ typedef enum
+ {
+ kTriggerButton, kTopButton, kNumButtonTypes
+ } ButtonType;
+ typedef enum
+ {
+ kLeftRumble, kRightRumble
+ } RumbleType;
+
+ explicit Joystick(uint32_t port);
+ Joystick(uint32_t port, uint32_t numAxisTypes, uint32_t numButtonTypes);
+ virtual ~Joystick();
+
+ uint32_t GetAxisChannel(AxisType axis);
+ void SetAxisChannel(AxisType axis, uint32_t channel);
+
+ virtual float GetX(JoystickHand hand = kRightHand);
+ virtual float GetY(JoystickHand hand = kRightHand);
+ virtual float GetZ();
+ virtual float GetTwist();
+ virtual float GetThrottle();
+ virtual float GetAxis(AxisType axis);
+ float GetRawAxis(uint32_t axis);
+
+ virtual bool GetTrigger(JoystickHand hand = kRightHand);
+ virtual bool GetTop(JoystickHand hand = kRightHand);
+ virtual bool GetBumper(JoystickHand hand = kRightHand);
+ virtual bool GetRawButton(uint32_t button);
+ virtual int GetPOV(uint32_t pov = 0);
+ bool GetButton(ButtonType button);
+ static Joystick* GetStickForPort(uint32_t port);
+
+ virtual float GetMagnitude();
+ virtual float GetDirectionRadians();
+ virtual float GetDirectionDegrees();
+
+ int GetAxisCount();
+ int GetButtonCount();
+ int GetPOVCount();
+
+ void SetRumble(RumbleType type, float value);
+ void SetOutput(uint8_t outputNumber, bool value);
+ void SetOutputs(uint32_t value);
+
+private:
+ DISALLOW_COPY_AND_ASSIGN(Joystick);
+ void InitJoystick(uint32_t numAxisTypes, uint32_t numButtonTypes);
+
+ DriverStation *m_ds;
+ uint32_t m_port;
+ uint32_t *m_axes;
+ uint32_t *m_buttons;
+ uint32_t m_outputs;
+ uint16_t m_leftRumble;
+ uint16_t m_rightRumble;
+};
+
+#endif
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/MotorSafety.h b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/MotorSafety.h
new file mode 100644
index 0000000..ea7dfc4
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/MotorSafety.h
@@ -0,0 +1,20 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+#pragma once
+
+#define DEFAULT_SAFETY_EXPIRATION 0.1
+
+class MotorSafety
+{
+public:
+ virtual void SetExpiration(float timeout) = 0;
+ virtual float GetExpiration() = 0;
+ virtual bool IsAlive() = 0;
+ virtual void StopMotor() = 0;
+ virtual void SetSafetyEnabled(bool enabled) = 0;
+ virtual bool IsSafetyEnabled() = 0;
+ virtual void GetDescription(char *desc) = 0;
+};
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/MotorSafetyHelper.h b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/MotorSafetyHelper.h
new file mode 100644
index 0000000..4434263
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/MotorSafetyHelper.h
@@ -0,0 +1,35 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+#pragma once
+
+#include "ErrorBase.h"
+#include "HAL/cpp/Synchronized.hpp"
+
+class MotorSafety;
+
+class MotorSafetyHelper : public ErrorBase
+{
+public:
+ MotorSafetyHelper(MotorSafety *safeObject);
+ ~MotorSafetyHelper();
+ void Feed();
+ void SetExpiration(float expirationTime);
+ float GetExpiration();
+ bool IsAlive();
+ void Check();
+ void SetSafetyEnabled(bool enabled);
+ bool IsSafetyEnabled();
+ static void CheckMotors();
+private:
+ double m_expiration; // the expiration time for this object
+ bool m_enabled; // true if motor safety is enabled for this motor
+ double m_stopTime; // the FPGA clock value when this motor has expired
+ ReentrantMutex m_syncMutex; // protect accesses to the state for this object
+ MotorSafety *m_safeObject; // the object that is using the helper
+ MotorSafetyHelper *m_nextHelper; // next object in the list of MotorSafetyHelpers
+ static MotorSafetyHelper *m_headHelper; // the head of the list of MotorSafetyHelper objects
+ static ReentrantMutex m_listMutex; // protect accesses to the list of helpers
+};
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/NIIMAQdx.h b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/NIIMAQdx.h
new file mode 100644
index 0000000..4940063
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/NIIMAQdx.h
@@ -0,0 +1,646 @@
+//==============================================================================
+//
+// Title : NIIMAQdx.h
+// Created : 1403685834 seconds after 1/1/1970 12:00:00 UTC
+// Copyright : © Copyright 2006, National Instruments Corporation, All rights reserved
+// Purpose : Include file for NI-IMAQdx library support.
+//
+//==============================================================================
+#ifndef ___niimaqdx_h___
+#define ___niimaqdx_h___
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#if !defined(niimaqdx_types)
+#define niimaqdx_types
+
+#ifdef _CVI_
+ #pragma EnableLibraryRuntimeChecking
+#endif
+
+//==============================================================================
+// Typedefs
+//==============================================================================
+#ifndef _NI_uInt8_DEFINED_
+ #define _NI_uInt8_DEFINED_
+ typedef unsigned char uInt8;
+#endif
+
+#ifndef _NI_uInt16_DEFINED_
+ #define _NI_uInt16_DEFINED_
+ typedef unsigned short int uInt16;
+#endif
+
+#ifndef _NI_uInt32_DEFINED_
+ #define _NI_uInt32_DEFINED_
+ #if defined(_MSC_VER)
+ typedef unsigned long uInt32;
+ #elif __GNUC__
+ #if __x86_64__
+ typedef unsigned int uInt32;
+ #else
+ typedef unsigned long uInt32;
+ #endif
+ #endif
+#endif
+
+#ifndef _NI_uInt64_DEFINED_
+ #define _NI_uInt64_DEFINED_
+ #if defined(_MSC_VER) || _CVI_ >= 700
+ typedef unsigned __int64 uInt64;
+ #elif __GNUC__
+ typedef unsigned long long uInt64;
+ #endif
+#endif
+
+#ifndef _NI_Int8_DEFINED_
+ #define _NI_Int8_DEFINED_
+ typedef char Int8;
+#endif
+
+#ifndef _NI_Int16_DEFINED_
+ #define _NI_Int16_DEFINED_
+ typedef short int Int16;
+#endif
+
+#ifndef _NI_Int32_DEFINED_
+ #define _NI_Int32_DEFINED_
+ #if defined(_MSC_VER)
+ typedef long Int32;
+ #elif __GNUC__
+ #if __x86_64__
+ typedef int Int32;
+ #else
+ typedef long Int32;
+ #endif
+ #endif
+#endif
+
+#ifndef _NI_Int64_DEFINED_
+ #define _NI_Int64_DEFINED_
+ #if defined(_MSC_VER) || _CVI_ >= 700
+ typedef __int64 Int64;
+ #elif __GNUC__
+ typedef long long int Int64;
+ #endif
+#endif
+
+#ifndef _NI_float32_DEFINED_
+ #define _NI_float32_DEFINED_
+ typedef float float32;
+#endif
+
+#ifndef _NI_float64_DEFINED_
+ #define _NI_float64_DEFINED_
+ typedef double float64;
+#endif
+
+#ifndef TRUE
+ #define TRUE (1L)
+#endif
+
+#ifndef FALSE
+ #define FALSE (0L)
+#endif
+
+#ifndef _NI_GUIDHNDL_DEFINED
+ typedef uInt32 GUIHNDL;
+#endif
+
+#if (defined(_MSC_VER) || defined(_CVI_))
+ #ifndef _NI_FUNC_DEFINED
+ #define NI_FUNC __stdcall
+ #endif
+
+ #ifndef _NI_FUNCC_DEFINED
+ #define NI_FUNCC __cdecl
+ #endif
+#elif defined(__GNUC__)
+ #ifndef _NI_FUNC_DEFINED
+ #define NI_FUNC
+ #endif
+
+ #ifndef _NI_FUNCC_DEFINED
+ #define NI_FUNCC
+ #endif
+#endif
+
+#ifndef _NI_bool32_DEFINED_
+ #define _NI_bool32_DEFINED_
+ typedef uInt32 bool32;
+#endif
+
+#ifndef _NI_IMAQdxSession_DEFINED_
+ #define _NI_IMAQdxSession_DEFINED_
+ typedef uInt32 IMAQdxSession;
+#endif
+
+#define IMAQDX_MAX_API_STRING_LENGTH 512
+
+//==============================================================================
+// Forward Declare Data Structures
+//==============================================================================
+typedef struct Image_struct Image;
+
+
+//==============================================================================
+// Error Codes Enumeration
+//==============================================================================
+typedef enum IMAQdxError_enum {
+ IMAQdxErrorSuccess = 0x0, // Success
+ IMAQdxErrorSystemMemoryFull = 0xBFF69000, // Not enough memory
+ IMAQdxErrorInternal, // Internal error
+ IMAQdxErrorInvalidParameter, // Invalid parameter
+ IMAQdxErrorInvalidPointer, // Invalid pointer
+ IMAQdxErrorInvalidInterface, // Invalid camera session
+ IMAQdxErrorInvalidRegistryKey, // Invalid registry key
+ IMAQdxErrorInvalidAddress, // Invalid address
+ IMAQdxErrorInvalidDeviceType, // Invalid device type
+ IMAQdxErrorNotImplemented, // Not implemented
+ IMAQdxErrorCameraNotFound, // Camera not found
+ IMAQdxErrorCameraInUse, // Camera is already in use.
+ IMAQdxErrorCameraNotInitialized, // Camera is not initialized.
+ IMAQdxErrorCameraRemoved, // Camera has been removed.
+ IMAQdxErrorCameraRunning, // Acquisition in progress.
+ IMAQdxErrorCameraNotRunning, // No acquisition in progress.
+ IMAQdxErrorAttributeNotSupported, // Attribute not supported by the camera.
+ IMAQdxErrorAttributeNotSettable, // Unable to set attribute.
+ IMAQdxErrorAttributeNotReadable, // Unable to get attribute.
+ IMAQdxErrorAttributeOutOfRange, // Attribute value is out of range.
+ IMAQdxErrorBufferNotAvailable, // Requested buffer is unavailable.
+ IMAQdxErrorBufferListEmpty, // Buffer list is empty. Add one or more buffers.
+ IMAQdxErrorBufferListLocked, // Buffer list is already locked. Reconfigure acquisition and try again.
+ IMAQdxErrorBufferListNotLocked, // No buffer list. Reconfigure acquisition and try again.
+ IMAQdxErrorResourcesAllocated, // Transfer engine resources already allocated. Reconfigure acquisition and try again.
+ IMAQdxErrorResourcesUnavailable, // Insufficient transfer engine resources.
+ IMAQdxErrorAsyncWrite, // Unable to perform asychronous register write.
+ IMAQdxErrorAsyncRead, // Unable to perform asychronous register read.
+ IMAQdxErrorTimeout, // Timeout.
+ IMAQdxErrorBusReset, // Bus reset occurred during a transaction.
+ IMAQdxErrorInvalidXML, // Unable to load camera's XML file.
+ IMAQdxErrorFileAccess, // Unable to read/write to file.
+ IMAQdxErrorInvalidCameraURLString, // Camera has malformed URL string.
+ IMAQdxErrorInvalidCameraFile, // Invalid camera file.
+ IMAQdxErrorGenICamError, // Unknown Genicam error.
+ IMAQdxErrorFormat7Parameters, // For format 7: The combination of speed, image position, image size, and color coding is incorrect.
+ IMAQdxErrorInvalidAttributeType, // The attribute type is not compatible with the passed variable type.
+ IMAQdxErrorDLLNotFound, // The DLL could not be found.
+ IMAQdxErrorFunctionNotFound, // The function could not be found.
+ IMAQdxErrorLicenseNotActivated, // License not activated.
+ IMAQdxErrorCameraNotConfiguredForListener, // The camera is not configured properly to support a listener.
+ IMAQdxErrorCameraMulticastNotAvailable, // Unable to configure the system for multicast support.
+ IMAQdxErrorBufferHasLostPackets, // The requested buffer has lost packets and the user requested an error to be generated.
+ IMAQdxErrorGiGEVisionError, // Unknown GiGE Vision error.
+ IMAQdxErrorNetworkError, // Unknown network error.
+ IMAQdxErrorCameraUnreachable, // Unable to connect to the camera.
+ IMAQdxErrorHighPerformanceNotSupported, // High performance acquisition is not supported on the specified network interface. Connect the camera to a network interface running the high performance driver.
+ IMAQdxErrorInterfaceNotRenamed, // Unable to rename interface. Invalid or duplicate name specified.
+ IMAQdxErrorNoSupportedVideoModes, // The camera does not have any video modes which are supported.
+ IMAQdxErrorSoftwareTriggerOverrun, // Software trigger overrun.
+ IMAQdxErrorTestPacketNotReceived, // The system did not receive a test packet from the camera. The packet size may be too large for the network configuration or a firewall may be enabled.
+ IMAQdxErrorCorruptedImageReceived, // The camera returned a corrupted image.
+ IMAQdxErrorCameraConfigurationHasChanged, // The camera did not return an image of the correct type it was configured for previously.
+ IMAQdxErrorCameraInvalidAuthentication, // The camera is configured with password authentication and either the user name and password were not configured or they are incorrect.
+ IMAQdxErrorUnknownHTTPError, // The camera returned an unknown HTTP error.
+ IMAQdxErrorKernelDriverUnavailable, // Unable to attach to the kernel mode driver.
+ IMAQdxErrorPixelFormatDecoderUnavailable, // No decoder available for selected pixel format.
+ IMAQdxErrorFirmwareUpdateNeeded, // The acquisition hardware needs a firmware update before it can be used.
+ IMAQdxErrorFirmwareUpdateRebootNeeded, // The firmware on the acquisition hardware has been updated and the system must be rebooted before use.
+ IMAQdxErrorLightingCurrentOutOfRange, // The requested current level from the lighting controller is not possible.
+ IMAQdxErrorUSB3VisionError, // Unknown USB3 Vision error.
+ IMAQdxErrorInvalidU3VUSBDescriptor, // The camera has a USB descriptor that is incompatible with the USB3 Vision specification.
+ IMAQdxErrorU3VInvalidControlInterface, // The USB3 Vision control interface is not implemented or is invalid on this camera.
+ IMAQdxErrorU3VControlInterfaceError, // There was an error from the control interface of the USB3 Vision camera.
+ IMAQdxErrorU3VInvalidEventInterface, // The USB3 Vision event interface is not implemented or is invalid on this camera.
+ IMAQdxErrorU3VEventInterfaceError, // There was an error from the event interface of the USB3 Vision camera.
+ IMAQdxErrorU3VInvalidStreamInterface, // The USB3 Vision stream interface is not implemented or is invalid on this camera.
+ IMAQdxErrorU3VStreamInterfaceError, // There was an error from the stream interface of the USB3 Vision camera.
+ IMAQdxErrorU3VUnsupportedConnectionSpeed, // The USB connection speed is not supported by the camera. Check whether the camera is plugged into a USB 2.0 port instead of a USB 3.0 port. If so, verify that the camera supports this use case.
+ IMAQdxErrorU3VInsufficientPower, // The USB3 Vision camera requires more current than can be supplied by the USB port in use.
+ IMAQdxErrorU3VInvalidMaxCurrent, // The U3V_MaximumCurrentUSB20_mA registry value is not valid for the connected USB3 Vision camera.
+ IMAQdxErrorBufferIncompleteData, // The requested buffer has incomplete data and the user requested an error to be generated.
+ IMAQdxErrorCameraAcquisitionConfigFailed, // The camera returned an error starting the acquisition.
+ IMAQdxErrorCameraClosePending, // The camera still has outstanding references and will be closed when these operations complete.
+ IMAQdxErrorSoftwareFault, // An unexpected software error occurred.
+ IMAQdxErrorCameraPropertyInvalid, // The value for an invalid camera property was requested.
+ IMAQdxErrorJumboFramesNotEnabled, // Jumbo frames are not enabled on the host. Maximum packet size is 1500 bytes.
+ IMAQdxErrorBayerPixelFormatNotSelected, // This operation requires that the camera has a Bayer pixel format selected.
+ IMAQdxErrorGuard = 0xFFFFFFFF,
+} IMAQdxError;
+
+
+//==============================================================================
+// Bus Type Enumeration
+//==============================================================================
+typedef enum IMAQdxBusType_enum {
+ IMAQdxBusTypeFireWire = 0x31333934,
+ IMAQdxBusTypeEthernet = 0x69707634,
+ IMAQdxBusTypeSimulator = 0x2073696D,
+ IMAQdxBusTypeDirectShow = 0x64736877,
+ IMAQdxBusTypeIP = 0x4950636D,
+ IMAQdxBusTypeSmartCam2 = 0x53436132,
+ IMAQdxBusTypeUSB3Vision = 0x55534233,
+ IMAQdxBusTypeUVC = 0x55564320,
+ IMAQdxBusTypeGuard = 0xFFFFFFFF,
+} IMAQdxBusType;
+
+
+//==============================================================================
+// Camera Control Mode Enumeration
+//==============================================================================
+typedef enum IMAQdxCameraControlMode_enum {
+ IMAQdxCameraControlModeController,
+ IMAQdxCameraControlModeListener,
+ IMAQdxCameraControlModeGuard = 0xFFFFFFFF,
+} IMAQdxCameraControlMode;
+
+
+//==============================================================================
+// Buffer Number Mode Enumeration
+//==============================================================================
+typedef enum IMAQdxBufferNumberMode_enum {
+ IMAQdxBufferNumberModeNext,
+ IMAQdxBufferNumberModeLast,
+ IMAQdxBufferNumberModeBufferNumber,
+ IMAQdxBufferNumberModeGuard = 0xFFFFFFFF,
+} IMAQdxBufferNumberMode;
+
+
+//==============================================================================
+// Plug n Play Event Enumeration
+//==============================================================================
+typedef enum IMAQdxPnpEvent_enum {
+ IMAQdxPnpEventCameraAttached,
+ IMAQdxPnpEventCameraDetached,
+ IMAQdxPnpEventBusReset,
+ IMAQdxPnpEventGuard = 0xFFFFFFFF,
+} IMAQdxPnpEvent;
+
+
+//==============================================================================
+// Bayer Pattern Enumeration
+//==============================================================================
+typedef enum IMAQdxBayerPattern_enum {
+ IMAQdxBayerPatternNone,
+ IMAQdxBayerPatternGB,
+ IMAQdxBayerPatternGR,
+ IMAQdxBayerPatternBG,
+ IMAQdxBayerPatternRG,
+ IMAQdxBayerPatternHardware,
+ IMAQdxBayerPatternGuard = 0xFFFFFFFF,
+} IMAQdxBayerPattern;
+
+
+//==============================================================================
+// Bayer Decode Algorithm Enumeration
+//==============================================================================
+typedef enum IMAQdxBayerAlgorithm_enum {
+ IMAQdxBayerAlgorithmBilinear,
+ IMAQdxBayerAlgorithmVNG,
+ IMAQdxBayerAlgorithmGuard = 0xFFFFFFFF,
+} IMAQdxBayerAlgorithm;
+
+
+//==============================================================================
+// Output Image Types -- Values match Vision Development Module image types
+//==============================================================================
+typedef enum IMAQdxOutputImageType_enum {
+ IMAQdxOutputImageTypeU8 = 0,
+ IMAQdxOutputImageTypeI16 = 1,
+ IMAQdxOutputImageTypeU16 = 7,
+ IMAQdxOutputImageTypeRGB32 = 4,
+ IMAQdxOutputImageTypeRGB64 = 6,
+ IMAQdxOutputImageTypeAuto = 0x7FFFFFFF,
+ IMAQdxOutputImageTypeGuard = 0xFFFFFFFF,
+} IMAQdxOutputImageType;
+
+
+//==============================================================================
+// Controller Destination Mode Enumeration
+//==============================================================================
+typedef enum IMAQdxDestinationMode_enum {
+ IMAQdxDestinationModeUnicast,
+ IMAQdxDestinationModeBroadcast,
+ IMAQdxDestinationModeMulticast,
+ IMAQdxDestinationModeGuard = 0xFFFFFFFF,
+} IMAQdxDestinationMode;
+
+
+//==============================================================================
+// Attribute Type Enumeration
+//==============================================================================
+typedef enum IMAQdxAttributeType_enum {
+ IMAQdxAttributeTypeU32,
+ IMAQdxAttributeTypeI64,
+ IMAQdxAttributeTypeF64,
+ IMAQdxAttributeTypeString,
+ IMAQdxAttributeTypeEnum,
+ IMAQdxAttributeTypeBool,
+ IMAQdxAttributeTypeCommand,
+ IMAQdxAttributeTypeBlob,
+ IMAQdxAttributeTypeGuard = 0xFFFFFFFF,
+} IMAQdxAttributeType;
+
+
+//==============================================================================
+// Value Type Enumeration
+//==============================================================================
+typedef enum IMAQdxValueType_enum {
+ IMAQdxValueTypeU32,
+ IMAQdxValueTypeI64,
+ IMAQdxValueTypeF64,
+ IMAQdxValueTypeString,
+ IMAQdxValueTypeEnumItem,
+ IMAQdxValueTypeBool,
+ IMAQdxValueTypeDisposableString,
+ IMAQdxValueTypeGuard = 0xFFFFFFFF,
+} IMAQdxValueType;
+
+
+//==============================================================================
+// Interface File Flags Enumeration
+//==============================================================================
+typedef enum IMAQdxInterfaceFileFlags_enum {
+ IMAQdxInterfaceFileFlagsConnected = 0x1,
+ IMAQdxInterfaceFileFlagsDirty = 0x2,
+ IMAQdxInterfaceFileFlagsGuard = 0xFFFFFFFF,
+} IMAQdxInterfaceFileFlags;
+
+
+//==============================================================================
+// Overwrite Mode Enumeration
+//==============================================================================
+typedef enum IMAQdxOverwriteMode_enum {
+ IMAQdxOverwriteModeGetOldest = 0x0,
+ IMAQdxOverwriteModeFail = 0x2,
+ IMAQdxOverwriteModeGetNewest = 0x3,
+ IMAQdxOverwriteModeGuard = 0xFFFFFFFF,
+} IMAQdxOverwriteMode;
+
+
+//==============================================================================
+// Incomplete Buffer Mode Enumeration
+//==============================================================================
+typedef enum IMAQdxIncompleteBufferMode_enum {
+ IMAQdxIncompleteBufferModeIgnore,
+ IMAQdxIncompleteBufferModeFail,
+ IMAQdxIncompleteBufferModeGuard = 0xFFFFFFFF,
+} IMAQdxIncompleteBufferMode;
+
+
+//==============================================================================
+// Lost Packet Mode Enumeration
+//==============================================================================
+typedef enum IMAQdxLostPacketMode_enum {
+ IMAQdxLostPacketModeIgnore,
+ IMAQdxLostPacketModeFail,
+ IMAQdxLostPacketModeGuard = 0xFFFFFFFF,
+} IMAQdxLostPacketMode;
+
+
+//==============================================================================
+// Attribute Visibility Enumeration
+//==============================================================================
+typedef enum IMAQdxAttributeVisibility_enum {
+ IMAQdxAttributeVisibilitySimple = 0x00001000,
+ IMAQdxAttributeVisibilityIntermediate = 0x00002000,
+ IMAQdxAttributeVisibilityAdvanced = 0x00004000,
+ IMAQdxAttributeVisibilityGuard = 0xFFFFFFFF,
+} IMAQdxAttributeVisibility;
+
+
+//==============================================================================
+// Stream Channel Mode Enumeration
+//==============================================================================
+typedef enum IMAQdxStreamChannelMode_enum {
+ IMAQdxStreamChannelModeAutomatic,
+ IMAQdxStreamChannelModeManual,
+ IMAQdxStreamChannelModeGuard = 0xFFFFFFFF,
+} IMAQdxStreamChannelMode;
+
+
+//==============================================================================
+// Pixel Signedness Enumeration
+//==============================================================================
+typedef enum IMAQdxPixelSignedness_enum {
+ IMAQdxPixelSignednessUnsigned,
+ IMAQdxPixelSignednessSigned,
+ IMAQdxPixelSignednessHardware,
+ IMAQdxPixelSignednessGuard = 0xFFFFFFFF,
+} IMAQdxPixelSignedness;
+
+
+//==============================================================================
+// USB Connection Speed Enumeration
+//==============================================================================
+typedef enum IMAQdxUSBConnectionSpeed_enum {
+ IMAQdxUSBConnectionSpeedLow = 1,
+ IMAQdxUSBConnectionSpeedFull = 2,
+ IMAQdxUSBConnectionSpeedHigh = 4,
+ IMAQdxUSBConnectionSpeedSuper = 8,
+ IMAQdxUSBConnectionSpeedGuard = 0xFFFFFFFF,
+} IMAQdxUSBConnectionSpeed;
+
+
+//==============================================================================
+// CVI Structures
+//==============================================================================
+#pragma pack(push, 4)
+
+
+//==============================================================================
+// Camera Information Structure
+//==============================================================================
+typedef struct IMAQdxCameraInformation_struct {
+ uInt32 Type;
+ uInt32 Version;
+ uInt32 Flags;
+ uInt32 SerialNumberHi;
+ uInt32 SerialNumberLo;
+ IMAQdxBusType BusType;
+ char InterfaceName[IMAQDX_MAX_API_STRING_LENGTH];
+ char VendorName[IMAQDX_MAX_API_STRING_LENGTH];
+ char ModelName[IMAQDX_MAX_API_STRING_LENGTH];
+ char CameraFileName[IMAQDX_MAX_API_STRING_LENGTH];
+ char CameraAttributeURL[IMAQDX_MAX_API_STRING_LENGTH];
+} IMAQdxCameraInformation;
+
+
+//==============================================================================
+// Camera File Structure
+//==============================================================================
+typedef struct IMAQdxCameraFile_struct {
+ uInt32 Type;
+ uInt32 Version;
+ char FileName[IMAQDX_MAX_API_STRING_LENGTH];
+} IMAQdxCameraFile;
+
+
+//==============================================================================
+// Attribute Information Structure
+//==============================================================================
+typedef struct IMAQdxAttributeInformation_struct {
+ IMAQdxAttributeType Type;
+ bool32 Readable;
+ bool32 Writable;
+ char Name[IMAQDX_MAX_API_STRING_LENGTH];
+} IMAQdxAttributeInformation;
+
+
+//==============================================================================
+// Enumeration Item Structure
+//==============================================================================
+typedef struct IMAQdxEnumItem_struct {
+ uInt32 Value;
+ uInt32 Reserved;
+ char Name[IMAQDX_MAX_API_STRING_LENGTH];
+} IMAQdxEnumItem;
+
+
+//==============================================================================
+// Camera Information Structure
+//==============================================================================
+typedef IMAQdxEnumItem IMAQdxVideoMode;
+
+
+#pragma pack(pop)
+
+
+//==============================================================================
+// Callbacks
+//==============================================================================
+typedef uInt32 (NI_FUNC *FrameDoneEventCallbackPtr)(IMAQdxSession id, uInt32 bufferNumber, void* callbackData);
+typedef uInt32 (NI_FUNC *PnpEventCallbackPtr)(IMAQdxSession id, IMAQdxPnpEvent pnpEvent, void* callbackData);
+typedef void (NI_FUNC *AttributeUpdatedEventCallbackPtr)(IMAQdxSession id, const char* name, void* callbackData);
+
+#endif //niimaqdx_types
+//==============================================================================
+// Attributes
+//==============================================================================
+#define IMAQdxAttributeBaseAddress "CameraInformation::BaseAddress" // Read only. Gets the base address of the camera registers.
+#define IMAQdxAttributeBusType "CameraInformation::BusType" // Read only. Gets the bus type of the camera.
+#define IMAQdxAttributeModelName "CameraInformation::ModelName" // Read only. Returns the model name.
+#define IMAQdxAttributeSerialNumberHigh "CameraInformation::SerialNumberHigh" // Read only. Gets the upper 32-bits of the camera 64-bit serial number.
+#define IMAQdxAttributeSerialNumberLow "CameraInformation::SerialNumberLow" // Read only. Gets the lower 32-bits of the camera 64-bit serial number.
+#define IMAQdxAttributeVendorName "CameraInformation::VendorName" // Read only. Returns the vendor name.
+#define IMAQdxAttributeHostIPAddress "CameraInformation::HostIPAddress" // Read only. Returns the host adapter IP address.
+#define IMAQdxAttributeIPAddress "CameraInformation::IPAddress" // Read only. Returns the IP address.
+#define IMAQdxAttributePrimaryURLString "CameraInformation::PrimaryURLString" // Read only. Gets the camera's primary URL string.
+#define IMAQdxAttributeSecondaryURLString "CameraInformation::SecondaryURLString" // Read only. Gets the camera's secondary URL string.
+#define IMAQdxAttributeAcqInProgress "StatusInformation::AcqInProgress" // Read only. Gets the current state of the acquisition. TRUE if acquiring; otherwise FALSE.
+#define IMAQdxAttributeLastBufferCount "StatusInformation::LastBufferCount" // Read only. Gets the number of transferred buffers.
+#define IMAQdxAttributeLastBufferNumber "StatusInformation::LastBufferNumber" // Read only. Gets the last cumulative buffer number transferred.
+#define IMAQdxAttributeLostBufferCount "StatusInformation::LostBufferCount" // Read only. Gets the number of lost buffers during an acquisition session.
+#define IMAQdxAttributeLostPacketCount "StatusInformation::LostPacketCount" // Read only. Gets the number of lost packets during an acquisition session.
+#define IMAQdxAttributeRequestedResendPackets "StatusInformation::RequestedResendPacketCount" // Read only. Gets the number of packets requested to be resent during an acquisition session.
+#define IMAQdxAttributeReceivedResendPackets "StatusInformation::ReceivedResendPackets" // Read only. Gets the number of packets that were requested to be resent during an acquisition session and were completed.
+#define IMAQdxAttributeHandledEventCount "StatusInformation::HandledEventCount" // Read only. Gets the number of handled events during an acquisition session.
+#define IMAQdxAttributeLostEventCount "StatusInformation::LostEventCount" // Read only. Gets the number of lost events during an acquisition session.
+#define IMAQdxAttributeBayerGainB "AcquisitionAttributes::Bayer::GainB" // Sets/gets the white balance gain for the blue component of the Bayer conversion.
+#define IMAQdxAttributeBayerGainG "AcquisitionAttributes::Bayer::GainG" // Sets/gets the white balance gain for the green component of the Bayer conversion.
+#define IMAQdxAttributeBayerGainR "AcquisitionAttributes::Bayer::GainR" // Sets/gets the white balance gain for the red component of the Bayer conversion.
+#define IMAQdxAttributeBayerPattern "AcquisitionAttributes::Bayer::Pattern" // Sets/gets the Bayer pattern to use.
+#define IMAQdxAttributeStreamChannelMode "AcquisitionAttributes::Controller::StreamChannelMode" // Gets/sets the mode for allocating a FireWire stream channel.
+#define IMAQdxAttributeDesiredStreamChannel "AcquisitionAttributes::Controller::DesiredStreamChannel" // Gets/sets the stream channel to manually allocate.
+#define IMAQdxAttributeFrameInterval "AcquisitionAttributes::FrameInterval" // Read only. Gets the duration in milliseconds between successive frames.
+#define IMAQdxAttributeIgnoreFirstFrame "AcquisitionAttributes::IgnoreFirstFrame" // Gets/sets the video delay of one frame between starting the camera and receiving the video feed.
+#define IMAQdxAttributeOffsetX "OffsetX" // Gets/sets the left offset of the image.
+#define IMAQdxAttributeOffsetY "OffsetY" // Gets/sets the top offset of the image.
+#define IMAQdxAttributeWidth "Width" // Gets/sets the width of the image.
+#define IMAQdxAttributeHeight "Height" // Gets/sets the height of the image.
+#define IMAQdxAttributePixelFormat "PixelFormat" // Gets/sets the pixel format of the source sensor.
+#define IMAQdxAttributePacketSize "PacketSize" // Gets/sets the packet size in bytes.
+#define IMAQdxAttributePayloadSize "PayloadSize" // Gets/sets the frame size in bytes.
+#define IMAQdxAttributeSpeed "AcquisitionAttributes::Speed" // Gets/sets the transfer speed in Mbps for a FireWire packet.
+#define IMAQdxAttributeShiftPixelBits "AcquisitionAttributes::ShiftPixelBits" // Gets/sets the alignment of 16-bit cameras. Downshift the pixel bits if the camera returns most significant bit-aligned data.
+#define IMAQdxAttributeSwapPixelBytes "AcquisitionAttributes::SwapPixelBytes" // Gets/sets the endianness of 16-bit cameras. Swap the pixel bytes if the camera returns little endian data.
+#define IMAQdxAttributeOverwriteMode "AcquisitionAttributes::OverwriteMode" // Gets/sets the overwrite mode, used to determine acquisition when an image transfer cannot be completed due to an overwritten internal buffer.
+#define IMAQdxAttributeTimeout "AcquisitionAttributes::Timeout" // Gets/sets the timeout value in milliseconds, used to abort an acquisition when the image transfer cannot be completed within the delay.
+#define IMAQdxAttributeVideoMode "AcquisitionAttributes::VideoMode" // Gets/sets the video mode for a camera.
+#define IMAQdxAttributeBitsPerPixel "AcquisitionAttributes::BitsPerPixel" // Gets/sets the actual bits per pixel. For 16-bit components, this represents the actual bit depth (10-, 12-, 14-, or 16-bit).
+#define IMAQdxAttributePixelSignedness "AcquisitionAttributes::PixelSignedness" // Gets/sets the signedness of the pixel. For 16-bit components, this represents the actual pixel signedness (Signed, or Unsigned).
+#define IMAQdxAttributeReserveDualPackets "AcquisitionAttributes::ReserveDualPackets" // Gets/sets if dual packets will be reserved for a very large FireWire packet.
+#define IMAQdxAttributeReceiveTimestampMode "AcquisitionAttributes::ReceiveTimestampMode" // Gets/sets the mode for timestamping images received by the driver.
+#define IMAQdxAttributeActualPeakBandwidth "AcquisitionAttributes::AdvancedEthernet::BandwidthControl::ActualPeakBandwidth" // Read only. Returns the actual maximum peak bandwidth the camera will be configured to use.
+#define IMAQdxAttributeDesiredPeakBandwidth "AcquisitionAttributes::AdvancedEthernet::BandwidthControl::DesiredPeakBandwidth" // Gets/sets the desired maximum peak bandwidth the camera should use.
+#define IMAQdxAttributeDestinationMode "AcquisitionAttributes::AdvancedEthernet::Controller::DestinationMode" // Gets/Sets where the camera is instructed to send the image stream.
+#define IMAQdxAttributeDestinationMulticastAddress "AcquisitionAttributes::AdvancedEthernet::Controller::DestinationMulticastAddress" // Gets/Sets the multicast address the camera should send data in multicast mode.
+#define IMAQdxAttributeEventsEnabled "AcquisitionAttributes::AdvancedEthernet::EventParameters::EventsEnabled" // Gets/Sets if events will be handled.
+#define IMAQdxAttributeMaxOutstandingEvents "AcquisitionAttributes::AdvancedEthernet::EventParameters::MaxOutstandingEvents" // Gets/Sets the maximum number of outstanding events to queue.
+#define IMAQdxAttributeTestPacketEnabled "AcquisitionAttributes::AdvancedEthernet::TestPacketParameters::TestPacketEnabled" // Gets/Sets whether the driver will validate the image streaming settings using test packets prior to an acquisition
+#define IMAQdxAttributeTestPacketTimeout "AcquisitionAttributes::AdvancedEthernet::TestPacketParameters::TestPacketTimeout" // Gets/Sets the timeout for validating test packet reception (if enabled)
+#define IMAQdxAttributeMaxTestPacketRetries "AcquisitionAttributes::AdvancedEthernet::TestPacketParameters::MaxTestPacketRetries" // Gets/Sets the number of retries for validating test packet reception (if enabled)
+#define IMAQdxAttributeChunkDataDecodingEnabled "AcquisitionAttributes::ChunkDataDecoding::ChunkDataDecodingEnabled" // Gets/Sets whether the driver will decode any chunk data in the image stream
+#define IMAQdxAttributeChunkDataDecodingMaxElementSize "AcquisitionAttributes::ChunkDataDecoding::MaximumChunkCopySize" // Gets/Sets the maximum size of any single chunk data element that will be made available
+#define IMAQdxAttributeLostPacketMode "AcquisitionAttributes::AdvancedEthernet::LostPacketMode" // Gets/sets the behavior when the user extracts a buffer that has missing packets.
+#define IMAQdxAttributeMemoryWindowSize "AcquisitionAttributes::AdvancedEthernet::ResendParameters::MemoryWindowSize" // Gets/sets the size of the memory window of the camera in kilobytes. Should match the camera's internal buffer size.
+#define IMAQdxAttributeResendsEnabled "AcquisitionAttributes::AdvancedEthernet::ResendParameters::ResendsEnabled" // Gets/sets if resends will be issued for missing packets.
+#define IMAQdxAttributeResendThresholdPercentage "AcquisitionAttributes::AdvancedEthernet::ResendParameters::ResendThresholdPercentage" // Gets/sets the threshold of the packet processing window that will trigger packets to be resent.
+#define IMAQdxAttributeResendBatchingPercentage "AcquisitionAttributes::AdvancedEthernet::ResendParameters::ResendBatchingPercentage" // Gets/sets the percent of the packet resend threshold that will be issued as one group past the initial threshold sent in a single request.
+#define IMAQdxAttributeMaxResendsPerPacket "AcquisitionAttributes::AdvancedEthernet::ResendParameters::MaxResendsPerPacket" // Gets/sets the maximum number of resend requests that will be issued for a missing packet.
+#define IMAQdxAttributeResendResponseTimeout "AcquisitionAttributes::AdvancedEthernet::ResendParameters::ResendResponseTimeout" // Gets/sets the time to wait for a resend request to be satisfied before sending another.
+#define IMAQdxAttributeNewPacketTimeout "AcquisitionAttributes::AdvancedEthernet::ResendParameters::NewPacketTimeout" // Gets/sets the time to wait for new packets to arrive in a partially completed image before assuming the rest of the image was lost.
+#define IMAQdxAttributeMissingPacketTimeout "AcquisitionAttributes::AdvancedEthernet::ResendParameters::MissingPacketTimeout" // Gets/sets the time to wait for a missing packet before issuing a resend.
+#define IMAQdxAttributeResendTimerResolution "AcquisitionAttributes::AdvancedEthernet::ResendParameters::ResendTimerResolution" // Gets/sets the resolution of the packet processing system that is used for all packet-related timeouts.
+
+
+//==============================================================================
+// Functions
+//==============================================================================
+IMAQdxError NI_FUNC IMAQdxSnap(IMAQdxSession id, Image* image);
+IMAQdxError NI_FUNC IMAQdxConfigureGrab(IMAQdxSession id);
+IMAQdxError NI_FUNC IMAQdxGrab(IMAQdxSession id, Image* image, bool32 waitForNextBuffer, uInt32* actualBufferNumber);
+IMAQdxError NI_FUNC IMAQdxSequence(IMAQdxSession id, Image* images[], uInt32 count);
+IMAQdxError NI_FUNC IMAQdxDiscoverEthernetCameras(const char* address, uInt32 timeout);
+IMAQdxError NI_FUNC IMAQdxEnumerateCameras(IMAQdxCameraInformation cameraInformationArray[], uInt32* count, bool32 connectedOnly);
+IMAQdxError NI_FUNC IMAQdxResetCamera(const char* name, bool32 resetAll);
+IMAQdxError NI_FUNC IMAQdxOpenCamera(const char* name, IMAQdxCameraControlMode mode, IMAQdxSession* id);
+IMAQdxError NI_FUNC IMAQdxCloseCamera(IMAQdxSession id);
+IMAQdxError NI_FUNC IMAQdxConfigureAcquisition(IMAQdxSession id, bool32 continuous, uInt32 bufferCount);
+IMAQdxError NI_FUNC IMAQdxStartAcquisition(IMAQdxSession id);
+IMAQdxError NI_FUNC IMAQdxGetImage(IMAQdxSession id, Image* image, IMAQdxBufferNumberMode mode, uInt32 desiredBufferNumber, uInt32* actualBufferNumber);
+IMAQdxError NI_FUNC IMAQdxGetImageData(IMAQdxSession id, void* buffer, uInt32 bufferSize, IMAQdxBufferNumberMode mode, uInt32 desiredBufferNumber, uInt32* actualBufferNumber);
+IMAQdxError NI_FUNC IMAQdxStopAcquisition(IMAQdxSession id);
+IMAQdxError NI_FUNC IMAQdxUnconfigureAcquisition(IMAQdxSession id);
+IMAQdxError NI_FUNC IMAQdxEnumerateVideoModes(IMAQdxSession id, IMAQdxVideoMode videoModeArray[], uInt32* count, uInt32* currentMode);
+IMAQdxError NI_FUNC IMAQdxEnumerateAttributes(IMAQdxSession id, IMAQdxAttributeInformation attributeInformationArray[], uInt32* count, const char* root);
+IMAQdxError NI_FUNC IMAQdxGetAttribute(IMAQdxSession id, const char* name, IMAQdxValueType type, void* value);
+IMAQdxError NI_FUNCC IMAQdxSetAttribute(IMAQdxSession id, const char* name, IMAQdxValueType type, ...);
+IMAQdxError NI_FUNC IMAQdxGetAttributeMinimum(IMAQdxSession id, const char* name, IMAQdxValueType type, void* value);
+IMAQdxError NI_FUNC IMAQdxGetAttributeMaximum(IMAQdxSession id, const char* name, IMAQdxValueType type, void* value);
+IMAQdxError NI_FUNC IMAQdxGetAttributeIncrement(IMAQdxSession id, const char* name, IMAQdxValueType type, void* value);
+IMAQdxError NI_FUNC IMAQdxGetAttributeType(IMAQdxSession id, const char* name, IMAQdxAttributeType* type);
+IMAQdxError NI_FUNC IMAQdxIsAttributeReadable(IMAQdxSession id, const char* name, bool32* readable);
+IMAQdxError NI_FUNC IMAQdxIsAttributeWritable(IMAQdxSession id, const char* name, bool32* writable);
+IMAQdxError NI_FUNC IMAQdxEnumerateAttributeValues(IMAQdxSession id, const char* name, IMAQdxEnumItem list[], uInt32* size);
+IMAQdxError NI_FUNC IMAQdxGetAttributeTooltip(IMAQdxSession id, const char* name, char* tooltip, uInt32 length);
+IMAQdxError NI_FUNC IMAQdxGetAttributeUnits(IMAQdxSession id, const char* name, char* units, uInt32 length);
+IMAQdxError NI_FUNC IMAQdxRegisterFrameDoneEvent(IMAQdxSession id, uInt32 bufferInterval, FrameDoneEventCallbackPtr callbackFunction, void* callbackData);
+IMAQdxError NI_FUNC IMAQdxRegisterPnpEvent(IMAQdxSession id, IMAQdxPnpEvent event, PnpEventCallbackPtr callbackFunction, void* callbackData);
+IMAQdxError NI_FUNC IMAQdxWriteRegister(IMAQdxSession id, uInt32 offset, uInt32 value);
+IMAQdxError NI_FUNC IMAQdxReadRegister(IMAQdxSession id, uInt32 offset, uInt32* value);
+IMAQdxError NI_FUNC IMAQdxWriteMemory(IMAQdxSession id, uInt32 offset, const char* values, uInt32 count);
+IMAQdxError NI_FUNC IMAQdxReadMemory(IMAQdxSession id, uInt32 offset, char* values, uInt32 count);
+IMAQdxError NI_FUNC IMAQdxGetErrorString(IMAQdxError error, char* message, uInt32 messageLength);
+IMAQdxError NI_FUNC IMAQdxWriteAttributes(IMAQdxSession id, const char* filename);
+IMAQdxError NI_FUNC IMAQdxReadAttributes(IMAQdxSession id, const char* filename);
+IMAQdxError NI_FUNC IMAQdxResetEthernetCameraAddress(const char* name, const char* address, const char* subnet, const char* gateway, uInt32 timeout);
+IMAQdxError NI_FUNC IMAQdxEnumerateAttributes2(IMAQdxSession id, IMAQdxAttributeInformation attributeInformationArray[], uInt32* count, const char* root, IMAQdxAttributeVisibility visibility);
+IMAQdxError NI_FUNC IMAQdxGetAttributeVisibility(IMAQdxSession id, const char* name, IMAQdxAttributeVisibility* visibility);
+IMAQdxError NI_FUNC IMAQdxGetAttributeDescription(IMAQdxSession id, const char* name, char* description, uInt32 length);
+IMAQdxError NI_FUNC IMAQdxGetAttributeDisplayName(IMAQdxSession id, const char* name, char* displayName, uInt32 length);
+IMAQdxError NI_FUNC IMAQdxDispose(void* buffer);
+IMAQdxError NI_FUNC IMAQdxRegisterAttributeUpdatedEvent(IMAQdxSession id, const char* name, AttributeUpdatedEventCallbackPtr callbackFunction, void* callbackData);
+IMAQdxError NI_FUNC IMAQdxEnumerateAttributes3(IMAQdxSession id, IMAQdxAttributeInformation attributeInformationArray[], uInt32* count, const char* root, IMAQdxAttributeVisibility visibility);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif // ___niimaqdx_h___
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/NetworkCommunication/AICalibration.h b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/NetworkCommunication/AICalibration.h
new file mode 100644
index 0000000..b2f366c
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/NetworkCommunication/AICalibration.h
@@ -0,0 +1,19 @@
+
+#ifndef __AICalibration_h__
+#define __AICalibration_h__
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+ uint32_t FRC_NetworkCommunication_nAICalibration_getLSBWeight(const uint32_t aiSystemIndex, const uint32_t channel, int32_t *status);
+ int32_t FRC_NetworkCommunication_nAICalibration_getOffset(const uint32_t aiSystemIndex, const uint32_t channel, int32_t *status);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // __AICalibration_h__
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/NetworkCommunication/CANInterfacePlugin.h b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/NetworkCommunication/CANInterfacePlugin.h
new file mode 100644
index 0000000..f4c4064
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/NetworkCommunication/CANInterfacePlugin.h
@@ -0,0 +1,80 @@
+// CANInterfacePlugin.h
+//
+// Defines the API for building a CAN Interface Plugin to support
+// PWM-cable-free CAN motor control on FRC robots. This allows you
+// to connect any CAN interface to the secure Jaguar CAN driver.
+//
+
+#ifndef __CANInterfacePlugin_h__
+#define __CANInterfacePlugin_h__
+
+#include <stdint.h>
+
+#define CAN_IS_FRAME_REMOTE 0x80000000
+#define CAN_IS_FRAME_11BIT 0x40000000
+#define CAN_29BIT_MESSAGE_ID_MASK 0x1FFFFFFF
+#define CAN_11BIT_MESSAGE_ID_MASK 0x000007FF
+
+class CANInterfacePlugin
+{
+public:
+ CANInterfacePlugin() {}
+ virtual ~CANInterfacePlugin() {}
+
+ /**
+ * This entry-point of the CANInterfacePlugin is passed a message that the driver needs to send to
+ * a device on the CAN bus.
+ *
+ * This function may be called from multiple contexts and must therefore be reentrant.
+ *
+ * @param messageID The 29-bit CAN message ID in the lsbs. The msb can indicate a remote frame.
+ * @param data A pointer to a buffer containing between 0 and 8 bytes to send with the message. May be NULL if dataSize is 0.
+ * @param dataSize The number of bytes to send with the message.
+ * @return Return any error code. On success return 0.
+ */
+ virtual int32_t sendMessage(uint32_t messageID, const uint8_t *data, uint8_t dataSize) = 0;
+
+ /**
+ * This entry-point of the CANInterfacePlugin is passed buffers which should be populated with
+ * any received messages from devices on the CAN bus.
+ *
+ * This function is always called by a single task in the Jaguar driver, so it need not be reentrant.
+ *
+ * This function is expected to block for some period of time waiting for a message from the CAN bus.
+ * It may timeout periodically (returning non-zero to indicate no message was populated) to allow for
+ * shutdown and unloading of the plugin.
+ *
+ * @param messageID A reference to be populated with a received 29-bit CAN message ID in the lsbs.
+ * @param data A pointer to a buffer of 8 bytes to be populated with data received with the message.
+ * @param dataSize A reference to be populated with the size of the data received (0 - 8 bytes).
+ * @return This should return 0 if a message was populated, non-0 if no message was not populated.
+ */
+ virtual int32_t receiveMessage(uint32_t &messageID, uint8_t *data, uint8_t &dataSize) = 0;
+
+ /**
+ * This entry-point of the CANInterfacePlugin returns status of the CAN bus.
+ *
+ * This function may be called from multiple contexts and must therefore be reentrant.
+ *
+ * This function will return detailed hardware status if available for diagnostics of the CAN interface.
+ *
+ * @param busOffCount The number of times that sendMessage failed with a busOff error indicating that messages
+ * are not successfully transmitted on the bus.
+ * @param txFullCount The number of times that sendMessage failed with a txFifoFull error indicating that messages
+ * are not successfully received by any CAN device.
+ * @param receiveErrorCount The count of receive errors as reported by the CAN driver.
+ * @param transmitErrorCount The count of transmit errors as reported by the CAN driver.
+ * @return This should return 0 if all status was retrieved successfully or an error code if not.
+ */
+ virtual int32_t getStatus(uint32_t &busOffCount, uint32_t &txFullCount, uint32_t &receiveErrorCount, uint32_t &transmitErrorCount) {return 0;}
+};
+
+/**
+ * This function allows you to register a CANInterfacePlugin to provide access a CAN bus.
+ *
+ * @param interface A pointer to an object that inherits from CANInterfacePlugin and implements
+ * the pure virtual interface. If NULL, unregister the current plugin.
+ */
+void FRC_NetworkCommunication_CANSessionMux_registerInterface(CANInterfacePlugin* interface);
+
+#endif // __CANInterfacePlugin_h__
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/NetworkCommunication/CANSessionMux.h b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/NetworkCommunication/CANSessionMux.h
new file mode 100644
index 0000000..81f63c5
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/NetworkCommunication/CANSessionMux.h
@@ -0,0 +1,63 @@
+// CANSessionMux.h
+//
+// Defines the API for building a CAN Interface Plugin to support
+// PWM-cable-free CAN motor control on FRC robots. This allows you
+// to connect any CAN interface to the secure Jaguar CAN driver.
+//
+
+#ifndef __CANSessionMux_h__
+#define __CANSessionMux_h__
+
+#if defined(__vxworks)
+#include <vxWorks.h>
+#else
+#include <stdint.h>
+#endif
+
+#define CAN_SEND_PERIOD_NO_REPEAT 0
+#define CAN_SEND_PERIOD_STOP_REPEATING -1
+
+/* Flags in the upper bits of the messageID */
+#define CAN_IS_FRAME_REMOTE 0x80000000
+#define CAN_IS_FRAME_11BIT 0x40000000
+
+#define ERR_CANSessionMux_InvalidBuffer -44086
+#define ERR_CANSessionMux_MessageNotFound -44087
+#define WARN_CANSessionMux_NoToken 44087
+#define ERR_CANSessionMux_NotAllowed -44088
+#define ERR_CANSessionMux_NotInitialized -44089
+
+struct tCANStreamMessage{
+ uint32_t messageID;
+ uint32_t timeStamp;
+ uint8_t data[8];
+ uint8_t dataSize;
+};
+
+namespace nCANSessionMux
+{
+ void sendMessage_wrapper(uint32_t messageID, const uint8_t *data, uint8_t dataSize, int32_t periodMs, int32_t *status);
+ void receiveMessage_wrapper(uint32_t *messageID, uint32_t messageIDMask, uint8_t *data, uint8_t *dataSize, uint32_t *timeStamp, int32_t *status);
+ void openStreamSession(uint32_t *sessionHandle, uint32_t messageID, uint32_t messageIDMask, uint32_t maxMessages, int32_t *status);
+ void closeStreamSession(uint32_t sessionHandle);
+ void readStreamSession(uint32_t sessionHandle, struct tCANStreamMessage *messages, uint32_t messagesToRead, uint32_t *messagesRead, int32_t *status);
+ void getCANStatus(float *percentBusUtilization, uint32_t *busOffCount, uint32_t *txFullCount, uint32_t *receiveErrorCount, uint32_t *transmitErrorCount, int32_t *status);
+}
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+ void FRC_NetworkCommunication_CANSessionMux_sendMessage(uint32_t messageID, const uint8_t *data, uint8_t dataSize, int32_t periodMs, int32_t *status);
+ void FRC_NetworkCommunication_CANSessionMux_receiveMessage(uint32_t *messageID, uint32_t messageIDMask, uint8_t *data, uint8_t *dataSize, uint32_t *timeStamp, int32_t *status);
+ void FRC_NetworkCommunication_CANSessionMux_openStreamSession(uint32_t *sessionHandle, uint32_t messageID, uint32_t messageIDMask, uint32_t maxMessages, int32_t *status);
+ void FRC_NetworkCommunication_CANSessionMux_closeStreamSession(uint32_t sessionHandle);
+ void FRC_NetworkCommunication_CANSessionMux_readStreamSession(uint32_t sessionHandle, struct tCANStreamMessage *messages, uint32_t messagesToRead, uint32_t *messagesRead, int32_t *status);
+ void FRC_NetworkCommunication_CANSessionMux_getCANStatus(float *percentBusUtilization, uint32_t *busOffCount, uint32_t *txFullCount, uint32_t *receiveErrorCount, uint32_t *transmitErrorCount, int32_t *status);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // __CANSessionMux_h__
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/NetworkCommunication/FRCComm.h b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/NetworkCommunication/FRCComm.h
new file mode 100644
index 0000000..d542eca
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/NetworkCommunication/FRCComm.h
@@ -0,0 +1,121 @@
+/*************************************************************
+ * NOTICE
+ *
+ * These are the only externally exposed functions to the
+ * NetworkCommunication library
+ *
+ * This is an implementation of FRC Spec for Comm Protocol
+ * Revision 4.5, June 30, 2008
+ *
+ * Copyright (c) National Instruments 2008. All Rights Reserved.
+ *
+ *************************************************************/
+
+#ifndef __FRC_COMM_H__
+#define __FRC_COMM_H__
+
+#ifdef SIMULATION
+#include <vxWorks_compat.h>
+#ifdef USE_THRIFT
+#define EXPORT_FUNC
+#else
+#define EXPORT_FUNC __declspec(dllexport) __cdecl
+#endif
+#else
+#if defined(__vxworks)
+#include <vxWorks.h>
+#define EXPORT_FUNC
+#else
+#include <stdint.h>
+#include <pthread.h>
+#define EXPORT_FUNC
+#endif
+#endif
+
+#define ERR_FRCSystem_NetCommNotResponding -44049
+
+enum AllianceStationID_t {
+ kAllianceStationID_red1,
+ kAllianceStationID_red2,
+ kAllianceStationID_red3,
+ kAllianceStationID_blue1,
+ kAllianceStationID_blue2,
+ kAllianceStationID_blue3,
+};
+
+struct ControlWord_t {
+#ifndef __vxworks
+ uint32_t enabled : 1;
+ uint32_t autonomous : 1;
+ uint32_t test :1;
+ uint32_t eStop : 1;
+ uint32_t fmsAttached:1;
+ uint32_t dsAttached:1;
+ uint32_t control_reserved : 26;
+#else
+ uint32_t control_reserved : 26;
+ uint32_t dsAttached:1;
+ uint32_t fmsAttached:1;
+ uint32_t eStop : 1;
+ uint32_t test :1;
+ uint32_t autonomous : 1;
+ uint32_t enabled : 1;
+#endif
+};
+
+struct JoystickAxes_t {
+ uint16_t count;
+ int16_t axes[1];
+};
+
+struct JoystickPOV_t {
+ uint16_t count;
+ int16_t povs[1];
+};
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+ int EXPORT_FUNC FRC_NetworkCommunication_Reserve(void *instance);
+#ifndef SIMULATION
+ void EXPORT_FUNC getFPGAHardwareVersion(uint16_t *fpgaVersion, uint32_t *fpgaRevision);
+#endif
+ int EXPORT_FUNC setStatusData(float battery, uint8_t dsDigitalOut, uint8_t updateNumber,
+ const char *userDataHigh, int userDataHighLength,
+ const char *userDataLow, int userDataLowLength, int wait_ms);
+ int EXPORT_FUNC setErrorData(const char *errors, int errorsLength, int wait_ms);
+
+#ifdef SIMULATION
+ void EXPORT_FUNC setNewDataSem(HANDLE);
+#else
+# if defined (__vxworks)
+ void EXPORT_FUNC setNewDataSem(SEM_ID);
+# else
+ void EXPORT_FUNC setNewDataSem(pthread_mutex_t *);
+# endif
+#endif
+
+ // this uint32_t is really a LVRefNum
+ int EXPORT_FUNC setNewDataOccurRef(uint32_t refnum);
+
+ int EXPORT_FUNC FRC_NetworkCommunication_getControlWord(struct ControlWord_t *controlWord);
+ int EXPORT_FUNC FRC_NetworkCommunication_getAllianceStation(enum AllianceStationID_t *allianceStation);
+ int EXPORT_FUNC FRC_NetworkCommunication_getMatchTime(float *matchTime);
+ int EXPORT_FUNC FRC_NetworkCommunication_getJoystickAxes(uint8_t joystickNum, struct JoystickAxes_t *axes, uint8_t maxAxes);
+ int EXPORT_FUNC FRC_NetworkCommunication_getJoystickButtons(uint8_t joystickNum, uint32_t *buttons, uint8_t *count);
+ int EXPORT_FUNC FRC_NetworkCommunication_getJoystickPOVs(uint8_t joystickNum, struct JoystickPOV_t *povs, uint8_t maxPOVs);
+ int EXPORT_FUNC FRC_NetworkCommunication_setJoystickOutputs(uint8_t joystickNum, uint32_t hidOutputs, uint16_t leftRumble, uint16_t rightRumble);
+ int EXPORT_FUNC FRC_NetworkCommunication_getJoystickDesc(uint8_t joystickNum, uint8_t *isXBox, uint8_t *type, char *name,
+ uint8_t *axisCount, uint8_t *axisTypes, uint8_t *buttonCount, uint8_t *povCount);
+
+ void EXPORT_FUNC FRC_NetworkCommunication_getVersionString(char *version);
+ int EXPORT_FUNC FRC_NetworkCommunication_observeUserProgramStarting(void);
+ void EXPORT_FUNC FRC_NetworkCommunication_observeUserProgramDisabled(void);
+ void EXPORT_FUNC FRC_NetworkCommunication_observeUserProgramAutonomous(void);
+ void EXPORT_FUNC FRC_NetworkCommunication_observeUserProgramTeleop(void);
+ void EXPORT_FUNC FRC_NetworkCommunication_observeUserProgramTest(void);
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/NetworkCommunication/LoadOut.h b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/NetworkCommunication/LoadOut.h
new file mode 100644
index 0000000..fce88a2
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/NetworkCommunication/LoadOut.h
@@ -0,0 +1,58 @@
+
+#ifndef __LoadOut_h__
+#define __LoadOut_h__
+
+#ifdef SIMULATION
+#include <vxWorks_compat.h>
+#define EXPORT_FUNC __declspec(dllexport) __cdecl
+#elif defined (__vxworks)
+#include <vxWorks.h>
+#define EXPORT_FUNC
+#else
+#include <stdint.h>
+#define EXPORT_FUNC
+#endif
+
+#define kMaxModuleNumber 2
+namespace nLoadOut
+{
+#if defined(__vxworks) || defined(SIMULATION)
+ typedef enum {
+ kModuleType_Unknown = 0x00,
+ kModuleType_Analog = 0x01,
+ kModuleType_Digital = 0x02,
+ kModuleType_Solenoid = 0x03,
+ } tModuleType;
+ bool EXPORT_FUNC getModulePresence(tModuleType moduleType, uint8_t moduleNumber);
+#endif
+ typedef enum {
+ kTargetClass_Unknown = 0x00,
+ kTargetClass_FRC1 = 0x10,
+ kTargetClass_FRC2 = 0x20,
+ kTargetClass_FRC3 = 0x30,
+ kTargetClass_RoboRIO = 0x40,
+#if defined(__vxworks) || defined(SIMULATION)
+ kTargetClass_FRC2_Analog = kTargetClass_FRC2 | kModuleType_Analog,
+ kTargetClass_FRC2_Digital = kTargetClass_FRC2 | kModuleType_Digital,
+ kTargetClass_FRC2_Solenoid = kTargetClass_FRC2 | kModuleType_Solenoid,
+#endif
+ kTargetClass_FamilyMask = 0xF0,
+ kTargetClass_ModuleMask = 0x0F,
+ } tTargetClass;
+ tTargetClass EXPORT_FUNC getTargetClass();
+}
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if defined(__vxworks) || defined(SIMULATION)
+ uint32_t EXPORT_FUNC FRC_NetworkCommunication_nLoadOut_getModulePresence(uint32_t moduleType, uint8_t moduleNumber);
+#endif
+ uint32_t EXPORT_FUNC FRC_NetworkCommunication_nLoadOut_getTargetClass();
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // __LoadOut_h__
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/NetworkCommunication/UsageReporting.h b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/NetworkCommunication/UsageReporting.h
new file mode 100644
index 0000000..918ac5a
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/NetworkCommunication/UsageReporting.h
@@ -0,0 +1,139 @@
+
+#ifndef __UsageReporting_h__
+#define __UsageReporting_h__
+
+#ifdef SIMULATION
+#include <vxWorks_compat.h>
+#define EXPORT_FUNC __declspec(dllexport) __cdecl
+#elif defined (__vxworks)
+#include <vxWorks.h>
+#define EXPORT_FUNC
+#else
+#include <stdint.h>
+#include <stdlib.h>
+#define EXPORT_FUNC
+#endif
+
+#define kUsageReporting_version 1
+
+namespace nUsageReporting
+{
+ typedef enum
+ {
+ kResourceType_Controller,
+ kResourceType_Module,
+ kResourceType_Language,
+ kResourceType_CANPlugin,
+ kResourceType_Accelerometer,
+ kResourceType_ADXL345,
+ kResourceType_AnalogChannel,
+ kResourceType_AnalogTrigger,
+ kResourceType_AnalogTriggerOutput,
+ kResourceType_CANJaguar,
+ kResourceType_Compressor,
+ kResourceType_Counter,
+ kResourceType_Dashboard,
+ kResourceType_DigitalInput,
+ kResourceType_DigitalOutput,
+ kResourceType_DriverStationCIO,
+ kResourceType_DriverStationEIO,
+ kResourceType_DriverStationLCD,
+ kResourceType_Encoder,
+ kResourceType_GearTooth,
+ kResourceType_Gyro,
+ kResourceType_I2C,
+ kResourceType_Framework,
+ kResourceType_Jaguar,
+ kResourceType_Joystick,
+ kResourceType_Kinect,
+ kResourceType_KinectStick,
+ kResourceType_PIDController,
+ kResourceType_Preferences,
+ kResourceType_PWM,
+ kResourceType_Relay,
+ kResourceType_RobotDrive,
+ kResourceType_SerialPort,
+ kResourceType_Servo,
+ kResourceType_Solenoid,
+ kResourceType_SPI,
+ kResourceType_Task,
+ kResourceType_Ultrasonic,
+ kResourceType_Victor,
+ kResourceType_Button,
+ kResourceType_Command,
+ kResourceType_AxisCamera,
+ kResourceType_PCVideoServer,
+ kResourceType_SmartDashboard,
+ kResourceType_Talon,
+ kResourceType_HiTechnicColorSensor,
+ kResourceType_HiTechnicAccel,
+ kResourceType_HiTechnicCompass,
+ kResourceType_SRF08,
+ } tResourceType;
+
+ typedef enum
+ {
+ kLanguage_LabVIEW = 1,
+ kLanguage_CPlusPlus = 2,
+ kLanguage_Java = 3,
+ kLanguage_Python = 4,
+
+ kCANPlugin_BlackJagBridge = 1,
+ kCANPlugin_2CAN = 2,
+
+ kFramework_Iterative = 1,
+ kFramework_Simple = 2,
+
+ kRobotDrive_ArcadeStandard = 1,
+ kRobotDrive_ArcadeButtonSpin = 2,
+ kRobotDrive_ArcadeRatioCurve = 3,
+ kRobotDrive_Tank = 4,
+ kRobotDrive_MecanumPolar = 5,
+ kRobotDrive_MecanumCartesian = 6,
+
+ kDriverStationCIO_Analog = 1,
+ kDriverStationCIO_DigitalIn = 2,
+ kDriverStationCIO_DigitalOut = 3,
+
+ kDriverStationEIO_Acceleration = 1,
+ kDriverStationEIO_AnalogIn = 2,
+ kDriverStationEIO_AnalogOut = 3,
+ kDriverStationEIO_Button = 4,
+ kDriverStationEIO_LED = 5,
+ kDriverStationEIO_DigitalIn = 6,
+ kDriverStationEIO_DigitalOut = 7,
+ kDriverStationEIO_FixedDigitalOut = 8,
+ kDriverStationEIO_PWM = 9,
+ kDriverStationEIO_Encoder = 10,
+ kDriverStationEIO_TouchSlider = 11,
+
+ kADXL345_SPI = 1,
+ kADXL345_I2C = 2,
+
+ kCommand_Scheduler = 1,
+
+ kSmartDashboard_Instance = 1,
+ } tInstances;
+
+ /**
+ * Report the usage of a resource of interest.
+ *
+ * @param resource one of the values in the tResourceType above (max value 51).
+ * @param instanceNumber an index that identifies the resource instance.
+ * @param context an optional additional context number for some cases (such as module number). Set to 0 to omit.
+ * @param feature a string to be included describing features in use on a specific resource. Setting the same resource more than once allows you to change the feature string.
+ */
+ uint32_t EXPORT_FUNC report(tResourceType resource, uint8_t instanceNumber, uint8_t context = 0, const char *feature = NULL);
+}
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ uint32_t EXPORT_FUNC FRC_NetworkCommunication_nUsageReporting_report(uint8_t resource, uint8_t instanceNumber, uint8_t context, const char *feature);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // __UsageReporting_h__
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/NetworkCommunication/symModuleLink.h b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/NetworkCommunication/symModuleLink.h
new file mode 100644
index 0000000..00867a5
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/NetworkCommunication/symModuleLink.h
@@ -0,0 +1,21 @@
+#ifndef __SYM_MODULE_LINK_H__
+#define __SYM_MODULE_LINK_H__
+
+#include "HAL/HAL.hpp"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern STATUS moduleNameFindBySymbolName
+ (
+ const char * symbol, /* symbol name to look for */
+ char * module /* where to return module name */
+ );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/PWM.h b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/PWM.h
new file mode 100644
index 0000000..93785bb
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/PWM.h
@@ -0,0 +1,126 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+#pragma once
+
+#include "SensorBase.h"
+
+/**
+ * Class implements the PWM generation in the FPGA.
+ *
+ * The values supplied as arguments for PWM outputs range from -1.0 to 1.0. They are mapped
+ * to the hardware dependent values, in this case 0-2000 for the FPGA.
+ * Changes are immediately sent to the FPGA, and the update occurs at the next
+ * FPGA cycle. There is no delay.
+ *
+ * As of revision 0.1.10 of the FPGA, the FPGA interprets the 0-2000 values as follows:
+ * - 2000 = maximum pulse width
+ * - 1999 to 1001 = linear scaling from "full forward" to "center"
+ * - 1000 = center value
+ * - 999 to 2 = linear scaling from "center" to "full reverse"
+ * - 1 = minimum pulse width (currently .5ms)
+ * - 0 = disabled (i.e. PWM output is held low)
+ */
+class PWM : public SensorBase
+{
+public:
+ enum PeriodMultiplier
+ {
+ kPeriodMultiplier_1X = 1,
+ kPeriodMultiplier_2X = 2,
+ kPeriodMultiplier_4X = 4
+ };
+
+ explicit PWM(uint32_t channel);
+ virtual ~PWM();
+ virtual void SetRaw(unsigned short value);
+ virtual unsigned short GetRaw();
+ void SetPeriodMultiplier(PeriodMultiplier mult);
+ void SetZeroLatch();
+ void EnableDeadbandElimination(bool eliminateDeadband);
+ void SetBounds(int32_t max, int32_t deadbandMax, int32_t center, int32_t deadbandMin,
+ int32_t min);
+ void SetBounds(double max, double deadbandMax, double center, double deadbandMin, double min);
+ uint32_t GetChannel()
+ {
+ return m_channel;
+ }
+
+protected:
+ /**
+ * kDefaultPwmPeriod is in ms
+ *
+ * - 20ms periods (50 Hz) are the "safest" setting in that this works for all devices
+ * - 20ms periods seem to be desirable for Vex Motors
+ * - 20ms periods are the specified period for HS-322HD servos, but work reliably down
+ * to 10.0 ms; starting at about 8.5ms, the servo sometimes hums and get hot;
+ * by 5.0ms the hum is nearly continuous
+ * - 10ms periods work well for Victor 884
+ * - 5ms periods allows higher update rates for Luminary Micro Jaguar speed controllers.
+ * Due to the shipping firmware on the Jaguar, we can't run the update period less
+ * than 5.05 ms.
+ *
+ * kDefaultPwmPeriod is the 1x period (5.05 ms). In hardware, the period scaling is implemented as an
+ * output squelch to get longer periods for old devices.
+ */
+ static constexpr float kDefaultPwmPeriod = 5.05;
+ /**
+ * kDefaultPwmCenter is the PWM range center in ms
+ */
+ static constexpr float kDefaultPwmCenter = 1.5;
+ /**
+ * kDefaultPWMStepsDown is the number of PWM steps below the centerpoint
+ */
+ static const int32_t kDefaultPwmStepsDown = 1000;
+ static const int32_t kPwmDisabled = 0;
+
+ virtual void SetPosition(float pos);
+ virtual float GetPosition();
+ virtual void SetSpeed(float speed);
+ virtual float GetSpeed();
+
+ bool m_eliminateDeadband;
+ int32_t m_maxPwm;
+ int32_t m_deadbandMaxPwm;
+ int32_t m_centerPwm;
+ int32_t m_deadbandMinPwm;
+ int32_t m_minPwm;
+
+private:
+ void InitPWM(uint32_t channel);
+ uint32_t m_channel;
+ int32_t GetMaxPositivePwm()
+ {
+ return m_maxPwm;
+ }
+ int32_t GetMinPositivePwm()
+ {
+ return m_eliminateDeadband ? m_deadbandMaxPwm : m_centerPwm + 1;
+ }
+ int32_t GetCenterPwm()
+ {
+ return m_centerPwm;
+ }
+ int32_t GetMaxNegativePwm()
+ {
+ return m_eliminateDeadband ? m_deadbandMinPwm : m_centerPwm - 1;
+ }
+ int32_t GetMinNegativePwm()
+ {
+ return m_minPwm;
+ }
+ int32_t GetPositiveScaleFactor()
+ {
+ return GetMaxPositivePwm() - GetMinPositivePwm();
+ } ///< The scale for positive speeds.
+ int32_t GetNegativeScaleFactor()
+ {
+ return GetMaxNegativePwm() - GetMinNegativePwm();
+ } ///< The scale for negative speeds.
+ int32_t GetFullRangeScaleFactor()
+ {
+ return GetMaxPositivePwm() - GetMinNegativePwm();
+ } ///< The scale for positions.
+};
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/PowerDistributionPanel.h b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/PowerDistributionPanel.h
new file mode 100644
index 0000000..2094e08
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/PowerDistributionPanel.h
@@ -0,0 +1,33 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2014. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+
+#pragma once
+#ifndef __WPILIB_POWER_DISTRIBUTION_PANEL_H__
+#define __WPILIB_POWER_DISTRIBUTION_PANEL_H__
+
+#include "SensorBase.h"
+
+/**
+ * Class for getting voltage, current, temperature, power and energy from the CAN PDP.
+ * The PDP must be at CAN Address 0.
+ * @author Thomas Clark
+ */
+class PowerDistributionPanel : public SensorBase {
+ public:
+ PowerDistributionPanel();
+
+ double GetVoltage();
+ double GetTemperature();
+ double GetCurrent(uint8_t channel);
+ double GetTotalCurrent();
+ double GetTotalPower();
+ double GetTotalEnergy();
+ void ResetTotalEnergy();
+ void ClearStickyFaults();
+};
+
+#endif /* __WPILIB_POWER_DISTRIBUTION_PANEL_H__ */
+
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/Relay.h b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/Relay.h
new file mode 100644
index 0000000..7dc9cc9
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/Relay.h
@@ -0,0 +1,47 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+#pragma once
+
+#include "SensorBase.h"
+
+/**
+ * Class for Spike style relay outputs.
+ * Relays are intended to be connected to spikes or similar relays. The relay channels controls
+ * a pair of pins that are either both off, one on, the other on, or both on. This translates into
+ * two spike outputs at 0v, one at 12v and one at 0v, one at 0v and the other at 12v, or two
+ * spike outputs at 12V. This allows off, full forward, or full reverse control of motors without
+ * variable speed. It also allows the two channels (forward and reverse) to be used independently
+ * for something that does not care about voltage polatiry (like a solenoid).
+ */
+class Relay : public SensorBase
+{
+public:
+ enum Value
+ {
+ kOff,
+ kOn,
+ kForward,
+ kReverse
+ };
+ enum Direction
+ {
+ kBothDirections,
+ kForwardOnly,
+ kReverseOnly
+ };
+
+ Relay(uint32_t channel, Direction direction = kBothDirections);
+ virtual ~Relay();
+
+ void Set(Value value);
+ Value Get();
+
+private:
+ void InitRelay();
+
+ uint32_t m_channel;
+ Direction m_direction;
+};
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/RobotBase.h b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/RobotBase.h
new file mode 100644
index 0000000..90530be
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/RobotBase.h
@@ -0,0 +1,63 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+#pragma once
+
+#include "Base.h"
+#include "Task.h"
+
+class DriverStation;
+
+#define START_ROBOT_CLASS(_ClassName_) \
+ int main() \
+ { \
+ if (!HALInitialize()){std::cerr<<"FATAL ERROR: HAL could not be initialized"<<std::endl;return -1;} \
+ HALReport(HALUsageReporting::kResourceType_Language, HALUsageReporting::kLanguage_CPlusPlus); \
+ _ClassName_ *robot = new _ClassName_(); \
+ RobotBase::robotSetup(robot); \
+ return 0; \
+ }
+
+/**
+ * Implement a Robot Program framework.
+ * The RobotBase class is intended to be subclassed by a user creating a robot program.
+ * Overridden Autonomous() and OperatorControl() methods are called at the appropriate time
+ * as the match proceeds. In the current implementation, the Autonomous code will run to
+ * completion before the OperatorControl code could start. In the future the Autonomous code
+ * might be spawned as a task, then killed at the end of the Autonomous period.
+ */
+class RobotBase
+{
+ friend class RobotDeleter;
+public:
+ static RobotBase &getInstance();
+ static void setInstance(RobotBase* robot);
+
+ bool IsEnabled();
+ bool IsDisabled();
+ bool IsAutonomous();
+ bool IsOperatorControl();
+ bool IsTest();
+ bool IsNewDataAvailable();
+ static void startRobotTask(FUNCPTR factory);
+ static void robotTask(FUNCPTR factory, Task *task);
+ virtual void StartCompetition() = 0;
+
+ static void robotSetup(RobotBase *robot);
+
+protected:
+ virtual ~RobotBase();
+ RobotBase();
+
+ virtual void Prestart();
+
+ Task *m_task;
+ DriverStation *m_ds;
+
+private:
+ static RobotBase *m_instance;
+
+ DISALLOW_COPY_AND_ASSIGN(RobotBase);
+};
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/RobotDrive.h b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/RobotDrive.h
new file mode 100644
index 0000000..7286331
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/RobotDrive.h
@@ -0,0 +1,114 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+#pragma once
+
+#include "ErrorBase.h"
+#include <stdlib.h>
+#include "HAL/HAL.hpp"
+#include "MotorSafety.h"
+#include "MotorSafetyHelper.h"
+
+class SpeedController;
+class GenericHID;
+
+/**
+ * Utility class for handling Robot drive based on a definition of the motor configuration.
+ * The robot drive class handles basic driving for a robot. Currently, 2 and 4 motor tank and
+ * mecanum drive trains are supported. In the future other drive types like swerve might be
+ * implemented. Motor channel numbers are passed supplied on creation of the class. Those
+ * are used for either the Drive function (intended for hand created drive code, such as
+ * autonomous) or with the Tank/Arcade functions intended to be used for Operator Control
+ * driving.
+ */
+class RobotDrive : public MotorSafety, public ErrorBase
+{
+public:
+ enum MotorType
+ {
+ kFrontLeftMotor = 0,
+ kFrontRightMotor = 1,
+ kRearLeftMotor = 2,
+ kRearRightMotor = 3
+ };
+
+ RobotDrive(uint32_t leftMotorChannel, uint32_t rightMotorChannel);
+ RobotDrive(uint32_t frontLeftMotorChannel, uint32_t rearLeftMotorChannel,
+ uint32_t frontRightMotorChannel, uint32_t rearRightMotorChannel);
+ RobotDrive(SpeedController *leftMotor, SpeedController *rightMotor);
+ RobotDrive(SpeedController &leftMotor, SpeedController &rightMotor);
+ RobotDrive(SpeedController *frontLeftMotor, SpeedController *rearLeftMotor,
+ SpeedController *frontRightMotor, SpeedController *rearRightMotor);
+ RobotDrive(SpeedController &frontLeftMotor, SpeedController &rearLeftMotor,
+ SpeedController &frontRightMotor, SpeedController &rearRightMotor);
+ virtual ~RobotDrive();
+
+ void Drive(float outputMagnitude, float curve);
+ void TankDrive(GenericHID *leftStick, GenericHID *rightStick, bool squaredInputs = true);
+ void TankDrive(GenericHID &leftStick, GenericHID &rightStick, bool squaredInputs = true);
+ void TankDrive(GenericHID *leftStick, uint32_t leftAxis, GenericHID *rightStick,
+ uint32_t rightAxis, bool squaredInputs = true);
+ void TankDrive(GenericHID &leftStick, uint32_t leftAxis, GenericHID &rightStick,
+ uint32_t rightAxis, bool squaredInputs = true);
+ void TankDrive(float leftValue, float rightValue, bool squaredInputs = true);
+ void ArcadeDrive(GenericHID *stick, bool squaredInputs = true);
+ void ArcadeDrive(GenericHID &stick, bool squaredInputs = true);
+ void ArcadeDrive(GenericHID *moveStick, uint32_t moveChannel, GenericHID *rotateStick,
+ uint32_t rotateChannel, bool squaredInputs = true);
+ void ArcadeDrive(GenericHID &moveStick, uint32_t moveChannel, GenericHID &rotateStick,
+ uint32_t rotateChannel, bool squaredInputs = true);
+ void ArcadeDrive(float moveValue, float rotateValue, bool squaredInputs = true);
+ void MecanumDrive_Cartesian(float x, float y, float rotation, float gyroAngle = 0.0);
+ void MecanumDrive_Polar(float magnitude, float direction, float rotation);
+ void HolonomicDrive(float magnitude, float direction, float rotation);
+ virtual void SetLeftRightMotorOutputs(float leftOutput, float rightOutput);
+ void SetInvertedMotor(MotorType motor, bool isInverted);
+ void SetSensitivity(float sensitivity);
+ void SetMaxOutput(double maxOutput);
+ void SetCANJaguarSyncGroup(uint8_t syncGroup);
+
+ void SetExpiration(float timeout);
+ float GetExpiration();
+ bool IsAlive();
+ void StopMotor();
+ bool IsSafetyEnabled();
+ void SetSafetyEnabled(bool enabled);
+ void GetDescription(char *desc);
+
+protected:
+ void InitRobotDrive();
+ float Limit(float num);
+ void Normalize(double *wheelSpeeds);
+ void RotateVector(double &x, double &y, double angle);
+
+ static const int32_t kMaxNumberOfMotors = 4;
+
+ int32_t m_invertedMotors[kMaxNumberOfMotors];
+ float m_sensitivity;
+ double m_maxOutput;
+ bool m_deleteSpeedControllers;
+ SpeedController *m_frontLeftMotor;
+ SpeedController *m_frontRightMotor;
+ SpeedController *m_rearLeftMotor;
+ SpeedController *m_rearRightMotor;
+ MotorSafetyHelper *m_safetyHelper;
+ uint8_t m_syncGroup;
+
+private:
+ int32_t GetNumMotors()
+ {
+ int motors = 0;
+ if (m_frontLeftMotor)
+ motors++;
+ if (m_frontRightMotor)
+ motors++;
+ if (m_rearLeftMotor)
+ motors++;
+ if (m_rearRightMotor)
+ motors++;
+ return motors;
+ }
+ DISALLOW_COPY_AND_ASSIGN(RobotDrive);
+};
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/SPI.h b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/SPI.h
new file mode 100644
index 0000000..7c6d61f
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/SPI.h
@@ -0,0 +1,58 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+#pragma once
+
+#include "HAL/HAL.hpp"
+#include "SensorBase.h"
+
+class DigitalOutput;
+class DigitalInput;
+
+/**
+ * SPI bus interface class.
+ *
+ * This class is intended to be used by sensor (and other SPI device) drivers.
+ * It probably should not be used directly.
+ *
+ */
+class SPI : public SensorBase
+{
+public:
+ enum Port {kOnboardCS0, kOnboardCS1, kOnboardCS2, kOnboardCS3, kMXP};
+ SPI(Port SPIport);
+ virtual ~SPI();
+
+ void SetClockRate(double hz);
+
+ void SetMSBFirst();
+ void SetLSBFirst();
+
+ void SetSampleDataOnFalling();
+ void SetSampleDataOnRising();
+
+
+ void SetClockActiveLow();
+ void SetClockActiveHigh();
+
+ void SetChipSelectActiveHigh();
+ void SetChipSelectActiveLow();
+
+ virtual int32_t Write(uint8_t* data, uint8_t size);
+ virtual int32_t Read(bool initiate, uint8_t* dataReceived, uint8_t size);
+ virtual int32_t Transaction(uint8_t* dataToSend, uint8_t* dataReceived, uint8_t size);
+
+
+protected:
+ uint8_t m_port;
+ bool m_msbFirst;
+ bool m_sampleOnTrailing;
+ bool m_clk_idle_high;
+
+private:
+ void Init();
+
+ DISALLOW_COPY_AND_ASSIGN(SPI);
+};
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/SafePWM.h b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/SafePWM.h
new file mode 100644
index 0000000..bed5c21
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/SafePWM.h
@@ -0,0 +1,38 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+#pragma once
+
+#include "MotorSafety.h"
+#include "PWM.h"
+
+class MotorSafetyHelper;
+
+/**
+ * A safe version of the PWM class.
+ * It is safe because it implements the MotorSafety interface that provides timeouts
+ * in the event that the motor value is not updated before the expiration time.
+ * This delegates the actual work to a MotorSafetyHelper object that is used for all
+ * objects that implement MotorSafety.
+ */
+class SafePWM : public PWM, public MotorSafety
+{
+public:
+ explicit SafePWM(uint32_t channel);
+ ~SafePWM();
+
+ void SetExpiration(float timeout);
+ float GetExpiration();
+ bool IsAlive();
+ void StopMotor();
+ bool IsSafetyEnabled();
+ void SetSafetyEnabled(bool enabled);
+ void GetDescription(char *desc);
+
+ virtual void SetSpeed(float speed);
+private:
+ void InitSafePWM();
+ MotorSafetyHelper *m_safetyHelper;
+};
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/SampleRobot.h b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/SampleRobot.h
new file mode 100644
index 0000000..66842b6
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/SampleRobot.h
@@ -0,0 +1,25 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+#pragma once
+
+#include "RobotBase.h"
+
+class SampleRobot : public RobotBase
+{
+public:
+ SampleRobot();
+ virtual ~SampleRobot() {}
+ virtual void RobotInit();
+ virtual void Disabled();
+ virtual void Autonomous();
+ virtual void OperatorControl();
+ virtual void Test();
+ virtual void RobotMain();
+ void StartCompetition();
+
+private:
+ bool m_robotMainOverridden;
+};
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/Servo.h b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/Servo.h
new file mode 100644
index 0000000..25cfe1f
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/Servo.h
@@ -0,0 +1,48 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+#pragma once
+
+#include "SafePWM.h"
+#include "SpeedController.h"
+
+/**
+ * Standard hobby style servo.
+ *
+ * The range parameters default to the appropriate values for the Hitec HS-322HD servo provided
+ * in the FIRST Kit of Parts in 2008.
+ */
+class Servo : public SafePWM
+{
+public:
+ explicit Servo(uint32_t channel);
+ virtual ~Servo();
+ void Set(float value);
+ void SetOffline();
+ float Get();
+ void SetAngle(float angle);
+ float GetAngle();
+ static float GetMaxAngle()
+ {
+ return kMaxServoAngle;
+ }
+ static float GetMinAngle()
+ {
+ return kMinServoAngle;
+ }
+
+private:
+ void InitServo();
+ float GetServoAngleRange()
+ {
+ return kMaxServoAngle - kMinServoAngle;
+ }
+
+ static constexpr float kMaxServoAngle = 180.0;
+ static constexpr float kMinServoAngle = 0.0;
+
+ static constexpr float kDefaultMaxServoPWM = 2.4;
+ static constexpr float kDefaultMinServoPWM = .6;
+};
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/Solenoid.h b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/Solenoid.h
new file mode 100644
index 0000000..fe54ea2
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/Solenoid.h
@@ -0,0 +1,30 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+#pragma once
+
+#include "SolenoidBase.h"
+
+/**
+ * Solenoid class for running high voltage Digital Output (PCM).
+ *
+ * The Solenoid class is typically used for pneumatics solenoids, but could be used
+ * for any device within the current spec of the PCM.
+ */
+class Solenoid : public SolenoidBase
+{
+public:
+ explicit Solenoid(uint32_t channel);
+ Solenoid(uint8_t moduleNumber, uint32_t channel);
+ virtual ~Solenoid();
+ virtual void Set(bool on);
+ virtual bool Get();
+ bool IsBlackListed();
+
+private:
+ void InitSolenoid();
+
+ uint32_t m_channel; ///< The channel on the module to control.
+};
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/SolenoidBase.h b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/SolenoidBase.h
new file mode 100644
index 0000000..d70823f
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/SolenoidBase.h
@@ -0,0 +1,37 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+#pragma once
+
+#include "Resource.h"
+#include "SensorBase.h"
+#include "HAL/HAL.hpp"
+#include "HAL/cpp/Synchronized.hpp"
+
+/**
+ * SolenoidBase class is the common base class for the Solenoid and
+ * DoubleSolenoid classes.
+ */
+class SolenoidBase : public SensorBase
+{
+public:
+ virtual ~SolenoidBase();
+ uint8_t GetAll();
+
+ uint8_t GetPCMSolenoidBlackList();
+ bool GetPCMSolenoidVoltageStickyFault();
+ bool GetPCMSolenoidVoltageFault();
+ void ClearAllPCMStickyFaults();
+protected:
+ explicit SolenoidBase(uint8_t pcmID);
+ void Set(uint8_t value, uint8_t mask);
+ virtual void InitSolenoid() = 0;
+
+ uint32_t m_moduleNumber; ///< Slot number where the module is plugged into the chassis.
+ static Resource *m_allocated;
+
+private:
+ void* m_ports[kSolenoidChannels];
+};
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/SpeedController.h b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/SpeedController.h
new file mode 100644
index 0000000..96d9266
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/SpeedController.h
@@ -0,0 +1,35 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+#pragma once
+
+#include "HAL/HAL.hpp"
+#include "PIDOutput.h"
+
+/**
+ * Interface for speed controlling devices.
+ */
+class SpeedController : public PIDOutput
+{
+public:
+ virtual ~SpeedController() {}
+ /**
+ * Common interface for setting the speed of a speed controller.
+ *
+ * @param speed The speed to set. Value should be between -1.0 and 1.0.
+ * @param syncGroup The update group to add this Set() to, pending UpdateSyncGroup(). If 0, update immediately.
+ */
+ virtual void Set(float speed, uint8_t syncGroup = 0) = 0;
+ /**
+ * Common interface for getting the current set speed of a speed controller.
+ *
+ * @return The current set speed. Value is between -1.0 and 1.0.
+ */
+ virtual float Get() = 0;
+ /**
+ * Common interface for disabling a motor.
+ */
+ virtual void Disable() = 0;
+};
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/Talon.h b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/Talon.h
new file mode 100644
index 0000000..6e11ccd
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/Talon.h
@@ -0,0 +1,28 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+#pragma once
+
+#include "SafePWM.h"
+#include "SpeedController.h"
+#include "PIDOutput.h"
+
+/**
+ * Cross the Road Electronics (CTRE) Talon and Talon SR Speed Controller
+ */
+class Talon : public SafePWM, public SpeedController
+{
+public:
+ explicit Talon(uint32_t channel);
+ virtual ~Talon();
+ virtual void Set(float value, uint8_t syncGroup = 0);
+ virtual float Get();
+ virtual void Disable();
+
+ virtual void PIDWrite(float output);
+
+private:
+ void InitTalon();
+};
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/TalonSRX.h b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/TalonSRX.h
new file mode 100644
index 0000000..fe36b45
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/TalonSRX.h
@@ -0,0 +1,29 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+#pragma once
+
+#include "SafePWM.h"
+#include "SpeedController.h"
+#include "PIDOutput.h"
+
+/**
+ * Cross the Road Electronics (CTRE) Talon SRX Speed Controller with PWM control
+ * @see CANTalon for CAN control
+ */
+class TalonSRX : public SafePWM, public SpeedController
+{
+public:
+ explicit TalonSRX(uint32_t channel);
+ virtual ~TalonSRX();
+ virtual void Set(float value, uint8_t syncGroup = 0);
+ virtual float Get();
+ virtual void Disable();
+
+ virtual void PIDWrite(float output);
+
+private:
+ void InitTalonSRX();
+};
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/USBCamera.h b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/USBCamera.h
new file mode 100644
index 0000000..ac8bfe4
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/USBCamera.h
@@ -0,0 +1,111 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2014. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+#pragma once
+
+#include "ErrorBase.h"
+#include "nivision.h"
+#include "NIIMAQdx.h"
+
+#include <mutex>
+#include <string>
+
+typedef enum whiteBalance_enum {
+ kFixedIndoor = 3000,
+ kFixedOutdoor1 = 4000,
+ kFixedOutdoor2 = 5000,
+ kFixedFluorescent1 = 5100,
+ kFixedFlourescent2 = 5200
+} whiteBalance;
+
+class USBCamera : public ErrorBase {
+ private:
+ static constexpr char const *ATTR_WB_MODE = "CameraAttributes::WhiteBalance::Mode";
+ static constexpr char const *ATTR_WB_VALUE = "CameraAttributes::WhiteBalance::Value";
+ static constexpr char const *ATTR_EX_MODE = "CameraAttributes::Exposure::Mode";
+ static constexpr char const *ATTR_EX_VALUE = "CameraAttributes::Exposure::Value";
+ static constexpr char const *ATTR_BR_MODE = "CameraAttributes::Brightness::Mode";
+ static constexpr char const *ATTR_BR_VALUE = "CameraAttributes::Brightness::Value";
+
+ protected:
+ IMAQdxSession m_id;
+ std::string m_name;
+ bool m_useJpeg;
+ bool m_active;
+ bool m_open;
+
+ std::recursive_mutex m_mutex;
+
+ unsigned int m_width;
+ unsigned int m_height;
+ double m_fps;
+ std::string m_whiteBalance;
+ unsigned int m_whiteBalanceValue;
+ bool m_whiteBalanceValuePresent;
+ std::string m_exposure;
+ unsigned int m_exposureValue;
+ bool m_exposureValuePresent;
+ unsigned int m_brightness;
+ bool m_needSettingsUpdate;
+
+ unsigned int GetJpegSize(void* buffer, unsigned int buffSize);
+
+ public:
+ static constexpr char const *kDefaultCameraName = "cam0";
+
+ USBCamera(std::string name, bool useJpeg);
+
+ void OpenCamera();
+ void CloseCamera();
+ void StartCapture();
+ void StopCapture();
+ void SetFPS(double fps);
+ void SetSize(unsigned int width, unsigned int height);
+
+ void UpdateSettings();
+ /**
+ * Set the brightness, as a percentage (0-100).
+ */
+ void SetBrightness(unsigned int brightness);
+
+ /**
+ * Get the brightness, as a percentage (0-100).
+ */
+ unsigned int GetBrightness();
+
+ /**
+ * Set the white balance to auto
+ */
+ void SetWhiteBalanceAuto();
+
+ /**
+ * Set the white balance to hold current
+ */
+ void SetWhiteBalanceHoldCurrent();
+
+ /**
+ * Set the white balance to manual, with specified color temperature
+ */
+ void SetWhiteBalanceManual(unsigned int wbValue);
+
+ /**
+ * Set the exposure to auto exposure
+ */
+ void SetExposureAuto();
+
+ /**
+ * Set the exposure to hold current
+ */
+ void SetExposureHoldCurrent();
+
+ /**
+ * Set the exposure to manual, with a given percentage (0-100)
+ */
+ void SetExposureManual(unsigned int expValue);
+
+ void GetImage(Image* image);
+ unsigned int GetImageData(void* buffer, unsigned int bufferSize);
+};
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/Ultrasonic.h b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/Ultrasonic.h
new file mode 100644
index 0000000..e84e4ca
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/Ultrasonic.h
@@ -0,0 +1,80 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+#pragma once
+
+#include "SensorBase.h"
+#include "Task.h"
+#include "PIDSource.h"
+
+class Counter;
+class DigitalInput;
+class DigitalOutput;
+
+/**
+ * Ultrasonic rangefinder class.
+ * The Ultrasonic rangefinder measures absolute distance based on the round-trip time
+ * of a ping generated by the controller. These sensors use two transducers, a speaker and
+ * a microphone both tuned to the ultrasonic range. A common ultrasonic sensor, the Daventech SRF04
+ * requires a short pulse to be generated on a digital channel. This causes the chirp to be
+ * emmitted. A second line becomes high as the ping is transmitted and goes low when
+ * the echo is received. The time that the line is high determines the round trip distance
+ * (time of flight).
+ */
+class Ultrasonic : public SensorBase, public PIDSource
+{
+public:
+ enum DistanceUnit
+ {
+ kInches = 0,
+ kMilliMeters = 1
+ };
+
+ Ultrasonic(DigitalOutput *pingChannel, DigitalInput *echoChannel, DistanceUnit units = kInches);
+ Ultrasonic(DigitalOutput &pingChannel, DigitalInput &echoChannel, DistanceUnit units = kInches);
+ Ultrasonic(uint32_t pingChannel, uint32_t echoChannel, DistanceUnit units = kInches);
+ virtual ~Ultrasonic();
+
+ void Ping();
+ bool IsRangeValid();
+ static void SetAutomaticMode(bool enabling);
+ double GetRangeInches();
+ double GetRangeMM();
+ bool IsEnabled()
+ {
+ return m_enabled;
+ }
+ void SetEnabled(bool enable)
+ {
+ m_enabled = enable;
+ }
+
+ double PIDGet();
+ void SetDistanceUnits(DistanceUnit units);
+ DistanceUnit GetDistanceUnits();
+
+private:
+ void Initialize();
+
+ static void UltrasonicChecker();
+
+ static constexpr double kPingTime = 10 * 1e-6; ///< Time (sec) for the ping trigger pulse.
+ static const uint32_t kPriority = 90; ///< Priority that the ultrasonic round robin task runs.
+ static constexpr double kMaxUltrasonicTime = 0.1; ///< Max time (ms) between readings.
+ static constexpr double kSpeedOfSoundInchesPerSec = 1130.0 * 12.0;
+
+ static Task m_task; // task doing the round-robin automatic sensing
+ static Ultrasonic *m_firstSensor; // head of the ultrasonic sensor list
+ static bool m_automaticEnabled; // automatic round robin mode
+ static SEMAPHORE_ID m_semaphore; // synchronize access to the list of sensors
+
+ DigitalInput *m_echoChannel;
+ DigitalOutput *m_pingChannel;
+ bool m_allocatedChannels;
+ bool m_enabled;
+ Counter *m_counter;
+ Ultrasonic *m_nextSensor;
+ DistanceUnit m_units;
+};
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/Victor.h b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/Victor.h
new file mode 100644
index 0000000..9a83519
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/Victor.h
@@ -0,0 +1,31 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+#pragma once
+
+#include "SafePWM.h"
+#include "SpeedController.h"
+#include "PIDOutput.h"
+
+/**
+ * Vex Robotics Victor 888 Speed Controller
+ *
+ * The Vex Robotics Victor 884 Speed Controller can also be used with this
+ * class but may need to be calibrated per the Victor 884 user manual.
+ */
+class Victor : public SafePWM, public SpeedController
+{
+public:
+ explicit Victor(uint32_t channel);
+ virtual ~Victor();
+ virtual void Set(float value, uint8_t syncGroup = 0);
+ virtual float Get();
+ virtual void Disable();
+
+ virtual void PIDWrite(float output);
+
+private:
+ void InitVictor();
+};
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/VictorSP.h b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/VictorSP.h
new file mode 100644
index 0000000..e2ba4dc
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/VictorSP.h
@@ -0,0 +1,28 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+#pragma once
+
+#include "SafePWM.h"
+#include "SpeedController.h"
+#include "PIDOutput.h"
+
+/**
+ * Vex Robotics Victor SP Speed Controller
+ */
+class VictorSP : public SafePWM, public SpeedController
+{
+public:
+ explicit VictorSP(uint32_t channel);
+ virtual ~VictorSP();
+ virtual void Set(float value, uint8_t syncGroup = 0);
+ virtual float Get();
+ virtual void Disable();
+
+ virtual void PIDWrite(float output);
+
+private:
+ void InitVictorSP();
+};
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/Vision/AxisCamera.h b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/Vision/AxisCamera.h
new file mode 100644
index 0000000..9792093
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/Vision/AxisCamera.h
@@ -0,0 +1,127 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2014. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+
+#pragma once
+
+#include <thread>
+#include <string>
+#include <mutex>
+
+#include "ErrorBase.h"
+#include "Vision/ColorImage.h"
+#include "Vision/HSLImage.h"
+#include "nivision.h"
+
+/**
+ * Axis M1011 network camera
+ */
+class AxisCamera: public ErrorBase
+{
+public:
+ enum WhiteBalance
+ {
+ kWhiteBalance_Automatic,
+ kWhiteBalance_Hold,
+ kWhiteBalance_FixedOutdoor1,
+ kWhiteBalance_FixedOutdoor2,
+ kWhiteBalance_FixedIndoor,
+ kWhiteBalance_FixedFluorescent1,
+ kWhiteBalance_FixedFluorescent2
+ };
+
+ enum ExposureControl
+ {
+ kExposureControl_Automatic,
+ kExposureControl_Hold,
+ kExposureControl_FlickerFree50Hz,
+ kExposureControl_FlickerFree60Hz
+ };
+
+ enum Resolution
+ {
+ kResolution_640x480,
+ kResolution_480x360,
+ kResolution_320x240,
+ kResolution_240x180,
+ kResolution_176x144,
+ kResolution_160x120,
+ };
+
+ enum Rotation
+ {
+ kRotation_0, kRotation_180
+ };
+
+ explicit AxisCamera(std::string const& cameraHost);
+ virtual ~AxisCamera();
+
+ bool IsFreshImage() const;
+
+ int GetImage(Image *image);
+ int GetImage(ColorImage *image);
+ HSLImage *GetImage();
+ int CopyJPEG(char **destImage, unsigned int &destImageSize, unsigned int &destImageBufferSize);
+
+ void WriteBrightness(int brightness);
+ int GetBrightness();
+
+ void WriteWhiteBalance(WhiteBalance whiteBalance);
+ WhiteBalance GetWhiteBalance();
+
+ void WriteColorLevel(int colorLevel);
+ int GetColorLevel();
+
+ void WriteExposureControl(ExposureControl exposureControl);
+ ExposureControl GetExposureControl();
+
+ void WriteExposurePriority(int exposurePriority);
+ int GetExposurePriority();
+
+ void WriteMaxFPS(int maxFPS);
+ int GetMaxFPS();
+
+ void WriteResolution(Resolution resolution);
+ Resolution GetResolution();
+
+ void WriteCompression(int compression);
+ int GetCompression();
+
+ void WriteRotation(Rotation rotation);
+ Rotation GetRotation();
+
+private:
+ std::thread m_captureThread;
+ std::string m_cameraHost;
+ int m_cameraSocket;
+ std::mutex m_captureMutex;
+
+ std::mutex m_imageDataMutex;
+ std::vector<uint8_t> m_imageData;
+ bool m_freshImage;
+
+ int m_brightness;
+ WhiteBalance m_whiteBalance;
+ int m_colorLevel;
+ ExposureControl m_exposureControl;
+ int m_exposurePriority;
+ int m_maxFPS;
+ Resolution m_resolution;
+ int m_compression;
+ Rotation m_rotation;
+ bool m_parametersDirty;
+ bool m_streamDirty;
+ std::mutex m_parametersMutex;
+
+ bool m_done;
+
+ void Capture();
+ void ReadImagesFromCamera();
+ bool WriteParameters();
+
+ int CreateCameraSocket(std::string const& requestString, bool setError);
+
+ DISALLOW_COPY_AND_ASSIGN(AxisCamera);
+};
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/Vision/BaeUtilities.h b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/Vision/BaeUtilities.h
new file mode 100644
index 0000000..a684b07
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/Vision/BaeUtilities.h
@@ -0,0 +1,57 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2014. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+
+#pragma once
+
+/* Constants */
+#define LOG_DEBUG __FILE__,__FUNCTION__,__LINE__,DEBUG_TYPE
+#define LOG_INFO __FILE__,__FUNCTION__,__LINE__,INFO_TYPE
+#define LOG_ERROR __FILE__,__FUNCTION__,__LINE__,ERROR_TYPE
+#define LOG_CRITICAL __FILE__,__FUNCTION__,__LINE__,CRITICAL_TYPE
+#define LOG_FATAL __FILE__,__FUNCTION__,__LINE__,FATAL_TYPE
+#define LOG_DEBUG __FILE__,__FUNCTION__,__LINE__,DEBUG_TYPE
+
+/* Enumerated Types */
+
+/** debug levels */
+enum dprint_type {DEBUG_TYPE, INFO_TYPE, ERROR_TYPE, CRITICAL_TYPE, FATAL_TYPE};
+
+/** debug output setting */
+typedef enum DebugOutputType_enum {
+ DEBUG_OFF, DEBUG_MOSTLY_OFF, DEBUG_SCREEN_ONLY, DEBUG_FILE_ONLY, DEBUG_SCREEN_AND_FILE
+}DebugOutputType;
+
+/* Enumerated Types */
+
+/* Utility functions */
+
+/* debug */
+void SetDebugFlag ( DebugOutputType flag );
+void dprintf ( const char * tempString, ... ); /* Variable argument list */
+
+/* set FRC ranges for drive */
+double RangeToNormalized(double pixel, int range);
+/* change normalized value to any range - used for servo */
+float NormalizeToRange(float normalizedValue, float minRange, float maxRange);
+float NormalizeToRange(float normalizedValue);
+
+/* system utilities */
+void ShowActivity (char *fmt, ...);
+double ElapsedTime (double startTime);
+
+/* servo panning utilities */
+class Servo;
+double SinPosition (double *period, double sinStart);
+void panInit();
+void panInit(double period);
+void panForTarget(Servo *panServo);
+void panForTarget(Servo *panServo, double sinStart);
+
+/* config file read utilities */
+int processFile(char *inputFile, char *outputString, int lineNumber);
+int emptyString(char *string);
+void stripString(char *string);
+
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/Vision/BinaryImage.h b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/Vision/BinaryImage.h
new file mode 100644
index 0000000..7c33f54
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/Vision/BinaryImage.h
@@ -0,0 +1,39 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2014. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+
+#pragma once
+
+#include "MonoImage.h"
+/**
+ * Included for ParticleAnalysisReport definition
+ * TODO: Eliminate this dependency!
+ */
+#include "Vision/VisionAPI.h"
+
+#include <vector>
+#include <algorithm>
+
+class BinaryImage : public MonoImage
+{
+public:
+ BinaryImage();
+ virtual ~BinaryImage();
+ int GetNumberParticles();
+ ParticleAnalysisReport GetParticleAnalysisReport(int particleNumber);
+ void GetParticleAnalysisReport(int particleNumber, ParticleAnalysisReport *par);
+ std::vector<ParticleAnalysisReport>* GetOrderedParticleAnalysisReports();
+ BinaryImage *RemoveSmallObjects(bool connectivity8, int erosions);
+ BinaryImage *RemoveLargeObjects(bool connectivity8, int erosions);
+ BinaryImage *ConvexHull(bool connectivity8);
+ BinaryImage *ParticleFilter(ParticleFilterCriteria2 *criteria, int criteriaCount);
+ virtual void Write(const char *fileName);
+private:
+ bool ParticleMeasurement(int particleNumber, MeasurementType whatToMeasure, int *result);
+ bool ParticleMeasurement(int particleNumber, MeasurementType whatToMeasure, double *result);
+ static double NormalizeFromRange(double position, int range);
+ static bool CompareParticleSizes(ParticleAnalysisReport particle1, ParticleAnalysisReport particle2);
+};
+
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/Vision/ColorImage.h b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/Vision/ColorImage.h
new file mode 100644
index 0000000..595ed83
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/Vision/ColorImage.h
@@ -0,0 +1,65 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2014. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+
+#pragma once
+
+#include "ImageBase.h"
+#include "BinaryImage.h"
+#include "Threshold.h"
+
+class ColorImage : public ImageBase
+{
+public:
+ ColorImage(ImageType type);
+ virtual ~ColorImage();
+ BinaryImage *ThresholdRGB(int redLow, int redHigh, int greenLow, int greenHigh, int blueLow, int blueHigh);
+ BinaryImage *ThresholdHSL(int hueLow, int hueHigh, int saturationLow, int saturationHigh, int luminenceLow, int luminenceHigh);
+ BinaryImage *ThresholdHSV(int hueLow, int hueHigh, int saturationLow, int saturationHigh, int valueHigh, int valueLow);
+ BinaryImage *ThresholdHSI(int hueLow, int hueHigh, int saturationLow, int saturationHigh, int intensityLow, int intensityHigh);
+ BinaryImage *ThresholdRGB(Threshold &threshold);
+ BinaryImage *ThresholdHSL(Threshold &threshold);
+ BinaryImage *ThresholdHSV(Threshold &threshold);
+ BinaryImage *ThresholdHSI(Threshold &threshold);
+ MonoImage *GetRedPlane();
+ MonoImage *GetGreenPlane();
+ MonoImage *GetBluePlane();
+ MonoImage *GetHSLHuePlane();
+ MonoImage *GetHSVHuePlane();
+ MonoImage *GetHSIHuePlane();
+ MonoImage *GetHSLSaturationPlane();
+ MonoImage *GetHSVSaturationPlane();
+ MonoImage *GetHSISaturationPlane();
+ MonoImage *GetLuminancePlane();
+ MonoImage *GetValuePlane();
+ MonoImage *GetIntensityPlane();
+ void ReplaceRedPlane(MonoImage *plane);
+ void ReplaceGreenPlane(MonoImage *plane);
+ void ReplaceBluePlane(MonoImage *plane);
+ void ReplaceHSLHuePlane(MonoImage *plane);
+ void ReplaceHSVHuePlane(MonoImage *plane);
+ void ReplaceHSIHuePlane(MonoImage *plane);
+ void ReplaceHSLSaturationPlane(MonoImage *plane);
+ void ReplaceHSVSaturationPlane(MonoImage *plane);
+ void ReplaceHSISaturationPlane(MonoImage *plane);
+ void ReplaceLuminancePlane(MonoImage *plane);
+ void ReplaceValuePlane(MonoImage *plane);
+ void ReplaceIntensityPlane(MonoImage *plane);
+ void ColorEqualize();
+ void LuminanceEqualize();
+
+private:
+ BinaryImage *ComputeThreshold(ColorMode colorMode, int low1, int high1, int low2, int high2, int low3, int high3);
+ void Equalize(bool allPlanes);
+ MonoImage * ExtractColorPlane(ColorMode mode, int planeNumber);
+ MonoImage * ExtractFirstColorPlane(ColorMode mode);
+ MonoImage * ExtractSecondColorPlane(ColorMode mode);
+ MonoImage * ExtractThirdColorPlane(ColorMode mode);
+ void ReplacePlane(ColorMode mode, MonoImage *plane, int planeNumber);
+ void ReplaceFirstColorPlane(ColorMode mode, MonoImage *plane);
+ void ReplaceSecondColorPlane(ColorMode mode, MonoImage *plane);
+ void ReplaceThirdColorPlane(ColorMode mode, MonoImage *plane);
+};
+
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/Vision/FrcError.h b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/Vision/FrcError.h
new file mode 100644
index 0000000..9bd47f4
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/Vision/FrcError.h
@@ -0,0 +1,30 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2014. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+
+#pragma once
+
+/* Error Codes */
+#define ERR_VISION_GENERAL_ERROR 166000 //
+#define ERR_COLOR_NOT_FOUND 166100 // TrackAPI.cpp
+#define ERR_PARTICLE_TOO_SMALL 166101 // TrackAPI.cpp
+
+#define ERR_CAMERA_FAILURE 166200 // AxisCamera.cpp
+#define ERR_CAMERA_SOCKET_CREATE_FAILED 166201 // AxisCamera.cpp
+#define ERR_CAMERA_CONNECT_FAILED 166202 // AxisCamera.cpp
+#define ERR_CAMERA_STALE_IMAGE 166203 // AxisCamera.cpp
+#define ERR_CAMERA_NOT_INITIALIZED 166204 // AxisCamera.cpp
+#define ERR_CAMERA_NO_BUFFER_AVAILABLE 166205 // AxisCamera.cpp
+#define ERR_CAMERA_HEADER_ERROR 166206 // AxisCamera.cpp
+#define ERR_CAMERA_BLOCKING_TIMEOUT 166207 // AxisCamera.cpp
+#define ERR_CAMERA_AUTHORIZATION_FAILED 166208 // AxisCamera.cpp
+#define ERR_CAMERA_TASK_SPAWN_FAILED 166209 // AxisCamera.cpp
+#define ERR_CAMERA_TASK_INPUT_OUT_OF_RANGE 166210 // AxisCamera.cpp
+#define ERR_CAMERA_COMMAND_FAILURE 166211 // AxisCamera.cpp
+
+/* error handling functions */
+int GetLastVisionError();
+const char* GetVisionErrorText(int errorCode);
+
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/Vision/HSLImage.h b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/Vision/HSLImage.h
new file mode 100644
index 0000000..71428aa
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/Vision/HSLImage.h
@@ -0,0 +1,22 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2014. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+
+#pragma once
+
+#include "ColorImage.h"
+
+/**
+ * A color image represented in HSL color space at 3 bytes per pixel.
+ */
+class HSLImage : public ColorImage
+{
+public:
+ HSLImage();
+ HSLImage(const char *fileName);
+ virtual ~HSLImage();
+};
+
+
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/Vision/ImageBase.h b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/Vision/ImageBase.h
new file mode 100644
index 0000000..ee5e895
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/Vision/ImageBase.h
@@ -0,0 +1,27 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2014. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+
+#pragma once
+
+#include <stdio.h>
+#include "nivision.h"
+#include "ErrorBase.h"
+
+#define DEFAULT_BORDER_SIZE 3
+
+class ImageBase : public ErrorBase
+{
+public:
+ ImageBase(ImageType type);
+ virtual ~ImageBase();
+ virtual void Write(const char *fileName);
+ int GetHeight();
+ int GetWidth();
+ Image *GetImaqImage();
+protected:
+ Image *m_imaqImage;
+};
+
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/Vision/MonoImage.h b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/Vision/MonoImage.h
new file mode 100644
index 0000000..e0e35eb
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/Vision/MonoImage.h
@@ -0,0 +1,25 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2014. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+
+#pragma once
+
+#include "ImageBase.h"
+
+#include <vector>
+
+class MonoImage : public ImageBase
+{
+public:
+ MonoImage();
+ virtual ~MonoImage();
+
+ std::vector<EllipseMatch> *DetectEllipses(EllipseDescriptor *ellipseDescriptor,
+ CurveOptions *curveOptions,
+ ShapeDetectionOptions *shapeDetectionOptions,
+ ROI *roi);
+ std::vector<EllipseMatch> * DetectEllipses(EllipseDescriptor *ellipseDescriptor);
+};
+
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/Vision/RGBImage.h b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/Vision/RGBImage.h
new file mode 100644
index 0000000..0ef2d72
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/Vision/RGBImage.h
@@ -0,0 +1,21 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2014. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+
+#pragma once
+
+#include "ColorImage.h"
+
+/**
+ * A color image represented in RGB color space at 3 bytes per pixel.
+ */
+class RGBImage : public ColorImage
+{
+public:
+ RGBImage();
+ RGBImage(const char *fileName);
+ virtual ~RGBImage();
+};
+
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/Vision/Threshold.h b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/Vision/Threshold.h
new file mode 100644
index 0000000..9070cbf
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/Vision/Threshold.h
@@ -0,0 +1,28 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2014. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+
+#pragma once
+
+/**
+ * Color threshold values.
+ * This object represnts the threshold values for any type of color object
+ * that is used in a threshhold operation. It simplifies passing values
+ * around in a program for color detection.
+ */
+class Threshold
+{
+public:
+ int plane1Low;
+ int plane1High;
+ int plane2Low;
+ int plane2High;
+ int plane3Low;
+ int plane3High;
+ Threshold(int plane1Low, int plane1High,
+ int plane2Low, int plane2High,
+ int plane3Low, int plane3High);
+};
+
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/Vision/VisionAPI.h b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/Vision/VisionAPI.h
new file mode 100644
index 0000000..ce291bf
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/Vision/VisionAPI.h
@@ -0,0 +1,148 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2014. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+
+#pragma once
+
+#include "nivision.h"
+
+/* Constants */
+
+#define DEFAULT_BORDER_SIZE 3 //VisionAPI.frcCreateImage
+#define DEFAULT_SATURATION_THRESHOLD 40 //TrackAPI.FindColor
+
+/* Forward Declare Data Structures */
+typedef struct FindEdgeOptions_struct FindEdgeOptions;
+typedef struct CircularEdgeReport_struct CircularEdgeReport;
+
+/* Data Structures */
+
+/** frcParticleAnalysis returns this structure */
+typedef struct ParticleAnalysisReport_struct {
+ int imageHeight;
+ int imageWidth;
+ double imageTimestamp;
+ int particleIndex; // the particle index analyzed
+ /* X-coordinate of the point representing the average position of the
+ * total particle mass, assuming every point in the particle has a constant density */
+ int center_mass_x; // MeasurementType: IMAQ_MT_CENTER_OF_MASS_X
+ /* Y-coordinate of the point representing the average position of the
+ * total particle mass, assuming every point in the particle has a constant density */
+ int center_mass_y; // MeasurementType: IMAQ_MT_CENTER_OF_MASS_Y
+ double center_mass_x_normalized; //Center of mass x value normalized to -1.0 to +1.0 range
+ double center_mass_y_normalized; //Center of mass y value normalized to -1.0 to +1.0 range
+ /* Area of the particle */
+ double particleArea; // MeasurementType: IMAQ_MT_AREA
+ /* Bounding Rectangle */
+ Rect boundingRect; // left/top/width/height
+ /* Percentage of the particle Area covering the Image Area. */
+ double particleToImagePercent; // MeasurementType: IMAQ_MT_AREA_BY_IMAGE_AREA
+ /* Percentage of the particle Area in relation to its Particle and Holes Area */
+ double particleQuality; // MeasurementType: IMAQ_MT_AREA_BY_PARTICLE_AND_HOLES_AREA
+} ParticleAnalysisReport;
+
+/** Tracking functions return this structure */
+typedef struct ColorReport_struct {
+ int numberParticlesFound; // Number of particles found for this color
+ int largestParticleNumber; // The particle index of the largest particle
+ /* Color information */
+ float particleHueMax; // HistogramReport: hue max
+ float particleHueMin; // HistogramReport: hue max
+ float particleHueMean; // HistogramReport: hue mean
+ float particleSatMax; // HistogramReport: saturation max
+ float particleSatMin; // HistogramReport: saturation max
+ float particleSatMean; // HistogramReport: saturation mean
+ float particleLumMax; // HistogramReport: luminance max
+ float particleLumMin; // HistogramReport: luminance max
+ float particleLumMean; // HistogramReport: luminance mean
+} ColorReport;
+
+
+/* Image Management functions */
+
+/* Create: calls imaqCreateImage. Border size is set to some default value */
+Image* frcCreateImage( ImageType type );
+
+/* Dispose: calls imaqDispose */
+int frcDispose( void* object );
+int frcDispose( const char* filename, ... ) ;
+
+/* Copy: calls imaqDuplicateImage */
+int frcCopyImage( Image* dest, const Image* source );
+
+/* Image Extraction: Crop: calls imaqScale */
+int frcCrop( Image* dest, const Image* source, Rect rect );
+
+/* Image Extraction: Scale: calls imaqScale. Scales entire image */
+int frcScale(Image* dest, const Image* source, int xScale, int yScale, ScalingMode scaleMode );
+
+/* Read Image : calls imaqReadFile */
+int frcReadImage( Image* image, const char* fileName );
+/* Write Image : calls imaqWriteFile */
+int frcWriteImage( const Image* image, const char* fileName);
+
+/* Measure Intensity functions */
+
+/* Histogram: calls imaqHistogram */
+HistogramReport* frcHistogram( const Image* image, int numClasses, float min, float max, Rect rect );
+/* Color Histogram: calls imaqColorHistogram2 */
+ColorHistogramReport* frcColorHistogram(const Image* image, int numClasses, ColorMode mode, Image* mask);
+
+/* Get Pixel Value: calls imaqGetPixel */
+int frcGetPixelValue( const Image* image, Point pixel, PixelValue* value );
+
+/* Particle Analysis functions */
+
+/* Particle Filter: calls imaqParticleFilter3 */
+int frcParticleFilter(Image* dest, Image* source, const ParticleFilterCriteria2* criteria,
+ int criteriaCount, const ParticleFilterOptions* options, Rect rect, int* numParticles);
+int frcParticleFilter(Image* dest, Image* source, const ParticleFilterCriteria2* criteria,
+ int criteriaCount, const ParticleFilterOptions* options, int* numParticles);
+/* Morphology: calls imaqMorphology */
+int frcMorphology(Image* dest, Image* source, MorphologyMethod method);
+int frcMorphology(Image* dest, Image* source, MorphologyMethod method, const StructuringElement* structuringElement);
+/* Reject Border: calls imaqRejectBorder */
+int frcRejectBorder(Image* dest, Image* source);
+int frcRejectBorder(Image* dest, Image* source, int connectivity8);
+/* Count Particles: calls imaqCountParticles */
+int frcCountParticles(Image* image, int* numParticles);
+/* Particle Analysis Report: calls imaqMeasureParticle */
+int frcParticleAnalysis(Image* image, int particleNumber, ParticleAnalysisReport* par);
+
+/* Image Enhancement functions */
+
+/* Equalize: calls imaqEqualize */
+int frcEqualize(Image* dest, const Image* source, float min, float max);
+int frcEqualize(Image* dest, const Image* source, float min, float max, const Image* mask);
+
+/* Color Equalize: calls imaqColorEqualize */
+int frcColorEqualize(Image* dest, const Image* source);
+int frcColorEqualize(Image* dest, const Image* source, int colorEqualization);
+
+/* Image Thresholding & Conversion functions */
+
+/* Smart Threshold: calls imaqLocalThreshold */
+int frcSmartThreshold(Image* dest, const Image* source, unsigned int windowWidth, unsigned int windowHeight,
+ LocalThresholdMethod method, double deviationWeight, ObjectType type);
+int frcSmartThreshold(Image* dest, const Image* source, unsigned int windowWidth, unsigned int windowHeight,
+ LocalThresholdMethod method, double deviationWeight, ObjectType type, float replaceValue);
+
+/* Simple Threshold: calls imaqThreshold */
+int frcSimpleThreshold(Image* dest, const Image* source, float rangeMin, float rangeMax, float newValue);
+int frcSimpleThreshold(Image* dest, const Image* source, float rangeMin, float rangeMax);
+
+/* Color/Hue Threshold: calls imaqColorThreshold */
+int frcColorThreshold(Image* dest, const Image* source, ColorMode mode,
+ const Range* plane1Range, const Range* plane2Range, const Range* plane3Range);
+int frcColorThreshold(Image* dest, const Image* source, int replaceValue, ColorMode mode,
+ const Range* plane1Range, const Range* plane2Range, const Range* plane3Range);
+int frcHueThreshold(Image* dest, const Image* source, const Range* hueRange);
+int frcHueThreshold(Image* dest, const Image* source, const Range* hueRange, int minSaturation);
+
+/* Extract ColorHue Plane: calls imaqExtractColorPlanes */
+int frcExtractColorPlanes(const Image* image, ColorMode mode, Image* plane1, Image* plane2, Image* plane3);
+int frcExtractHuePlane(const Image* image, Image* huePlane);
+int frcExtractHuePlane(const Image* image, Image* huePlane, int minSaturation);
+
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/WPILib.h b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/WPILib.h
new file mode 100644
index 0000000..5af18c9
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/WPILib.h
@@ -0,0 +1,77 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+#pragma once
+
+#define REAL
+
+#include "string.h"
+#include <iostream>
+
+#include "ADXL345_I2C.h"
+#include "ADXL345_SPI.h"
+#include "AnalogAccelerometer.h"
+#include "AnalogInput.h"
+#include "AnalogOutput.h"
+#include "AnalogPotentiometer.h"
+#include "AnalogTrigger.h"
+#include "AnalogTriggerOutput.h"
+#include "BuiltInAccelerometer.h"
+#include "CANJaguar.h"
+#include "CANTalon.h"
+#include "Compressor.h"
+#include "ControllerPower.h"
+#include "Counter.h"
+#include "DigitalInput.h"
+#include "DigitalOutput.h"
+#include "DigitalSource.h"
+#include "DoubleSolenoid.h"
+#include "DriverStation.h"
+#include "Encoder.h"
+#include "ErrorBase.h"
+#include "GearTooth.h"
+#include "GenericHID.h"
+#include "Gyro.h"
+#include "interfaces/Accelerometer.h"
+#include "interfaces/Potentiometer.h"
+#include "I2C.h"
+#include "IterativeRobot.h"
+#include "InterruptableSensorBase.h"
+#include "Jaguar.h"
+#include "Joystick.h"
+#include "Notifier.h"
+#include "PIDOutput.h"
+#include "PIDSource.h"
+#include "PowerDistributionPanel.h"
+#include "PWM.h"
+#include "Relay.h"
+#include "Resource.h"
+#include "RobotBase.h"
+#include "RobotDrive.h"
+#include "SensorBase.h"
+#include "Servo.h"
+#include "SampleRobot.h"
+#include "Solenoid.h"
+#include "SpeedController.h"
+#include "SPI.h"
+#include "HAL/cpp/Synchronized.hpp"
+#include "Talon.h"
+#include "TalonSRX.h"
+#include "Task.h"
+#include "Timer.h"
+#include "Ultrasonic.h"
+#include "Utility.h"
+#include "Victor.h"
+#include "VictorSP.h"
+#include "Vision/AxisCamera.h"
+#include "Vision/BinaryImage.h"
+#include "Vision/ColorImage.h"
+#include "Vision/HSLImage.h"
+#include "Vision/ImageBase.h"
+#include "Vision/MonoImage.h"
+#include "Vision/RGBImage.h"
+#include "Vision/Threshold.h"
+// XXX: #include "Vision/AxisCamera.h"
+#include "WPIErrors.h"
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/ctre/CanTalonSRX.h b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/ctre/CanTalonSRX.h
new file mode 100644
index 0000000..37bd5e4
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/ctre/CanTalonSRX.h
@@ -0,0 +1,374 @@
+/**
+ * @brief CAN TALON SRX driver.
+ *
+ * The TALON SRX is designed to instrument all runtime signals periodically. The default periods are chosen to support 16 TALONs
+ * with 10ms update rate for control (throttle or setpoint). However these can be overridden with SetStatusFrameRate. @see SetStatusFrameRate
+ * The getters for these unsolicited signals are auto generated at the bottom of this module.
+ *
+ * Likewise most control signals are sent periodically using the fire-and-forget CAN API.
+ * The setters for these unsolicited signals are auto generated at the bottom of this module.
+ *
+ * Signals that are not available in an unsolicited fashion are the Close Loop gains.
+ * For teams that have a single profile for their TALON close loop they can use either the webpage to configure their TALONs once
+ * or set the PIDF,Izone,CloseLoopRampRate,etc... once in the robot application. These parameters are saved to flash so once they are
+ * loaded in the TALON, they will persist through power cycles and mode changes.
+ *
+ * For teams that have one or two profiles to switch between, they can use the same strategy since there are two slots to choose from
+ * and the ProfileSlotSelect is periodically sent in the 10 ms control frame.
+ *
+ * For teams that require changing gains frequently, they can use the soliciting API to get and set those parameters. Most likely
+ * they will only need to set them in a periodic fashion as a function of what motion the application is attempting.
+ * If this API is used, be mindful of the CAN utilization reported in the driver station.
+ *
+ * Encoder position is measured in encoder edges. Every edge is counted (similar to roboRIO 4X mode).
+ * Analog position is 10 bits, meaning 1024 ticks per rotation (0V => 3.3V).
+ * Use SetFeedbackDeviceSelect to select which sensor type you need. Once you do that you can use GetSensorPosition()
+ * and GetSensorVelocity(). These signals are updated on CANBus every 20ms (by default).
+ * If a relative sensor is selected, you can zero (or change the current value) using SetSensorPosition.
+ *
+ * Analog Input and quadrature position (and velocity) are also explicitly reported in GetEncPosition, GetEncVel, GetAnalogInWithOv, GetAnalogInVel.
+ * These signals are available all the time, regardless of what sensor is selected at a rate of 100ms. This allows easy instrumentation
+ * for "in the pits" checking of all sensors regardless of modeselect. The 100ms rate is overridable for teams who want to acquire sensor
+ * data for processing, not just instrumentation. Or just select the sensor using SetFeedbackDeviceSelect to get it at 20ms.
+ *
+ * Velocity is in position ticks / 100ms.
+ *
+ * All output units are in respect to duty cycle (throttle) which is -1023(full reverse) to +1023 (full forward).
+ * This includes demand (which specifies duty cycle when in duty cycle mode) and rampRamp, which is in throttle units per 10ms (if nonzero).
+ *
+ * Pos and velocity close loops are calc'd as
+ * err = target - posOrVel.
+ * iErr += err;
+ * if( (IZone!=0) and abs(err) > IZone)
+ * ClearIaccum()
+ * output = P X err + I X iErr + D X dErr + F X target
+ * dErr = err - lastErr
+ * P, I,and D gains are always positive. F can be negative.
+ * Motor direction can be reversed using SetRevMotDuringCloseLoopEn if sensor and motor are out of phase.
+ * Similarly feedback sensor can also be reversed (multiplied by -1) if you prefer the sensor to be inverted.
+ *
+ * P gain is specified in throttle per error tick. For example, a value of 102 is ~9.9% (which is 102/1023) throttle per 1
+ * ADC unit(10bit) or 1 quadrature encoder edge depending on selected sensor.
+ *
+ * I gain is specified in throttle per integrated error. For example, a value of 10 equates to ~0.99% (which is 10/1023)
+ * for each accumulated ADC unit(10bit) or 1 quadrature encoder edge depending on selected sensor.
+ * Close loop and integral accumulator runs every 1ms.
+ *
+ * D gain is specified in throttle per derivative error. For example a value of 102 equates to ~9.9% (which is 102/1023)
+ * per change of 1 unit (ADC or encoder) per ms.
+ *
+ * I Zone is specified in the same units as sensor position (ADC units or quadrature edges). If pos/vel error is outside of
+ * this value, the integrated error will auto-clear...
+ * if( (IZone!=0) and abs(err) > IZone)
+ * ClearIaccum()
+ * ...this is very useful in preventing integral windup and is highly recommended if using full PID to keep stability low.
+ *
+ * CloseLoopRampRate is in throttle units per 1ms. Set to zero to disable ramping.
+ * Works the same as RampThrottle but only is in effect when a close loop mode and profile slot is selected.
+ *
+ * auto generated using spreadsheet and WpiClassGen.csproj
+ * @link https://docs.google.com/spreadsheets/d/1OU_ZV7fZLGYUQ-Uhc8sVAmUmWTlT8XBFYK8lfjg_tac/edit#gid=1766046967
+ */
+#ifndef CanTalonSRX_H_
+#define CanTalonSRX_H_
+#include "ctre.h" //BIT Defines + Typedefs
+#include "CtreCanNode.h"
+#include <NetworkCommunication/CANSessionMux.h> //CAN Comm
+#include <map>
+class CanTalonSRX : public CtreCanNode
+{
+private:
+
+ /** just in case user wants to modify periods of certain status frames.
+ * Default the vars to match the firmware default. */
+ uint32_t _statusRateMs[4];
+ //---------------------- Vars for opening a CAN stream if caller needs signals that require soliciting */
+ uint32_t _can_h; //!< Session handle for catching response params.
+ int32_t _can_stat; //!< Session handle status.
+ struct tCANStreamMessage _msgBuff[20];
+ static int const kMsgCapacity = 20;
+ typedef std::map<uint32_t, uint32_t> sigs_t;
+ sigs_t _sigs; //!< Catches signal updates that are solicited. Expect this to be very few.
+ void OpenSessionIfNeedBe();
+ void ProcessStreamMessages();
+ /**
+ * Send a one shot frame to set an arbitrary signal.
+ * Most signals are in the control frame so avoid using this API unless you have to.
+ * Use this api for...
+ * -A motor controller profile signal eProfileParam_XXXs. These are backed up in flash. If you are gain-scheduling then call this periodically.
+ * -Default brake and limit switch signals... eOnBoot_XXXs. Avoid doing this, use the override signals in the control frame.
+ * Talon will automatically send a PARAM_RESPONSE after the set, so GetParamResponse will catch the latest value after a couple ms.
+ */
+ CTR_Code SetParamRaw(uint32_t paramEnum, int32_t rawBits);
+ /**
+ * Checks cached CAN frames and updating solicited signals.
+ */
+ CTR_Code GetParamResponseRaw(uint32_t paramEnum, int32_t & rawBits);
+public:
+ static const int kDefaultControlPeriodMs = 10; //!< default control update rate is 10ms.
+ CanTalonSRX(int deviceNumber = 0,int controlPeriodMs = kDefaultControlPeriodMs);
+ ~CanTalonSRX();
+ void Set(double value);
+ /* mode select enumerations */
+ static const int kMode_DutyCycle = 0; //!< Demand is 11bit signed duty cycle [-1023,1023].
+ static const int kMode_PositionCloseLoop = 1; //!< Position PIDF.
+ static const int kMode_VelocityCloseLoop = 2; //!< Velocity PIDF.
+ static const int kMode_CurrentCloseLoop = 3; //!< Current close loop - not done.
+ static const int kMode_VoltCompen = 4; //!< Voltage Compensation Mode - not done. Demand is fixed pt target 8.8 volts.
+ static const int kMode_SlaveFollower = 5; //!< Demand is the 6 bit Device ID of the 'master' TALON SRX.
+ static const int kMode_NoDrive = 15; //!< Zero the output (honors brake/coast) regardless of demand. Might be useful if we need to change modes but can't atomically change all the signals we want in between.
+ /* limit switch enumerations */
+ static const int kLimitSwitchOverride_UseDefaultsFromFlash = 1;
+ static const int kLimitSwitchOverride_DisableFwd_DisableRev = 4;
+ static const int kLimitSwitchOverride_DisableFwd_EnableRev = 5;
+ static const int kLimitSwitchOverride_EnableFwd_DisableRev = 6;
+ static const int kLimitSwitchOverride_EnableFwd_EnableRev = 7;
+ /* brake override enumerations */
+ static const int kBrakeOverride_UseDefaultsFromFlash = 0;
+ static const int kBrakeOverride_OverrideCoast = 1;
+ static const int kBrakeOverride_OverrideBrake = 2;
+ /* feedback device enumerations */
+ static const int kFeedbackDev_DigitalQuadEnc=0;
+ static const int kFeedbackDev_AnalogPot=2;
+ static const int kFeedbackDev_AnalogEncoder=3;
+ static const int kFeedbackDev_CountEveryRisingEdge=4;
+ static const int kFeedbackDev_CountEveryFallingEdge=5;
+ static const int kFeedbackDev_PosIsPulseWidth=8;
+ /* ProfileSlotSelect enumerations*/
+ static const int kProfileSlotSelect_Slot0 = 0;
+ static const int kProfileSlotSelect_Slot1 = 1;
+ /* status frame rate types */
+ static const int kStatusFrame_General = 0;
+ static const int kStatusFrame_Feedback = 1;
+ static const int kStatusFrame_Encoder = 2;
+ static const int kStatusFrame_AnalogTempVbat = 3;
+ /**
+ * Signal enumeration for generic signal access.
+ * Although every signal is enumerated, only use this for traffic that must be solicited.
+ * Use the auto generated getters/setters at bottom of this header as much as possible.
+ */
+ typedef enum _param_t{
+ eProfileParamSlot0_P=1,
+ eProfileParamSlot0_I=2,
+ eProfileParamSlot0_D=3,
+ eProfileParamSlot0_F=4,
+ eProfileParamSlot0_IZone=5,
+ eProfileParamSlot0_CloseLoopRampRate=6,
+ eProfileParamSlot1_P=11,
+ eProfileParamSlot1_I=12,
+ eProfileParamSlot1_D=13,
+ eProfileParamSlot1_F=14,
+ eProfileParamSlot1_IZone=15,
+ eProfileParamSlot1_CloseLoopRampRate=16,
+ eProfileParamSoftLimitForThreshold=21,
+ eProfileParamSoftLimitRevThreshold=22,
+ eProfileParamSoftLimitForEnable=23,
+ eProfileParamSoftLimitRevEnable=24,
+ eOnBoot_BrakeMode=31,
+ eOnBoot_LimitSwitch_Forward_NormallyClosed=32,
+ eOnBoot_LimitSwitch_Reverse_NormallyClosed=33,
+ eOnBoot_LimitSwitch_Forward_Disable=34,
+ eOnBoot_LimitSwitch_Reverse_Disable=35,
+ eFault_OverTemp=41,
+ eFault_UnderVoltage=42,
+ eFault_ForLim=43,
+ eFault_RevLim=44,
+ eFault_HardwareFailure=45,
+ eFault_ForSoftLim=46,
+ eFault_RevSoftLim=47,
+ eStckyFault_OverTemp=48,
+ eStckyFault_UnderVoltage=49,
+ eStckyFault_ForLim=50,
+ eStckyFault_RevLim=51,
+ eStckyFault_ForSoftLim=52,
+ eStckyFault_RevSoftLim=53,
+ eAppliedThrottle=61,
+ eCloseLoopErr=62,
+ eFeedbackDeviceSelect=63,
+ eRevMotDuringCloseLoopEn=64,
+ eModeSelect=65,
+ eProfileSlotSelect=66,
+ eRampThrottle=67,
+ eRevFeedbackSensor=68,
+ eLimitSwitchEn=69,
+ eLimitSwitchClosedFor=70,
+ eLimitSwitchClosedRev=71,
+ eSensorPosition=73,
+ eSensorVelocity=74,
+ eCurrent=75,
+ eBrakeIsEnabled=76,
+ eEncPosition=77,
+ eEncVel=78,
+ eEncIndexRiseEvents=79,
+ eQuadApin=80,
+ eQuadBpin=81,
+ eQuadIdxpin=82,
+ eAnalogInWithOv=83,
+ eAnalogInVel=84,
+ eTemp=85,
+ eBatteryV=86,
+ eResetCount=87,
+ eResetFlags=88,
+ eFirmVers=89,
+ eSettingsChanged=90,
+ eQuadFilterEn=91,
+ ePidIaccum=93,
+ }param_t;
+ /*---------------------setters and getters that use the solicated param request/response-------------*//**
+ * Send a one shot frame to set an arbitrary signal.
+ * Most signals are in the control frame so avoid using this API unless you have to.
+ * Use this api for...
+ * -A motor controller profile signal eProfileParam_XXXs. These are backed up in flash. If you are gain-scheduling then call this periodically.
+ * -Default brake and limit switch signals... eOnBoot_XXXs. Avoid doing this, use the override signals in the control frame.
+ * Talon will automatically send a PARAM_RESPONSE after the set, so GetParamResponse will catch the latest value after a couple ms.
+ */
+ CTR_Code SetParam(param_t paramEnum, double value);
+ /**
+ * Asks TALON to immedietely respond with signal value. This API is only used for signals that are not sent periodically.
+ * This can be useful for reading params that rarely change like Limit Switch settings and PIDF values.
+ * @param param to request.
+ */
+ CTR_Code RequestParam(param_t paramEnum);
+ CTR_Code GetParamResponse(param_t paramEnum, double & value);
+ CTR_Code GetParamResponseInt32(param_t paramEnum, int & value);
+ /*----- getters and setters that use param request/response. These signals are backed up in flash and will survive a power cycle. ---------*/
+ /*----- If your application requires changing these values consider using both slots and switch between slot0 <=> slot1. ------------------*/
+ /*----- If your application requires changing these signals frequently then it makes sense to leverage this API. --------------------------*/
+ /*----- Getters don't block, so it may require several calls to get the latest value. --------------------------*/
+ CTR_Code SetPgain(unsigned slotIdx,double gain);
+ CTR_Code SetIgain(unsigned slotIdx,double gain);
+ CTR_Code SetDgain(unsigned slotIdx,double gain);
+ CTR_Code SetFgain(unsigned slotIdx,double gain);
+ CTR_Code SetIzone(unsigned slotIdx,int zone);
+ CTR_Code SetCloseLoopRampRate(unsigned slotIdx,int closeLoopRampRate);
+ CTR_Code SetSensorPosition(int pos);
+ CTR_Code SetForwardSoftLimit(int forwardLimit);
+ CTR_Code SetReverseSoftLimit(int reverseLimit);
+ CTR_Code SetForwardSoftEnable(int enable);
+ CTR_Code SetReverseSoftEnable(int enable);
+ CTR_Code GetPgain(unsigned slotIdx,double & gain);
+ CTR_Code GetIgain(unsigned slotIdx,double & gain);
+ CTR_Code GetDgain(unsigned slotIdx,double & gain);
+ CTR_Code GetFgain(unsigned slotIdx,double & gain);
+ CTR_Code GetIzone(unsigned slotIdx,int & zone);
+ CTR_Code GetCloseLoopRampRate(unsigned slotIdx,int & closeLoopRampRate);
+ CTR_Code GetForwardSoftLimit(int & forwardLimit);
+ CTR_Code GetReverseSoftLimit(int & reverseLimit);
+ CTR_Code GetForwardSoftEnable(int & enable);
+ CTR_Code GetReverseSoftEnable(int & enable);
+ /**
+ * Change the periodMs of a TALON's status frame. See kStatusFrame_* enums for what's available.
+ */
+ CTR_Code SetStatusFrameRate(unsigned frameEnum, unsigned periodMs);
+ /**
+ * Clear all sticky faults in TALON.
+ */
+ CTR_Code ClearStickyFaults();
+ /*------------------------ auto generated. This API is optimal since it uses the fire-and-forget CAN interface ----------------------*/
+ /*------------------------ These signals should cover the majority of all use cases. ----------------------------------*/
+ CTR_Code GetFault_OverTemp(int ¶m);
+ CTR_Code GetFault_UnderVoltage(int ¶m);
+ CTR_Code GetFault_ForLim(int ¶m);
+ CTR_Code GetFault_RevLim(int ¶m);
+ CTR_Code GetFault_HardwareFailure(int ¶m);
+ CTR_Code GetFault_ForSoftLim(int ¶m);
+ CTR_Code GetFault_RevSoftLim(int ¶m);
+ CTR_Code GetStckyFault_OverTemp(int ¶m);
+ CTR_Code GetStckyFault_UnderVoltage(int ¶m);
+ CTR_Code GetStckyFault_ForLim(int ¶m);
+ CTR_Code GetStckyFault_RevLim(int ¶m);
+ CTR_Code GetStckyFault_ForSoftLim(int ¶m);
+ CTR_Code GetStckyFault_RevSoftLim(int ¶m);
+ CTR_Code GetAppliedThrottle(int ¶m);
+ CTR_Code GetCloseLoopErr(int ¶m);
+ CTR_Code GetFeedbackDeviceSelect(int ¶m);
+ CTR_Code GetModeSelect(int ¶m);
+ CTR_Code GetLimitSwitchEn(int ¶m);
+ CTR_Code GetLimitSwitchClosedFor(int ¶m);
+ CTR_Code GetLimitSwitchClosedRev(int ¶m);
+ CTR_Code GetSensorPosition(int ¶m);
+ CTR_Code GetSensorVelocity(int ¶m);
+ CTR_Code GetCurrent(double ¶m);
+ CTR_Code GetBrakeIsEnabled(int ¶m);
+ CTR_Code GetEncPosition(int ¶m);
+ CTR_Code GetEncVel(int ¶m);
+ CTR_Code GetEncIndexRiseEvents(int ¶m);
+ CTR_Code GetQuadApin(int ¶m);
+ CTR_Code GetQuadBpin(int ¶m);
+ CTR_Code GetQuadIdxpin(int ¶m);
+ CTR_Code GetAnalogInWithOv(int ¶m);
+ CTR_Code GetAnalogInVel(int ¶m);
+ CTR_Code GetTemp(double ¶m);
+ CTR_Code GetBatteryV(double ¶m);
+ CTR_Code GetResetCount(int ¶m);
+ CTR_Code GetResetFlags(int ¶m);
+ CTR_Code GetFirmVers(int ¶m);
+ CTR_Code SetDemand(int param);
+ CTR_Code SetOverrideLimitSwitchEn(int param);
+ CTR_Code SetFeedbackDeviceSelect(int param);
+ CTR_Code SetRevMotDuringCloseLoopEn(int param);
+ CTR_Code SetOverrideBrakeType(int param);
+ CTR_Code SetModeSelect(int param);
+ CTR_Code SetModeSelect(int modeSelect,int demand);
+ CTR_Code SetProfileSlotSelect(int param);
+ CTR_Code SetRampThrottle(int param);
+ CTR_Code SetRevFeedbackSensor(int param);
+};
+extern "C" {
+ void *c_TalonSRX_Create(int deviceNumber, int controlPeriodMs);
+ void c_TalonSRX_Destroy(void *handle);
+ CTR_Code c_TalonSRX_SetParam(void *handle, int paramEnum, double value);
+ CTR_Code c_TalonSRX_RequestParam(void *handle, int paramEnum);
+ CTR_Code c_TalonSRX_GetParamResponse(void *handle, int paramEnum, double *value);
+ CTR_Code c_TalonSRX_GetParamResponseInt32(void *handle, int paramEnum, int *value);
+ CTR_Code c_TalonSRX_SetStatusFrameRate(void *handle, unsigned frameEnum, unsigned periodMs);
+ CTR_Code c_TalonSRX_ClearStickyFaults(void *handle);
+ CTR_Code c_TalonSRX_GetFault_OverTemp(void *handle, int *param);
+ CTR_Code c_TalonSRX_GetFault_UnderVoltage(void *handle, int *param);
+ CTR_Code c_TalonSRX_GetFault_ForLim(void *handle, int *param);
+ CTR_Code c_TalonSRX_GetFault_RevLim(void *handle, int *param);
+ CTR_Code c_TalonSRX_GetFault_HardwareFailure(void *handle, int *param);
+ CTR_Code c_TalonSRX_GetFault_ForSoftLim(void *handle, int *param);
+ CTR_Code c_TalonSRX_GetFault_RevSoftLim(void *handle, int *param);
+ CTR_Code c_TalonSRX_GetStckyFault_OverTemp(void *handle, int *param);
+ CTR_Code c_TalonSRX_GetStckyFault_UnderVoltage(void *handle, int *param);
+ CTR_Code c_TalonSRX_GetStckyFault_ForLim(void *handle, int *param);
+ CTR_Code c_TalonSRX_GetStckyFault_RevLim(void *handle, int *param);
+ CTR_Code c_TalonSRX_GetStckyFault_ForSoftLim(void *handle, int *param);
+ CTR_Code c_TalonSRX_GetStckyFault_RevSoftLim(void *handle, int *param);
+ CTR_Code c_TalonSRX_GetAppliedThrottle(void *handle, int *param);
+ CTR_Code c_TalonSRX_GetCloseLoopErr(void *handle, int *param);
+ CTR_Code c_TalonSRX_GetFeedbackDeviceSelect(void *handle, int *param);
+ CTR_Code c_TalonSRX_GetModeSelect(void *handle, int *param);
+ CTR_Code c_TalonSRX_GetLimitSwitchEn(void *handle, int *param);
+ CTR_Code c_TalonSRX_GetLimitSwitchClosedFor(void *handle, int *param);
+ CTR_Code c_TalonSRX_GetLimitSwitchClosedRev(void *handle, int *param);
+ CTR_Code c_TalonSRX_GetSensorPosition(void *handle, int *param);
+ CTR_Code c_TalonSRX_GetSensorVelocity(void *handle, int *param);
+ CTR_Code c_TalonSRX_GetCurrent(void *handle, double *param);
+ CTR_Code c_TalonSRX_GetBrakeIsEnabled(void *handle, int *param);
+ CTR_Code c_TalonSRX_GetEncPosition(void *handle, int *param);
+ CTR_Code c_TalonSRX_GetEncVel(void *handle, int *param);
+ CTR_Code c_TalonSRX_GetEncIndexRiseEvents(void *handle, int *param);
+ CTR_Code c_TalonSRX_GetQuadApin(void *handle, int *param);
+ CTR_Code c_TalonSRX_GetQuadBpin(void *handle, int *param);
+ CTR_Code c_TalonSRX_GetQuadIdxpin(void *handle, int *param);
+ CTR_Code c_TalonSRX_GetAnalogInWithOv(void *handle, int *param);
+ CTR_Code c_TalonSRX_GetAnalogInVel(void *handle, int *param);
+ CTR_Code c_TalonSRX_GetTemp(void *handle, double *param);
+ CTR_Code c_TalonSRX_GetBatteryV(void *handle, double *param);
+ CTR_Code c_TalonSRX_GetResetCount(void *handle, int *param);
+ CTR_Code c_TalonSRX_GetResetFlags(void *handle, int *param);
+ CTR_Code c_TalonSRX_GetFirmVers(void *handle, int *param);
+ CTR_Code c_TalonSRX_SetDemand(void *handle, int param);
+ CTR_Code c_TalonSRX_SetOverrideLimitSwitchEn(void *handle, int param);
+ CTR_Code c_TalonSRX_SetFeedbackDeviceSelect(void *handle, int param);
+ CTR_Code c_TalonSRX_SetRevMotDuringCloseLoopEn(void *handle, int param);
+ CTR_Code c_TalonSRX_SetOverrideBrakeType(void *handle, int param);
+ CTR_Code c_TalonSRX_SetModeSelect(void *handle, int param);
+ CTR_Code c_TalonSRX_SetProfileSlotSelect(void *handle, int param);
+ CTR_Code c_TalonSRX_SetRampThrottle(void *handle, int param);
+ CTR_Code c_TalonSRX_SetRevFeedbackSensor(void *handle, int param);
+}
+#endif
+
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/ctre/CtreCanNode.h b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/ctre/CtreCanNode.h
new file mode 100644
index 0000000..59e0939
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/ctre/CtreCanNode.h
@@ -0,0 +1,116 @@
+#ifndef CtreCanNode_H_
+#define CtreCanNode_H_
+#include "ctre.h" //BIT Defines + Typedefs
+#include <NetworkCommunication/CANSessionMux.h> //CAN Comm
+#include <pthread.h>
+#include <map>
+#include <string.h> // memcpy
+#include <sys/time.h>
+class CtreCanNode
+{
+public:
+ CtreCanNode(UINT8 deviceNumber);
+ ~CtreCanNode();
+
+ UINT8 GetDeviceNumber()
+ {
+ return _deviceNumber;
+ }
+protected:
+
+
+ template <typename T> class txTask{
+ public:
+ uint32_t arbId;
+ T * toSend;
+ T * operator -> ()
+ {
+ return toSend;
+ }
+ T & operator*()
+ {
+ return *toSend;
+ }
+ bool IsEmpty()
+ {
+ if(toSend == 0)
+ return true;
+ return false;
+ }
+ };
+ template <typename T> class recMsg{
+ public:
+ uint32_t arbId;
+ uint8_t bytes[8];
+ CTR_Code err;
+ T * operator -> ()
+ {
+ return (T *)bytes;
+ }
+ T & operator*()
+ {
+ return *(T *)bytes;
+ }
+ };
+ UINT8 _deviceNumber;
+ void RegisterRx(uint32_t arbId);
+ void RegisterTx(uint32_t arbId, uint32_t periodMs);
+
+ CTR_Code GetRx(uint32_t arbId,uint8_t * dataBytes,uint32_t timeoutMs);
+ void FlushTx(uint32_t arbId);
+
+ template<typename T> txTask<T> GetTx(uint32_t arbId)
+ {
+ txTask<T> retval = {0};
+ txJobs_t::iterator i = _txJobs.find(arbId);
+ if(i != _txJobs.end()){
+ retval.arbId = i->second.arbId;
+ retval.toSend = (T*)i->second.toSend;
+ }
+ return retval;
+ }
+ template<class T> void FlushTx(T & par)
+ {
+ FlushTx(par.arbId);
+ }
+
+ template<class T> recMsg<T> GetRx(uint32_t arbId, uint32_t timeoutMs)
+ {
+ recMsg<T> retval;
+ retval.err = GetRx(arbId,retval.bytes, timeoutMs);
+ return retval;
+ }
+
+private:
+
+ class txJob_t {
+ public:
+ uint32_t arbId;
+ uint8_t toSend[8];
+ uint32_t periodMs;
+ };
+
+ class rxEvent_t{
+ public:
+ uint8_t bytes[8];
+ struct timespec time;
+ rxEvent_t()
+ {
+ bytes[0] = 0;
+ bytes[1] = 0;
+ bytes[2] = 0;
+ bytes[3] = 0;
+ bytes[4] = 0;
+ bytes[5] = 0;
+ bytes[6] = 0;
+ bytes[7] = 0;
+ }
+ };
+
+ typedef std::map<uint32_t,txJob_t> txJobs_t;
+ txJobs_t _txJobs;
+
+ typedef std::map<uint32_t,rxEvent_t> rxRxEvents_t;
+ rxRxEvents_t _rxRxEvents;
+};
+#endif
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/ctre/ctre.h b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/ctre/ctre.h
new file mode 100644
index 0000000..c2d3f69
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/ctre/ctre.h
@@ -0,0 +1,50 @@
+#ifndef GLOBAL_H
+#define GLOBAL_H
+
+//Bit Defines
+#define BIT0 0x01
+#define BIT1 0x02
+#define BIT2 0x04
+#define BIT3 0x08
+#define BIT4 0x10
+#define BIT5 0x20
+#define BIT6 0x40
+#define BIT7 0x80
+#define BIT8 0x0100
+#define BIT9 0x0200
+#define BIT10 0x0400
+#define BIT11 0x0800
+#define BIT12 0x1000
+#define BIT13 0x2000
+#define BIT14 0x4000
+#define BIT15 0x8000
+
+//Signed
+typedef signed char INT8;
+typedef signed short INT16;
+typedef signed int INT32;
+typedef signed long long INT64;
+
+//Unsigned
+typedef unsigned char UINT8;
+typedef unsigned short UINT16;
+typedef unsigned int UINT32;
+typedef unsigned long long UINT64;
+
+//Other
+typedef unsigned char UCHAR;
+typedef unsigned short USHORT;
+typedef unsigned int UINT;
+typedef unsigned long ULONG;
+
+typedef enum {
+ CTR_OKAY, //!< No Error - Function executed as expected
+ CTR_RxTimeout, //!< CAN frame has not been received within specified period of time.
+ CTR_TxTimeout, //!< Not used.
+ CTR_InvalidParamValue, //!< Caller passed an invalid param
+ CTR_UnexpectedArbId, //!< Specified CAN Id is invalid.
+ CTR_TxFailed, //!< Could not transmit the CAN frame.
+ CTR_SigNotUpdated, //!< Have not received an value response for signal.
+}CTR_Code;
+
+#endif
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/nivision.h b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/nivision.h
new file mode 100644
index 0000000..09bb984
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/nivision.h
@@ -0,0 +1,5345 @@
+/*============================================================================*/
+/* IMAQ Vision */
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) National Instruments 2001. All Rights Reserved. */
+/*----------------------------------------------------------------------------*/
+/* */
+/* Title: NIVision.h */
+/* */
+/*============================================================================*/
+#if !defined(NiVision_h)
+#define NiVision_h
+
+//============================================================================
+// Includes
+//============================================================================
+#include <stddef.h>
+
+
+//============================================================================
+// Control Defines
+//============================================================================
+#if !defined(IMAQ_IMPORT)
+ #ifndef __GNUC__
+ #define IMAQ_IMPORT __declspec(dllimport)
+ #else
+ #define IMAQ_IMPORT
+ #endif
+#endif
+
+#if !defined(IMAQ_FUNC)
+ #if !defined(__cplusplus)
+ #define IMAQ_FUNC IMAQ_IMPORT
+ #else
+ #define IMAQ_FUNC extern "C" IMAQ_IMPORT
+ #endif
+#endif
+
+#if !defined(IMAQ_STDCALL)
+ #ifndef __GNUC__
+ #define IMAQ_STDCALL __stdcall
+ #else
+ #define IMAQ_STDCALL
+ #endif
+#endif
+
+#ifdef _CVI_
+#pragma EnableLibraryRuntimeChecking
+#include <ansi_c.h>
+#endif
+
+#define IMAQ_CALLBACK __cdecl
+
+//============================================================================
+// Manifest Constants
+//============================================================================
+#ifndef NULL
+ #ifdef __cplusplus
+ #define NULL 0
+ #else
+ #define NULL ((void *)0)
+ #endif
+#endif
+
+#ifndef FALSE
+ #define FALSE 0
+#endif
+
+#ifndef TRUE
+ #define TRUE 1
+#endif
+
+#define IMAQ_DEFAULT_SHOW_COORDINATES TRUE
+#define IMAQ_DEFAULT_MAX_ICONS_PER_LINE 4
+#define IMAQ_DEFAULT_LEARNING_MODE IMAQ_LEARN_SHIFT_INFORMATION
+#define IMAQ_DEFAULT_BMP_COMPRESS FALSE
+#define IMAQ_DEFAULT_PNG_QUALITY 750
+#define IMAQ_DEFAULT_JPEG_QUALITY 750
+#define IMAQ_ALL_CONTOURS -1
+#define IMAQ_ALL_WINDOWS -1
+#define IMAQ_SHIFT 1
+#define IMAQ_ALT 2
+#define IMAQ_CTRL 4
+#define IMAQ_CAPS_LOCK 8
+#define IMAQ_MODAL_DIALOG -1
+#define IMAQ_INIT_RGB_TRANSPARENT { 0, 0, 0, 1 }
+#define IMAQ_INIT_RGB_RED { 0, 0, 255, 0 }
+#define IMAQ_INIT_RGB_BLUE { 255, 0, 0, 0 }
+#define IMAQ_INIT_RGB_GREEN { 0, 255, 0, 0 }
+#define IMAQ_INIT_RGB_YELLOW { 0, 255, 255, 0 }
+#define IMAQ_INIT_RGB_WHITE { 255, 255, 255, 0 }
+#define IMAQ_INIT_RGB_BLACK { 0, 0, 0, 0 }
+#define IMAQ_USE_DEFAULT_QUALITY -1
+#define IMAQ_ALL_SAMPLES -1
+#define IMAQ_ALL_OBJECTS -1
+#define IMAQ_ALL_CHARACTERS -1
+
+//============================================================================
+// Predefined Valid Characters
+//============================================================================
+#define IMAQ_ANY_CHARACTER "" //Any Character
+#define IMAQ_ALPHABETIC "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" //Alphabetic
+#define IMAQ_ALPHANUMERIC "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" //Alphanumeric
+#define IMAQ_UPPERCASE_LETTERS "ABCDEFGHIJKLMNOPQRSTUVWXYZ" //Uppercase Letters
+#define IMAQ_LOWERCASE_LETTERS "abcdefghijklmnopqrstuvwxyz" //Lowercase Letters
+#define IMAQ_DECIMAL_DIGITS "0123456789" //Decimal Digits
+#define IMAQ_HEXADECIMAL_DIGITS "0123456789ABCDEFabcdef" //Hexadecimal Digits
+#define IMAQ_PATTERN "\xFF" //Pattern (A single character string with the character value set to 255)
+#define IMAQ_FORCE_SPACE " " //Force Space
+
+//============================================================================
+// Macros
+//============================================================================
+#define IMAQ_NO_RECT imaqMakeRect( 0, 0, 0x7FFFFFFF, 0x7FFFFFFF)
+#define IMAQ_NO_ROTATED_RECT imaqMakeRotatedRect( 0, 0, 0x7FFFFFFF, 0x7FFFFFFF, 0)
+#define IMAQ_NO_POINT imaqMakePoint( -1, -1)
+#define IMAQ_NO_POINT_FLOAT imaqMakePointFloat( -1.0, -1.0 )
+#define IMAQ_NO_OFFSET imaqMakePointFloat( 0.0, 0.0 )
+
+
+
+//============================================================================
+// When in Borland, some functions must be mapped to different names.
+// This accomplishes said task.
+//============================================================================
+#if defined(__BORLANDC__) || (defined(_CVI_) && defined(_NI_BC_))
+ #define imaqMakePoint imaqMakePoint_BC
+ #define imaqMakePointFloat imaqMakePointFloat_BC
+#endif
+
+
+//============================================================================
+// When in Watcom, some functions must be mapped to different names.
+// This accomplishes said task.
+//============================================================================
+#if defined(__WATCOMC__) || (defined(_CVI_) && defined(_NI_WC_))
+ #define imaqMakePoint imaqMakePoint_BC
+ #define imaqMakePointFloat imaqMakePointFloat_BC
+#endif
+
+//============================================================================
+// If using Visual C++, force startup & shutdown code to run.
+//============================================================================
+#if defined(_MSC_VER) && !defined(_CVI_) && !defined(__BORLANDC__)
+ #pragma comment(linker, "/INCLUDE:_nivision_startup_shutdown")
+ #pragma comment(linker, "/DEFAULTLIB:nivision.lib")
+#endif
+
+//============================================================================
+// Error Codes
+//============================================================================
+#define ERR_SUCCESS 0 // No error.
+#define ERR_SYSTEM_ERROR -1074396160 // System error.
+#define ERR_OUT_OF_MEMORY -1074396159 // Not enough memory for requested operation.
+#define ERR_MEMORY_ERROR -1074396158 // Memory error.
+#define ERR_UNREGISTERED -1074396157 // Unlicensed copy of NI Vision.
+#define ERR_NEED_FULL_VERSION -1074396156 // The function requires an NI Vision 5.0 Advanced license.
+#define ERR_UNINIT -1074396155 // NI Vision did not initialize properly.
+#define ERR_IMAGE_TOO_SMALL -1074396154 // The image is not large enough for the operation.
+#define ERR_BARCODE_CODABAR -1074396153 // The barcode is not a valid Codabar barcode.
+#define ERR_BARCODE_CODE39 -1074396152 // The barcode is not a valid Code 3 of 9 barcode.
+#define ERR_BARCODE_CODE93 -1074396151 // The barcode is not a valid Code93 barcode.
+#define ERR_BARCODE_CODE128 -1074396150 // The barcode is not a valid Code128 barcode.
+#define ERR_BARCODE_EAN8 -1074396149 // The barcode is not a valid EAN8 barcode.
+#define ERR_BARCODE_EAN13 -1074396148 // The barcode is not a valid EAN13 barcode.
+#define ERR_BARCODE_I25 -1074396147 // The barcode is not a valid Interleaved 2 of 5 barcode.
+#define ERR_BARCODE_MSI -1074396146 // The barcode is not a valid MSI barcode.
+#define ERR_BARCODE_UPCA -1074396145 // The barcode is not a valid UPCA barcode.
+#define ERR_BARCODE_CODE93_SHIFT -1074396144 // The Code93 barcode contains invalid shift encoding.
+#define ERR_BARCODE_TYPE -1074396143 // The barcode type is invalid.
+#define ERR_BARCODE_INVALID -1074396142 // The image does not represent a valid linear barcode.
+#define ERR_BARCODE_CODE128_FNC -1074396141 // The FNC value in the Code128 barcode is not located before the first data value.
+#define ERR_BARCODE_CODE128_SET -1074396140 // The starting code set in the Code128 barcode is not valid.
+#define ERR_ROLLBACK_RESOURCE_OUT_OF_MEMORY -1074396139 // Not enough reserved memory in the timed environment for the requested operation.
+#define ERR_ROLLBACK_NOT_SUPPORTED -1074396138 // The function is not supported when a time limit is active.
+#define ERR_DIRECTX_DLL_NOT_FOUND -1074396137 // Quartz.dll not found. Install DirectX 8.1 or later.
+#define ERR_DIRECTX_INVALID_FILTER_QUALITY -1074396136 // The filter quality you provided is invalid. Valid quality values range from -1 to 1000.
+#define ERR_INVALID_BUTTON_LABEL -1074396135 // Invalid button label.
+#define ERR_THREAD_INITIALIZING -1074396134 // Could not execute the function in the separate thread because the thread has not completed initialization.
+#define ERR_THREAD_COULD_NOT_INITIALIZE -1074396133 // Could not execute the function in the separate thread because the thread could not initialize.
+#define ERR_MASK_NOT_TEMPLATE_SIZE -1074396132 // The mask must be the same size as the template.
+#define ERR_NOT_RECT_OR_ROTATED_RECT -1074396130 // The ROI must only have either a single Rectangle contour or a single Rotated Rectangle contour.
+#define ERR_ROLLBACK_UNBOUNDED_INTERFACE -1074396129 // During timed execution, you must use the preallocated version of this operation.
+#define ERR_ROLLBACK_RESOURCE_CONFLICT_3 -1074396128 // An image being modified by one process cannot be requested by another process while a time limit is active.
+#define ERR_ROLLBACK_RESOURCE_CONFLICT_2 -1074396127 // An image with pattern matching, calibration, or overlay information cannot be manipulated while a time limit is active.
+#define ERR_ROLLBACK_RESOURCE_CONFLICT_1 -1074396126 // An image created before a time limit is started cannot be resized while a time limit is active.
+#define ERR_INVALID_CONTRAST_THRESHOLD -1074396125 // Invalid contrast threshold. The threshold value must be greater than 0.
+#define ERR_INVALID_CALIBRATION_ROI_MODE -1074396124 // NI Vision does not support the calibration ROI mode you supplied.
+#define ERR_INVALID_CALIBRATION_MODE -1074396123 // NI Vision does not support the calibration mode you supplied.
+#define ERR_DRAWTEXT_COLOR_MUST_BE_GRAYSCALE -1074396122 // Set the foreground and background text colors to grayscale to draw on a U8 image.
+#define ERR_SATURATION_THRESHOLD_OUT_OF_RANGE -1074396121 // The value of the saturation threshold must be from 0 to 255.
+#define ERR_NOT_IMAGE -1074396120 // Not an image.
+#define ERR_CUSTOMDATA_INVALID_KEY -1074396119 // They custom data key you supplied is invalid. The only valid character values are decimal 32-126 and 161-255. There must also be no repeated, leading, or trailing spaces.
+#define ERR_INVALID_STEP_SIZE -1074396118 // Step size must be greater than zero and less than Image size
+#define ERR_MATRIX_SIZE -1074396117 // Invalid matrix size in the structuring element.
+#define ERR_CALIBRATION_INSF_POINTS -1074396116 // Insufficient number of calibration feature points.
+#define ERR_CALIBRATION_IMAGE_CORRECTED -1074396115 // The operation is invalid in a corrected image.
+#define ERR_CALIBRATION_INVALID_ROI -1074396114 // The ROI contains an invalid contour type or is not contained in the ROI learned for calibration.
+#define ERR_CALIBRATION_IMAGE_UNCALIBRATED -1074396113 // The source/input image has not been calibrated.
+#define ERR_INCOMP_MATRIX_SIZE -1074396112 // The number of pixel and real-world coordinates must be equal.
+#define ERR_CALIBRATION_FAILED_TO_FIND_GRID -1074396111 // Unable to automatically detect grid because the image is too distorted.
+#define ERR_CALIBRATION_INFO_VERSION -1074396110 // Invalid calibration information version.
+#define ERR_CALIBRATION_INVALID_SCALING_FACTOR -1074396109 // Invalid calibration scaling factor.
+#define ERR_CALIBRATION_ERRORMAP -1074396108 // The calibration error map cannot be computed.
+#define ERR_CALIBRATION_INFO_1 -1074396107 // Invalid calibration template image.
+#define ERR_CALIBRATION_INFO_2 -1074396106 // Invalid calibration template image.
+#define ERR_CALIBRATION_INFO_3 -1074396105 // Invalid calibration template image.
+#define ERR_CALIBRATION_INFO_4 -1074396104 // Invalid calibration template image.
+#define ERR_CALIBRATION_INFO_5 -1074396103 // Invalid calibration template image.
+#define ERR_CALIBRATION_INFO_6 -1074396102 // Invalid calibration template image.
+#define ERR_CALIBRATION_INFO_MICRO_PLANE -1074396101 // Invalid calibration template image.
+#define ERR_CALIBRATION_INFO_PERSPECTIVE_PROJECTION -1074396100 // Invalid calibration template image.
+#define ERR_CALIBRATION_INFO_SIMPLE_TRANSFORM -1074396099 // Invalid calibration template image.
+#define ERR_RESERVED_MUST_BE_NULL -1074396098 // You must pass NULL for the reserved parameter.
+#define ERR_INVALID_PARTICLE_PARAMETER_VALUE -1074396097 // You entered an invalid selection in the particle parameter.
+#define ERR_NOT_AN_OBJECT -1074396096 // Not an object.
+#define ERR_CALIBRATION_DUPLICATE_REFERENCE_POINT -1074396095 // The reference points passed are inconsistent. At least two similar pixel coordinates correspond to different real-world coordinates.
+#define ERR_ROLLBACK_RESOURCE_CANNOT_UNLOCK -1074396094 // A resource conflict occurred in the timed environment. Two processes cannot manage the same resource and be time bounded.
+#define ERR_ROLLBACK_RESOURCE_LOCKED -1074396093 // A resource conflict occurred in the timed environment. Two processes cannot access the same resource and be time bounded.
+#define ERR_ROLLBACK_RESOURCE_NON_EMPTY_INITIALIZE -1074396092 // Multiple timed environments are not supported.
+#define ERR_ROLLBACK_RESOURCE_UNINITIALIZED_ENABLE -1074396091 // A time limit cannot be started until the timed environment is initialized.
+#define ERR_ROLLBACK_RESOURCE_ENABLED -1074396090 // Multiple timed environments are not supported.
+#define ERR_ROLLBACK_RESOURCE_REINITIALIZE -1074396089 // The timed environment is already initialized.
+#define ERR_ROLLBACK_RESIZE -1074396088 // The results of the operation exceeded the size limits on the output data arrays.
+#define ERR_ROLLBACK_STOP_TIMER -1074396087 // No time limit is available to stop.
+#define ERR_ROLLBACK_START_TIMER -1074396086 // A time limit could not be set.
+#define ERR_ROLLBACK_INIT_TIMER -1074396085 // The timed environment could not be initialized.
+#define ERR_ROLLBACK_DELETE_TIMER -1074396084 // No initialized timed environment is available to close.
+#define ERR_ROLLBACK_TIMEOUT -1074396083 // The time limit has expired.
+#define ERR_PALETTE_NOT_SUPPORTED -1074396082 // Only 8-bit images support the use of palettes. Either do not use a palette, or convert your image to an 8-bit image before using a palette.
+#define ERR_BAD_PASSWORD -1074396081 // Incorrect password.
+#define ERR_INVALID_IMAGE_TYPE -1074396080 // Invalid image type.
+#define ERR_INVALID_METAFILE_HANDLE -1074396079 // Invalid metafile handle.
+#define ERR_INCOMP_TYPE -1074396077 // Incompatible image type.
+#define ERR_COORD_SYS_FIRST_AXIS -1074396076 // Unable to fit a line for the primary axis.
+#define ERR_COORD_SYS_SECOND_AXIS -1074396075 // Unable to fit a line for the secondary axis.
+#define ERR_INCOMP_SIZE -1074396074 // Incompatible image size.
+#define ERR_MASK_OUTSIDE_IMAGE -1074396073 // When the mask's offset was applied, the mask was entirely outside of the image.
+#define ERR_INVALID_BORDER -1074396072 // Invalid image border.
+#define ERR_INVALID_SCAN_DIRECTION -1074396071 // Invalid scan direction.
+#define ERR_INVALID_FUNCTION -1074396070 // Unsupported function.
+#define ERR_INVALID_COLOR_MODE -1074396069 // NI Vision does not support the color mode you specified.
+#define ERR_INVALID_ACTION -1074396068 // The function does not support the requested action.
+#define ERR_IMAGES_NOT_DIFF -1074396067 // The source image and destination image must be different.
+#define ERR_INVALID_POINTSYMBOL -1074396066 // Invalid point symbol.
+#define ERR_CANT_RESIZE_EXTERNAL -1074396065 // Cannot resize an image in an acquisition buffer.
+#define ERR_EXTERNAL_NOT_SUPPORTED -1074396064 // This operation is not supported for images in an acquisition buffer.
+#define ERR_EXTERNAL_ALIGNMENT -1074396063 // The external buffer must be aligned on a 4-byte boundary. The line width and border pixels must be 4-byte aligned, as well.
+#define ERR_INVALID_TOLERANCE -1074396062 // The tolerance parameter must be greater than or equal to 0.
+#define ERR_INVALID_WINDOW_SIZE -1074396061 // The size of each dimension of the window must be greater than 2 and less than or equal to the size of the image in the corresponding dimension.
+#define ERR_JPEG2000_LOSSLESS_WITH_FLOATING_POINT -1074396060 // Lossless compression cannot be used with the floating point wavelet transform mode. Either set the wavelet transform mode to integer, or use lossy compression.
+#define ERR_INVALID_MAX_ITERATIONS -1074396059 // Invalid maximum number of iterations. Maximum number of iterations must be greater than zero.
+#define ERR_INVALID_ROTATION_MODE -1074396058 // Invalid rotation mode.
+#define ERR_INVALID_SEARCH_VECTOR_WIDTH -1074396057 // Invalid search vector width. The width must be an odd number greater than zero.
+#define ERR_INVALID_MATRIX_MIRROR_MODE -1074396056 // Invalid matrix mirror mode.
+#define ERR_INVALID_ASPECT_RATIO -1074396055 // Invalid aspect ratio. Valid aspect ratios must be greater than or equal to zero.
+#define ERR_INVALID_CELL_FILL_TYPE -1074396054 // Invalid cell fill type.
+#define ERR_INVALID_BORDER_INTEGRITY -1074396053 // Invalid border integrity. Valid values range from 0 to 100.
+#define ERR_INVALID_DEMODULATION_MODE -1074396052 // Invalid demodulation mode.
+#define ERR_INVALID_CELL_FILTER_MODE -1074396051 // Invalid cell filter mode.
+#define ERR_INVALID_ECC_TYPE -1074396050 // Invalid ECC type.
+#define ERR_INVALID_MATRIX_POLARITY -1074396049 // Invalid matrix polarity.
+#define ERR_INVALID_CELL_SAMPLE_SIZE -1074396048 // Invalid cell sample size.
+#define ERR_INVALID_LINEAR_AVERAGE_MODE -1074396047 // Invalid linear average mode.
+#define ERR_INVALID_2D_BARCODE_CONTRAST_FOR_ROI -1074396046 // When using a region of interest that is not a rectangle, you must specify the contrast mode of the barcode as either black on white or white on black.
+#define ERR_INVALID_2D_BARCODE_SUBTYPE -1074396045 // Invalid 2-D barcode Data Matrix subtype.
+#define ERR_INVALID_2D_BARCODE_SHAPE -1074396044 // Invalid 2-D barcode shape.
+#define ERR_INVALID_2D_BARCODE_CELL_SHAPE -1074396043 // Invalid 2-D barcode cell shape.
+#define ERR_INVALID_2D_BARCODE_CONTRAST -1074396042 // Invalid 2-D barcode contrast.
+#define ERR_INVALID_2D_BARCODE_TYPE -1074396041 // Invalid 2-D barcode type.
+#define ERR_DRIVER -1074396040 // Cannot access NI-IMAQ driver.
+#define ERR_IO_ERROR -1074396039 // I/O error.
+#define ERR_FIND_COORDSYS_MORE_THAN_ONE_EDGE -1074396038 // When searching for a coordinate system, the number of lines to fit must be 1.
+#define ERR_TIMEOUT -1074396037 // Trigger timeout.
+#define ERR_INVALID_SKELETONMODE -1074396036 // The Skeleton mode you specified is invalid.
+#define ERR_TEMPLATEIMAGE_NOCIRCLE -1074396035 // The template image does not contain enough information for learning the aggressive search strategy.
+#define ERR_TEMPLATEIMAGE_EDGEINFO -1074396034 // The template image does not contain enough edge information for the sample size(s) requested.
+#define ERR_TEMPLATEDESCRIPTOR_LEARNSETUPDATA -1074396033 // Invalid template descriptor.
+#define ERR_TEMPLATEDESCRIPTOR_ROTATION_SEARCHSTRATEGY -1074396032 // The template descriptor does not contain data required for the requested search strategy in rotation-invariant matching.
+#define ERR_INVALID_TETRAGON -1074396031 // The input tetragon must have four points. The points are specified clockwise starting with the top left point.
+#define ERR_TOO_MANY_CLASSIFICATION_SESSIONS -1074396030 // There are too many classification sessions open. You must close a session before you can open another one.
+#define ERR_TIME_BOUNDED_EXECUTION_NOT_SUPPORTED -1074396028 // NI Vision no longer supports time-bounded execution.
+#define ERR_INVALID_COLOR_RESOLUTION -1074396027 // Invalid Color Resolution for the Color Classifier
+#define ERR_INVALID_PROCESS_TYPE_FOR_EDGE_DETECTION -1074396026 // Invalid process type for edge detection.
+#define ERR_INVALID_ANGLE_RANGE_FOR_STRAIGHT_EDGE -1074396025 // Angle range value should be equal to or greater than zero.
+#define ERR_INVALID_MIN_COVERAGE_FOR_STRAIGHT_EDGE -1074396024 // Minimum coverage value should be greater than zero.
+#define ERR_INVALID_ANGLE_TOL_FOR_STRAIGHT_EDGE -1074396023 // The angle tolerance should be equal to or greater than 0.001.
+#define ERR_INVALID_SEARCH_MODE_FOR_STRAIGHT_EDGE -1074396022 // Invalid search mode for detecting straight edges
+#define ERR_INVALID_KERNEL_SIZE_FOR_EDGE_DETECTION -1074396021 // Invalid kernel size for edge detection. The minimum kernel size is 3, the maximum kernel size is 1073741823 and the kernel size must be odd.
+#define ERR_INVALID_GRADING_MODE -1074396020 // Invalid grading mode.
+#define ERR_INVALID_THRESHOLD_PERCENTAGE -1074396019 // Invalid threshold percentage. Valid values range from 0 to 100.
+#define ERR_INVALID_EDGE_POLARITY_SEARCH_MODE -1074396018 // Invalid edge polarity search mode.
+#define ERR_OPENING_NEWER_AIM_GRADING_DATA -1074396017 // The AIM grading data attached to the image you tried to open was created with a newer version of NI Vision. Upgrade to the latest version of NI Vision to read this file.
+#define ERR_NO_VIDEO_DRIVER -1074396016 // No video driver is installed.
+#define ERR_RPC_EXECUTE_IVB -1074396015 // Unable to establish network connection with remote system.
+#define ERR_INVALID_VIDEO_BLIT -1074396014 // RT Video Out does not support displaying the supplied image type at the selected color depth.
+#define ERR_INVALID_VIDEO_MODE -1074396013 // Invalid video mode.
+#define ERR_RPC_EXECUTE -1074396012 // Unable to display remote image on network connection.
+#define ERR_RPC_BIND -1074396011 // Unable to establish network connection.
+#define ERR_INVALID_FRAME_NUMBER -1074396010 // Invalid frame number.
+#define ERR_DIRECTX -1074396009 // An internal DirectX error has occurred. Try upgrading to the latest version of DirectX.
+#define ERR_DIRECTX_NO_FILTER -1074396008 // An appropriate DirectX filter to process this file could not be found. Install the filter that was used to create this AVI. Upgrading to the latest version of DirectX may correct this error. NI Vision requires DirectX 8.1 or higher.
+#define ERR_DIRECTX_INCOMPATIBLE_COMPRESSION_FILTER -1074396007 // Incompatible compression filter.
+#define ERR_DIRECTX_UNKNOWN_COMPRESSION_FILTER -1074396006 // Unknown compression filter.
+#define ERR_INVALID_AVI_SESSION -1074396005 // Invalid AVI session.
+#define ERR_DIRECTX_CERTIFICATION_FAILURE -1074396004 // A software key is restricting the use of this compression filter.
+#define ERR_AVI_DATA_EXCEEDS_BUFFER_SIZE -1074396003 // The data for this frame exceeds the data buffer size specified when creating the AVI file.
+#define ERR_INVALID_LINEGAUGEMETHOD -1074396002 // Invalid line gauge method.
+#define ERR_TOO_MANY_AVI_SESSIONS -1074396001 // There are too many AVI sessions open. You must close a session before you can open another one.
+#define ERR_FILE_FILE_HEADER -1074396000 // Invalid file header.
+#define ERR_FILE_FILE_TYPE -1074395999 // Invalid file type.
+#define ERR_FILE_COLOR_TABLE -1074395998 // Invalid color table.
+#define ERR_FILE_ARGERR -1074395997 // Invalid parameter.
+#define ERR_FILE_OPEN -1074395996 // File is already open for writing.
+#define ERR_FILE_NOT_FOUND -1074395995 // File not found.
+#define ERR_FILE_TOO_MANY_OPEN -1074395994 // Too many files open.
+#define ERR_FILE_IO_ERR -1074395993 // File I/O error.
+#define ERR_FILE_PERMISSION -1074395992 // File access denied.
+#define ERR_FILE_INVALID_TYPE -1074395991 // NI Vision does not support the file type you specified.
+#define ERR_FILE_GET_INFO -1074395990 // Could not read Vision info from file.
+#define ERR_FILE_READ -1074395989 // Unable to read data.
+#define ERR_FILE_WRITE -1074395988 // Unable to write data.
+#define ERR_FILE_EOF -1074395987 // Premature end of file.
+#define ERR_FILE_FORMAT -1074395986 // Invalid file format.
+#define ERR_FILE_OPERATION -1074395985 // Invalid file operation.
+#define ERR_FILE_INVALID_DATA_TYPE -1074395984 // NI Vision does not support the file data type you specified.
+#define ERR_FILE_NO_SPACE -1074395983 // Disk full.
+#define ERR_INVALID_FRAMES_PER_SECOND -1074395982 // The frames per second in an AVI must be greater than zero.
+#define ERR_INSUFFICIENT_BUFFER_SIZE -1074395981 // The buffer that was passed in is not big enough to hold all of the data.
+#define ERR_COM_INITIALIZE -1074395980 // Error initializing COM.
+#define ERR_INVALID_PARTICLE_INFO -1074395979 // The image has invalid particle information. Call imaqCountParticles on the image to create particle information.
+#define ERR_INVALID_PARTICLE_NUMBER -1074395978 // Invalid particle number.
+#define ERR_AVI_VERSION -1074395977 // The AVI file was created in a newer version of NI Vision. Upgrade to the latest version of NI Vision to read this AVI file.
+#define ERR_NUMBER_OF_PALETTE_COLORS -1074395976 // The color palette must have exactly 0 or 256 entries.
+#define ERR_AVI_TIMEOUT -1074395975 // DirectX has timed out reading or writing the AVI file. When closing an AVI file, try adding an additional delay. When reading an AVI file, try reducing CPU and disk load.
+#define ERR_UNSUPPORTED_JPEG2000_COLORSPACE_METHOD -1074395974 // NI Vision does not support reading JPEG2000 files with this colorspace method.
+#define ERR_JPEG2000_UNSUPPORTED_MULTIPLE_LAYERS -1074395973 // NI Vision does not support reading JPEG2000 files with more than one layer.
+#define ERR_DIRECTX_ENUMERATE_FILTERS -1074395972 // DirectX is unable to enumerate the compression filters. This is caused by a third-party compression filter that is either improperly installed or is preventing itself from being enumerated. Remove any recently installed compression filters and try again.
+#define ERR_INVALID_OFFSET -1074395971 // The offset you specified must be size 2.
+#define ERR_INIT -1074395960 // Initialization error.
+#define ERR_CREATE_WINDOW -1074395959 // Unable to create window.
+#define ERR_WINDOW_ID -1074395958 // Invalid window ID.
+#define ERR_ARRAY_SIZE_MISMATCH -1074395957 // The array sizes are not compatible.
+#define ERR_INVALID_QUALITY -1074395956 // The quality you provided is invalid. Valid quality values range from -1 to 1000.
+#define ERR_INVALID_MAX_WAVELET_TRANSFORM_LEVEL -1074395955 // Invalid maximum wavelet transform level. Valid values range from 0 to 255.
+#define ERR_INVALID_QUANTIZATION_STEP_SIZE -1074395954 // The quantization step size must be greater than or equal to 0.
+#define ERR_INVALID_WAVELET_TRANSFORM_MODE -1074395953 // Invalid wavelet transform mode.
+#define ERR_ROI_NOT_POINT -1074395952 // The ROI must only have a single Point contour.
+#define ERR_ROI_NOT_POINTS -1074395951 // The ROI must only have Point contours.
+#define ERR_ROI_NOT_LINE -1074395950 // The ROI must only have a single Line contour.
+#define ERR_ROI_NOT_ANNULUS -1074395949 // The ROI must only have a single Annulus contour.
+#define ERR_INVALID_MEASURE_PARTICLES_CALIBRATION_MODE -1074395948 // Invalid measure particles calibration mode.
+#define ERR_INVALID_PARTICLE_CLASSIFIER_THRESHOLD_TYPE -1074395947 // Invalid particle classifier threshold type.
+#define ERR_INVALID_DISTANCE -1074395946 // Invalid Color Segmentation Distance
+#define ERR_INVALID_PARTICLE_AREA -1074395945 // Invalid Color Segmenation Particle Area
+#define ERR_CLASS_NAME_NOT_FOUND -1074395944 // Required Class name is not found in trained labels/Class names
+#define ERR_NUMBER_LABEL_LIMIT_EXCEEDED -1074395943 // Number of Labels exceeded limit of label Image type
+#define ERR_INVALID_DISTANCE_LEVEL -1074395942 // Invalid Color Segmentation distance level
+#define ERR_INVALID_SVM_TYPE -1074395941 // Invalid SVM model type
+#define ERR_INVALID_SVM_KERNEL -1074395940 // Invalid SVM kernel type
+#define ERR_NO_SUPPORT_VECTOR_FOUND -1074395939 // No Support Vector is found at SVM training
+#define ERR_COST_LABEL_NOT_FOUND -1074395938 // Label name is not found in added samples
+#define ERR_EXCEEDED_SVM_MAX_ITERATION -1074395937 // SVM training exceeded maximim Iteration limit
+#define ERR_INVALID_SVM_PARAMETER -1074395936 // Invalid SVM Parameter
+#define ERR_INVALID_IDENTIFICATION_SCORE -1074395935 // Invalid Identification score. Must be between 0-1000.
+#define ERR_INVALID_TEXTURE_FEATURE -1074395934 // Requested for invalid texture feature
+#define ERR_INVALID_COOCCURRENCE_LEVEL -1074395933 // The coOccurrence Level must lie between 1 and the maximum pixel value of an image (255 for U8 image)
+#define ERR_INVALID_WAVELET_SUBBAND -1074395932 // Request for invalid wavelet subBand
+#define ERR_INVALID_FINAL_STEP_SIZE -1074395931 // The final step size must be lesser than the initial step size
+#define ERR_INVALID_ENERGY -1074395930 // Minimum Energy should lie between 0 and 100
+#define ERR_INVALID_TEXTURE_LABEL -1074395929 // The classification label must be texture or defect for texture defect classifier
+#define ERR_INVALID_WAVELET_TYPE -1074395928 // The wavelet type is invalid
+#define ERR_SAME_WAVELET_BANDS_SELECTED -1074395927 // Same Wavelet band is selected multiple times
+#define ERR_IMAGE_SIZE_MISMATCH -1074395926 // The two input image sizes are different
+#define ERR_NUMBER_CLASS -1074395920 // Invalid number of classes.
+#define ERR_INVALID_LUCAS_KANADE_WINDOW_SIZE -1074395888 // Both dimensions of the window size should be odd, greater than 2 and less than 16.
+#define ERR_INVALID_MATRIX_TYPE -1074395887 // The type of matrix supplied to the function is not supported.
+#define ERR_INVALID_OPTICAL_FLOW_TERMINATION_CRITERIA_TYPE -1074395886 // An invalid termination criteria was specified for the optical flow computation.
+#define ERR_LKP_NULL_PYRAMID -1074395885 // The pyramid levels where not properly allocated.
+#define ERR_INVALID_PYRAMID_LEVEL -1074395884 // The pyramid level specified cannot be negative
+#define ERR_INVALID_LKP_KERNEL -1074395883 // The kernel must be symmetric with non-zero coefficients and of odd size
+#define ERR_INVALID_HORN_SCHUNCK_LAMBDA -1074395882 // Invalid smoothing parameter in Horn Schunck operation.
+#define ERR_INVALID_HORN_SCHUNCK_TYPE -1074395881 // Invalid stopping criteria type for Horn Schunck optical flow.
+#define ERR_PARTICLE -1074395880 // Invalid particle.
+#define ERR_BAD_MEASURE -1074395879 // Invalid measure number.
+#define ERR_PROP_NODE_WRITE_NOT_SUPPORTED -1074395878 // The Image Display control does not support writing this property node.
+#define ERR_COLORMODE_REQUIRES_CHANGECOLORSPACE2 -1074395877 // The specified color mode requires the use of imaqChangeColorSpace2.
+#define ERR_UNSUPPORTED_COLOR_MODE -1074395876 // This function does not currently support the color mode you specified.
+#define ERR_BARCODE_PHARMACODE -1074395875 // The barcode is not a valid Pharmacode symbol
+#define ERR_BAD_INDEX -1074395840 // Invalid handle table index.
+#define ERR_INVALID_COMPRESSION_RATIO -1074395837 // The compression ratio must be greater than or equal to 1.
+#define ERR_TOO_MANY_CONTOURS -1074395801 // The ROI contains too many contours.
+#define ERR_PROTECTION -1074395800 // Protection error.
+#define ERR_INTERNAL -1074395799 // Internal error.
+#define ERR_INVALID_CUSTOM_SAMPLE -1074395798 // The size of the feature vector in the custom sample must match the size of those you have already added.
+#define ERR_INVALID_CLASSIFIER_SESSION -1074395797 // Not a valid classifier session.
+#define ERR_INVALID_KNN_METHOD -1074395796 // You requested an invalid Nearest Neighbor classifier method.
+#define ERR_K_TOO_LOW -1074395795 // The k parameter must be greater than two.
+#define ERR_K_TOO_HIGH -1074395794 // The k parameter must be <= the number of samples in each class.
+#define ERR_INVALID_OPERATION_ON_COMPACT_SESSION_ATTEMPTED -1074395793 // This classifier session is compact. Only the Classify and Dispose functions may be called on a compact classifier session.
+#define ERR_CLASSIFIER_SESSION_NOT_TRAINED -1074395792 // This classifier session is not trained. You may only call this function on a trained classifier session.
+#define ERR_CLASSIFIER_INVALID_SESSION_TYPE -1074395791 // This classifier function cannot be called on this type of classifier session.
+#define ERR_INVALID_DISTANCE_METRIC -1074395790 // You requested an invalid distance metric.
+#define ERR_OPENING_NEWER_CLASSIFIER_SESSION -1074395789 // The classifier session you tried to open was created with a newer version of NI Vision. Upgrade to the latest version of NI Vision to read this file.
+#define ERR_NO_SAMPLES -1074395788 // This operation cannot be performed because you have not added any samples.
+#define ERR_INVALID_CLASSIFIER_TYPE -1074395787 // You requested an invalid classifier type.
+#define ERR_INVALID_PARTICLE_OPTIONS -1074395786 // The sum of Scale Dependence and Symmetry Dependence must be less than 1000.
+#define ERR_NO_PARTICLE -1074395785 // The image yielded no particles.
+#define ERR_INVALID_LIMITS -1074395784 // The limits you supplied are not valid.
+#define ERR_BAD_SAMPLE_INDEX -1074395783 // The Sample Index fell outside the range of Samples.
+#define ERR_DESCRIPTION_TOO_LONG -1074395782 // The description must be <= 255 characters.
+#define ERR_CLASSIFIER_INVALID_ENGINE_TYPE -1074395781 // The engine for this classifier session does not support this operation.
+#define ERR_INVALID_PARTICLE_TYPE -1074395780 // You requested an invalid particle type.
+#define ERR_CANNOT_COMPACT_UNTRAINED -1074395779 // You may only save a session in compact form if it is trained.
+#define ERR_INVALID_KERNEL_SIZE -1074395778 // The Kernel size must be smaller than the image size.
+#define ERR_INCOMPATIBLE_CLASSIFIER_TYPES -1074395777 // The session you read from file must be the same type as the session you passed in.
+#define ERR_INVALID_USE_OF_COMPACT_SESSION_FILE -1074395776 // You can not use a compact classification file with read options other than Read All.
+#define ERR_ROI_HAS_OPEN_CONTOURS -1074395775 // The ROI you passed in may only contain closed contours.
+#define ERR_NO_LABEL -1074395774 // You must pass in a label.
+#define ERR_NO_DEST_IMAGE -1074395773 // You must provide a destination image.
+#define ERR_INVALID_REGISTRATION_METHOD -1074395772 // You provided an invalid registration method.
+#define ERR_OPENING_NEWER_INSPECTION_TEMPLATE -1074395771 // The golden template you tried to open was created with a newer version of NI Vision. Upgrade to the latest version of NI Vision to read this file.
+#define ERR_INVALID_INSPECTION_TEMPLATE -1074395770 // Invalid golden template.
+#define ERR_INVALID_EDGE_THICKNESS -1074395769 // Edge Thickness to Ignore must be greater than zero.
+#define ERR_INVALID_SCALE -1074395768 // Scale must be greater than zero.
+#define ERR_INVALID_ALIGNMENT -1074395767 // The supplied scale is invalid for your template.
+#define ERR_DEPRECATED_FUNCTION -1074395766 // This backwards-compatibility function can not be used with this session. Use newer, supported functions instead.
+#define ERR_INVALID_NORMALIZATION_METHOD -1074395763 // You must provide a valid normalization method.
+#define ERR_INVALID_NIBLACK_DEVIATION_FACTOR -1074395762 // The deviation factor for Niblack local threshold must be between 0 and 1.
+#define ERR_BOARD_NOT_FOUND -1074395760 // Board not found.
+#define ERR_BOARD_NOT_OPEN -1074395758 // Board not opened.
+#define ERR_DLL_NOT_FOUND -1074395757 // DLL not found.
+#define ERR_DLL_FUNCTION_NOT_FOUND -1074395756 // DLL function not found.
+#define ERR_TRIG_TIMEOUT -1074395754 // Trigger timeout.
+#define ERR_CONTOUR_INVALID_REFINEMENTS -1074395746 // Invalid number specified for maximum contour refinements.
+#define ERR_TOO_MANY_CURVES -1074395745 // Too many curves extracted from image. Raise the edge threshold or reduce the ROI.
+#define ERR_CONTOUR_INVALID_KERNEL_FOR_SMOOTHING -1074395744 // Invalid kernel for contour smoothing. Zero indicates no smoothing, otherwise value must be odd.
+#define ERR_CONTOUR_LINE_INVALID -1074395743 // The contour line fit is invalid. Line segment start and stop must differ.
+#define ERR_CONTOUR_TEMPLATE_IMAGE_INVALID -1074395742 // The template image must be trained with IMAQ Learn Contour Pattern or be the same size as the target image.
+#define ERR_CONTOUR_GPM_FAIL -1074395741 // Matching failed to align the template and target contours.
+#define ERR_CONTOUR_OPENING_NEWER_VERSION -1074395740 // The contour you tried to open was created with a newer version of NI Vision. Upgrade to the latest version of NI Vision to read this file.
+#define ERR_CONTOUR_CONNECT_DUPLICATE -1074395739 // Only one range is allowed per curve connection constraint type.
+#define ERR_CONTOUR_CONNECT_TYPE -1074395738 // Invalid contour connection constraint type.
+#define ERR_CONTOUR_MATCH_STR_NOT_APPLICABLE -1074395737 // In order to use contour matching, you must provide a template image that has been trained with IMAQ Learn Contour Pattern
+#define ERR_CONTOUR_CURVATURE_KERNEL -1074395736 // Invalid kernel width for curvature calculation. Must be an odd value greater than 1.
+#define ERR_CONTOUR_EXTRACT_SELECTION -1074395735 // Invalid Contour Selection method for contour extraction.
+#define ERR_CONTOUR_EXTRACT_DIRECTION -1074395734 // Invalid Search Direction for contour extraction.
+#define ERR_CONTOUR_EXTRACT_ROI -1074395733 // Invalid ROI for contour extraction. The ROI must contain an annulus, rectangle or rotated rectangle.
+#define ERR_CONTOUR_NO_CURVES -1074395732 // No curves were found in the image.
+#define ERR_CONTOUR_COMPARE_KERNEL -1074395731 // Invalid Smoothing Kernel width for contour comparison. Must be zero or an odd positive integer.
+#define ERR_CONTOUR_COMPARE_SINGLE_IMAGE -1074395730 // If no template image is provided, the target image must contain both a contour with extracted points and a fitted equation.
+#define ERR_CONTOUR_INVALID -1074395729 // Invalid contour image.
+#define ERR_INVALID_2D_BARCODE_SEARCH_MODE -1074395728 // NI Vision does not support the search mode you provided.
+#define ERR_UNSUPPORTED_2D_BARCODE_SEARCH_MODE -1074395727 // NI Vision does not support the search mode you provided for the type of 2D barcode for which you are searching.
+#define ERR_MATCHFACTOR_OBSOLETE -1074395726 // matchFactor has been obsoleted. Instead, set the initialMatchListLength and matchListReductionFactor in the MatchPatternAdvancedOptions structure.
+#define ERR_DATA_VERSION -1074395725 // The data was stored with a newer version of NI Vision. Upgrade to the latest version of NI Vision to read this data.
+#define ERR_CUSTOMDATA_INVALID_SIZE -1074395724 // The size you specified is out of the valid range.
+#define ERR_CUSTOMDATA_KEY_NOT_FOUND -1074395723 // The key you specified cannot be found in the image.
+#define ERR_CLASSIFIER_CLASSIFY_IMAGE_WITH_CUSTOM_SESSION -1074395722 // Custom classifier sessions only classify feature vectors. They do not support classifying images.
+#define ERR_INVALID_BIT_DEPTH -1074395721 // NI Vision does not support the bit depth you supplied for the image you supplied.
+#define ERR_BAD_ROI -1074395720 // Invalid ROI.
+#define ERR_BAD_ROI_BOX -1074395719 // Invalid ROI global rectangle.
+#define ERR_LAB_VERSION -1074395718 // The version of LabVIEW or BridgeVIEW you are running does not support this operation.
+#define ERR_INVALID_RANGE -1074395717 // The range you supplied is invalid.
+#define ERR_INVALID_SCALING_METHOD -1074395716 // NI Vision does not support the scaling method you provided.
+#define ERR_INVALID_CALIBRATION_UNIT -1074395715 // NI Vision does not support the calibration unit you supplied.
+#define ERR_INVALID_AXIS_ORIENTATION -1074395714 // NI Vision does not support the axis orientation you supplied.
+#define ERR_VALUE_NOT_IN_ENUM -1074395713 // Value not in enumeration.
+#define ERR_WRONG_REGION_TYPE -1074395712 // You selected a region that is not of the right type.
+#define ERR_NOT_ENOUGH_REGIONS -1074395711 // You specified a viewer that does not contain enough regions.
+#define ERR_TOO_MANY_PARTICLES -1074395710 // The image has too many particles for this process.
+#define ERR_AVI_UNOPENED_SESSION -1074395709 // The AVI session has not been opened.
+#define ERR_AVI_READ_SESSION_REQUIRED -1074395708 // The AVI session is a write session, but this operation requires a read session.
+#define ERR_AVI_WRITE_SESSION_REQUIRED -1074395707 // The AVI session is a read session, but this operation requires a write session.
+#define ERR_AVI_SESSION_ALREADY_OPEN -1074395706 // This AVI session is already open. You must close it before calling the Create or Open functions.
+#define ERR_DATA_CORRUPTED -1074395705 // The data is corrupted and cannot be read.
+#define ERR_INVALID_COMPRESSION_TYPE -1074395704 // Invalid compression type.
+#define ERR_INVALID_TYPE_OF_FLATTEN -1074395703 // Invalid type of flatten.
+#define ERR_INVALID_LENGTH -1074395702 // The length of the edge detection line must be greater than zero.
+#define ERR_INVALID_MATRIX_SIZE_RANGE -1074395701 // The maximum Data Matrix barcode size must be equal to or greater than the minimum Data Matrix barcode size.
+#define ERR_REQUIRES_WIN2000_OR_NEWER -1074395700 // The function requires the operating system to be Microsoft Windows 2000 or newer.
+#define ERR_INVALID_CALIBRATION_METHOD -1074395662 // Invalid calibration method requested
+#define ERR_INVALID_OPERATION_ON_COMPACT_CALIBRATION_ATTEMPTED -1074395661 // This calibration is compact. Re-Learning calibration and retrieving thumbnails are not possible with this calibration
+#define ERR_INVALID_POLYNOMIAL_MODEL_K_COUNT -1074395660 // Invalid number of K values
+#define ERR_INVALID_DISTORTION_MODEL -1074395659 // Invalid distortion model type
+#define ERR_CAMERA_MODEL_NOT_AVAILABLE -1074395658 // Camera Model is not learned
+#define ERR_INVALID_THUMBNAIL_INDEX -1074395657 // Supplied thumbnail index is invalid
+#define ERR_SMOOTH_CONTOURS_MUST_BE_SAME -1074395656 // You must specify the same value for the smooth contours advanced match option for all templates you want to match.
+#define ERR_ENABLE_CALIBRATION_SUPPORT_MUST_BE_SAME -1074395655 // You must specify the same value for the enable calibration support advanced match option for all templates you want to match.
+#define ERR_GRADING_INFORMATION_NOT_FOUND -1074395654 // The source image does not contain grading information. You must prepare the source image for grading when reading the Data Matrix, and you cannot change the contents of the source image between reading and grading the Data Matrix.
+#define ERR_OPENING_NEWER_MULTIPLE_GEOMETRIC_TEMPLATE -1074395653 // The multiple geometric matching template you tried to open was created with a newer version of NI Vision. Upgrade to the latest version of NI Vision to read this file.
+#define ERR_OPENING_NEWER_GEOMETRIC_MATCHING_TEMPLATE -1074395652 // The geometric matching template you tried to open was created with a newer version of NI Vision. Upgrade to the latest version of NI Vision to read this file.
+#define ERR_EDGE_FILTER_SIZE_MUST_BE_SAME -1074395651 // You must specify the same edge filter size for all the templates you want to match.
+#define ERR_CURVE_EXTRACTION_MODE_MUST_BE_SAME -1074395650 // You must specify the same curve extraction mode for all the templates you want to match.
+#define ERR_INVALID_GEOMETRIC_FEATURE_TYPE -1074395649 // The geometric feature type specified is invalid.
+#define ERR_TEMPLATE_NOT_LEARNED -1074395648 // You supplied a template that was not learned.
+#define ERR_INVALID_MULTIPLE_GEOMETRIC_TEMPLATE -1074395647 // Invalid multiple geometric template.
+#define ERR_NO_TEMPLATE_TO_LEARN -1074395646 // Need at least one template to learn.
+#define ERR_INVALID_NUMBER_OF_LABELS -1074395645 // You supplied an invalid number of labels.
+#define ERR_LABEL_TOO_LONG -1074395644 // Labels must be <= 255 characters.
+#define ERR_INVALID_NUMBER_OF_MATCH_OPTIONS -1074395643 // You supplied an invalid number of match options.
+#define ERR_LABEL_NOT_FOUND -1074395642 // Cannot find a label that matches the one you specified.
+#define ERR_DUPLICATE_LABEL -1074395641 // Duplicate labels are not allowed.
+#define ERR_TOO_MANY_ZONES -1074395640 // The number of zones found exceeded the capacity of the algorithm.
+#define ERR_INVALID_HATCH_STYLE -1074395639 // The hatch style for the window background is invalid.
+#define ERR_INVALID_FILL_STYLE -1074395638 // The fill style for the window background is invalid.
+#define ERR_HARDWARE_DOESNT_SUPPORT_NONTEARING -1074395637 // Your hardware is not supported by DirectX and cannot be put into NonTearing mode.
+#define ERR_DIRECTX_NOT_FOUND -1074395636 // DirectX is required for this feature. Please install the latest version..
+#define ERR_INVALID_SHAPE_DESCRIPTOR -1074395635 // The passed shape descriptor is invalid.
+#define ERR_INVALID_MAX_MATCH_OVERLAP -1074395634 // Invalid max match overlap. Values must be between -1 and 100.
+#define ERR_INVALID_MIN_MATCH_SEPARATION_SCALE -1074395633 // Invalid minimum match separation scale. Values must be greater than or equal to -1.
+#define ERR_INVALID_MIN_MATCH_SEPARATION_ANGLE -1074395632 // Invalid minimum match separation angle. Values must be between -1 and 360.
+#define ERR_INVALID_MIN_MATCH_SEPARATION_DISTANCE -1074395631 // Invalid minimum match separation distance. Values must be greater than or equal to -1.
+#define ERR_INVALID_MAXIMUM_FEATURES_LEARNED -1074395630 // Invalid maximum number of features learn. Values must be integers greater than zero.
+#define ERR_INVALID_MAXIMUM_PIXEL_DISTANCE_FROM_LINE -1074395629 // Invalid maximum pixel distance from line. Values must be positive real numbers.
+#define ERR_INVALID_GEOMETRIC_MATCHING_TEMPLATE -1074395628 // Invalid geometric matching template image.
+#define ERR_NOT_ENOUGH_TEMPLATE_FEATURES_1 -1074395627 // The template does not contain enough features for geometric matching.
+#define ERR_NOT_ENOUGH_TEMPLATE_FEATURES -1074395626 // The template does not contain enough features for geometric matching.
+#define ERR_INVALID_MATCH_CONSTRAINT_TYPE -1074395625 // You specified an invalid value for the match constraint value of the range settings.
+#define ERR_INVALID_OCCLUSION_RANGE -1074395624 // Invalid occlusion range. Valid values for the bounds range from 0 to 100 and the upper bound must be greater than or equal to the lower bound.
+#define ERR_INVALID_SCALE_RANGE -1074395623 // Invalid scale range. Values for the lower bound must be a positive real numbers and the upper bound must be greater than or equal to the lower bound.
+#define ERR_INVALID_MATCH_GEOMETRIC_PATTERN_SETUP_DATA -1074395622 // Invalid match geometric pattern setup data.
+#define ERR_INVALID_LEARN_GEOMETRIC_PATTERN_SETUP_DATA -1074395621 // Invalid learn geometric pattern setup data.
+#define ERR_INVALID_CURVE_EXTRACTION_MODE -1074395620 // Invalid curve extraction mode.
+#define ERR_TOO_MANY_OCCLUSION_RANGES -1074395619 // You can specify only one occlusion range.
+#define ERR_TOO_MANY_SCALE_RANGES -1074395618 // You can specify only one scale range.
+#define ERR_INVALID_NUMBER_OF_FEATURES_RANGE -1074395617 // The minimum number of features must be less than or equal to the maximum number of features.
+#define ERR_INVALID_EDGE_FILTER_SIZE -1074395616 // Invalid edge filter size.
+#define ERR_INVALID_MINIMUM_FEATURE_STRENGTH -1074395615 // Invalid minimum strength for features. Values must be positive real numbers.
+#define ERR_INVALID_MINIMUM_FEATURE_ASPECT_RATIO -1074395614 // Invalid aspect ratio for rectangular features. Values must be positive real numbers in the range 0.01 to 1.0.
+#define ERR_INVALID_MINIMUM_FEATURE_LENGTH -1074395613 // Invalid minimum length for linear features. Values must be integers greater than 0.
+#define ERR_INVALID_MINIMUM_FEATURE_RADIUS -1074395612 // Invalid minimum radius for circular features. Values must be integers greater than 0.
+#define ERR_INVALID_MINIMUM_RECTANGLE_DIMENSION -1074395611 // Invalid minimum rectangle dimension. Values must be integers greater than 0.
+#define ERR_INVALID_INITIAL_MATCH_LIST_LENGTH -1074395610 // Invalid initial match list length. Values must be integers greater than 5.
+#define ERR_INVALID_SUBPIXEL_TOLERANCE -1074395609 // Invalid subpixel tolerance. Values must be positive real numbers.
+#define ERR_INVALID_SUBPIXEL_ITERATIONS -1074395608 // Invalid number of subpixel iterations. Values must be integers greater 10.
+#define ERR_INVALID_MAXIMUM_FEATURES_PER_MATCH -1074395607 // Invalid maximum number of features used per match. Values must be integers greater than or equal to zero.
+#define ERR_INVALID_MINIMUM_FEATURES_TO_MATCH -1074395606 // Invalid minimum number of features used for matching. Values must be integers greater than zero.
+#define ERR_INVALID_MAXIMUM_END_POINT_GAP -1074395605 // Invalid maximum end point gap. Valid values range from 0 to 32767.
+#define ERR_INVALID_COLUMN_STEP -1074395604 // Invalid column step. Valid range is 1 to 255.
+#define ERR_INVALID_ROW_STEP -1074395603 // Invalid row step. Valid range is 1 to 255.
+#define ERR_INVALID_MINIMUM_CURVE_LENGTH -1074395602 // Invalid minimum length. Valid values must be greater than or equal to zero.
+#define ERR_INVALID_EDGE_THRESHOLD -1074395601 // Invalid edge threshold. Valid values range from 1 to 360.
+#define ERR_INFO_NOT_FOUND -1074395600 // You must provide information about the subimage within the browser.
+#define ERR_NIOCR_INVALID_ACCEPTANCE_LEVEL -1074395598 // The acceptance level is outside the valid range of 0 to 1000.
+#define ERR_NIOCR_NOT_A_VALID_SESSION -1074395597 // Not a valid OCR session.
+#define ERR_NIOCR_INVALID_CHARACTER_SIZE -1074395596 // Invalid character size. Character size must be >= 1.
+#define ERR_NIOCR_INVALID_THRESHOLD_MODE -1074395595 // Invalid threshold mode value.
+#define ERR_NIOCR_INVALID_SUBSTITUTION_CHARACTER -1074395594 // Invalid substitution character. Valid substitution characters are ASCII values that range from 1 to 254.
+#define ERR_NIOCR_INVALID_NUMBER_OF_BLOCKS -1074395593 // Invalid number of blocks. Number of blocks must be >= 4 and <= 50.
+#define ERR_NIOCR_INVALID_READ_STRATEGY -1074395592 // Invalid read strategy.
+#define ERR_NIOCR_INVALID_CHARACTER_INDEX -1074395591 // Invalid character index.
+#define ERR_NIOCR_INVALID_NUMBER_OF_VALID_CHARACTER_POSITIONS -1074395590 // Invalid number of character positions. Valid values range from 0 to 255.
+#define ERR_NIOCR_INVALID_LOW_THRESHOLD_VALUE -1074395589 // Invalid low threshold value. Valid threshold values range from 0 to 255.
+#define ERR_NIOCR_INVALID_HIGH_THRESHOLD_VALUE -1074395588 // Invalid high threshold value. Valid threshold values range from 0 to 255.
+#define ERR_NIOCR_INVALID_THRESHOLD_RANGE -1074395587 // The low threshold must be less than the high threshold.
+#define ERR_NIOCR_INVALID_LOWER_THRESHOLD_LIMIT -1074395586 // Invalid lower threshold limit. Valid lower threshold limits range from 0 to 255.
+#define ERR_NIOCR_INVALID_UPPER_THRESHOLD_LIMIT -1074395585 // Invalid upper threshold limit. Valid upper threshold limits range from 0 to 255.
+#define ERR_NIOCR_INVALID_THRESHOLD_LIMITS -1074395584 // The lower threshold limit must be less than the upper threshold limit.
+#define ERR_NIOCR_INVALID_MIN_CHAR_SPACING -1074395583 // Invalid minimum character spacing value. Character spacing must be >= 1 pixel.
+#define ERR_NIOCR_INVALID_MAX_HORIZ_ELEMENT_SPACING -1074395582 // Invalid maximum horizontal element spacing value. Maximum horizontal element spacing must be >= 0.
+#define ERR_NIOCR_INVALID_MAX_VERT_ELEMENT_SPACING -1074395581 // Invalid maximum vertical element spacing value. Maximum vertical element spacing must be >= 0.
+#define ERR_NIOCR_INVALID_MIN_BOUNDING_RECT_WIDTH -1074395580 // Invalid minimum bounding rectangle width. Minimum bounding rectangle width must be >= 1.
+#define ERR_NIOCR_INVALID_ASPECT_RATIO -1074395579 // Invalid aspect ratio value. The aspect ratio must be zero or >= 100.
+#define ERR_NIOCR_INVALID_CHARACTER_SET_FILE -1074395578 // Invalid or corrupt character set file.
+#define ERR_NIOCR_CHARACTER_VALUE_CANNOT_BE_EMPTYSTRING -1074395577 // The character value must not be an empty string.
+#define ERR_NIOCR_CHARACTER_VALUE_TOO_LONG -1074395576 // Character values must be <=255 characters.
+#define ERR_NIOCR_INVALID_NUMBER_OF_EROSIONS -1074395575 // Invalid number of erosions. The number of erosions must be >= 0.
+#define ERR_NIOCR_CHARACTER_SET_DESCRIPTION_TOO_LONG -1074395574 // The character set description must be <=255 characters.
+#define ERR_NIOCR_INVALID_CHARACTER_SET_FILE_VERSION -1074395573 // The character set file was created by a newer version of NI Vision. Upgrade to the latest version of NI Vision to read the character set file.
+#define ERR_NIOCR_INTEGER_VALUE_FOR_STRING_ATTRIBUTE -1074395572 // You must specify characters for a string. A string cannot contain integers.
+#define ERR_NIOCR_GET_ONLY_ATTRIBUTE -1074395571 // This attribute is read-only.
+#define ERR_NIOCR_INTEGER_VALUE_FOR_BOOLEAN_ATTRIBUTE -1074395570 // This attribute requires a Boolean value.
+#define ERR_NIOCR_INVALID_ATTRIBUTE -1074395569 // Invalid attribute.
+#define ERR_NIOCR_STRING_VALUE_FOR_INTEGER_ATTRIBUTE -1074395568 // This attribute requires integer values.
+#define ERR_NIOCR_STRING_VALUE_FOR_BOOLEAN_ATTRIBUTE -1074395567 // String values are invalid for this attribute. Enter a boolean value.
+#define ERR_NIOCR_BOOLEAN_VALUE_FOR_INTEGER_ATTRIBUTE -1074395566 // Boolean values are not valid for this attribute. Enter an integer value.
+#define ERR_NIOCR_MUST_BE_SINGLE_CHARACTER -1074395565 // Requires a single-character string.
+#define ERR_NIOCR_INVALID_PREDEFINED_CHARACTER -1074395564 // Invalid predefined character value.
+#define ERR_NIOCR_UNLICENSED -1074395563 // This copy of NI OCR is unlicensed.
+#define ERR_NIOCR_BOOLEAN_VALUE_FOR_STRING_ATTRIBUTE -1074395562 // String values are not valid for this attribute. Enter a Boolean value.
+#define ERR_NIOCR_INVALID_NUMBER_OF_CHARACTERS -1074395561 // The number of characters in the character value must match the number of objects in the image.
+#define ERR_NIOCR_INVALID_OBJECT_INDEX -1074395560 // Invalid object index.
+#define ERR_NIOCR_INVALID_READ_OPTION -1074395559 // Invalid read option.
+#define ERR_NIOCR_INVALID_CHARACTER_SIZE_RANGE -1074395558 // The minimum character size must be less than the maximum character size.
+#define ERR_NIOCR_INVALID_BOUNDING_RECT_WIDTH_RANGE -1074395557 // The minimum character bounding rectangle width must be less than the maximum character bounding rectangle width.
+#define ERR_NIOCR_INVALID_BOUNDING_RECT_HEIGHT_RANGE -1074395556 // The minimum character bounding rectangle height must be less than the maximum character bounding rectangle height.
+#define ERR_NIOCR_INVALID_SPACING_RANGE -1074395555 // The maximum horizontal element spacing value must not exceed the minimum character spacing value.
+#define ERR_NIOCR_INVALID_READ_RESOLUTION -1074395554 // Invalid read resolution.
+#define ERR_NIOCR_INVALID_MIN_BOUNDING_RECT_HEIGHT -1074395553 // Invalid minimum bounding rectangle height. The minimum bounding rectangle height must be >= 1.
+#define ERR_NIOCR_NOT_A_VALID_CHARACTER_SET -1074395552 // Not a valid character set.
+#define ERR_NIOCR_RENAME_REFCHAR -1074395551 // A trained OCR character cannot be renamed while it is a reference character.
+#define ERR_NIOCR_INVALID_CHARACTER_VALUE -1074395550 // A character cannot have an ASCII value of 255.
+#define ERR_NIOCR_INVALID_NUMBER_OF_OBJECTS_TO_VERIFY -1074395549 // The number of objects found does not match the number of expected characters or patterns to verify.
+#define ERR_INVALID_STEREO_BLOCKMATCHING_PREFILTER_CAP -1074395421 // The specified value for the filter cap for block matching is invalid.
+#define ERR_INVALID_STEREO_BLOCKMATCHING_PREFILTER_SIZE -1074395420 // The specified prefilter size for block matching is invalid.
+#define ERR_INVALID_STEREO_BLOCKMATCHING_PREFILTER_TYPE -1074395419 // The specified prefilter type for block matching is invalid.
+#define ERR_INVALID_STEREO_BLOCKMATCHING_NUMDISPARITIES -1074395418 // The specifed value for number of disparities is invalid.
+#define ERR_INVALID_STEREO_BLOCKMATCHING_WINDOW_SIZE -1074395417 // The specified window size for block matching is invalid.
+#define ERR_3DVISION_INVALID_SESSION_TYPE -1074395416 // This 3D vision function cannot be called on this type of 3d vision session.
+#define ERR_TOO_MANY_3DVISION_SESSIONS -1074395415 // There are too many 3D vision sessions open. You must close a session before you can open another one.
+#define ERR_OPENING_NEWER_3DVISION_SESSION -1074395414 // The 3D vision session you tried to open was created with a newer version of NI Vision. Upgrade to the latest version of NI Vision to read this file.
+#define ERR_INVALID_STEREO_BLOCKMATCHING_FILTERTYPE -1074395413 // You have specified an invalid filter type for block matching.
+#define ERR_INVALID_STEREO_CAMERA_POSITION -1074395412 // You have requested results at an invalid camera position in the stereo setup.
+#define ERR_INVALID_3DVISION_SESSION -1074395411 // Not a valid 3D Vision session.
+#define ERR_INVALID_ICONS_PER_LINE -1074395410 // NI Vision does not support less than one icon per line.
+#define ERR_INVALID_SUBPIXEL_DIVISIONS -1074395409 // Invalid subpixel divisions.
+#define ERR_INVALID_DETECTION_MODE -1074395408 // Invalid detection mode.
+#define ERR_INVALID_CONTRAST -1074395407 // Invalid contrast value. Valid contrast values range from 0 to 255.
+#define ERR_COORDSYS_NOT_FOUND -1074395406 // The coordinate system could not be found on this image.
+#define ERR_INVALID_TEXTORIENTATION -1074395405 // NI Vision does not support the text orientation value you supplied.
+#define ERR_INVALID_INTERPOLATIONMETHOD_FOR_UNWRAP -1074395404 // UnwrapImage does not support the interpolation method value you supplied. Valid interpolation methods are zero order and bilinear.
+#define ERR_EXTRAINFO_VERSION -1074395403 // The image was created in a newer version of NI Vision. Upgrade to the latest version of NI Vision to use this image.
+#define ERR_INVALID_MAXPOINTS -1074395402 // The function does not support the maximum number of points that you specified.
+#define ERR_INVALID_MATCHFACTOR -1074395401 // The function does not support the matchFactor that you specified.
+#define ERR_MULTICORE_OPERATION -1074395400 // The operation you have given Multicore Options is invalid. Please see the available enumeration values for Multicore Operation.
+#define ERR_MULTICORE_INVALID_ARGUMENT -1074395399 // You have given Multicore Options an invalid argument.
+#define ERR_COMPLEX_IMAGE_REQUIRED -1074395397 // A complex image is required.
+#define ERR_COLOR_IMAGE_REQUIRED -1074395395 // The input image must be a color image.
+#define ERR_COLOR_SPECTRUM_MASK -1074395394 // The color mask removes too much color information.
+#define ERR_COLOR_TEMPLATE_IMAGE_TOO_SMALL -1074395393 // The color template image is too small.
+#define ERR_COLOR_TEMPLATE_IMAGE_TOO_LARGE -1074395392 // The color template image is too large.
+#define ERR_COLOR_TEMPLATE_IMAGE_HUE_CONTRAST_TOO_LOW -1074395391 // The contrast in the hue plane of the image is too low for learning shape features.
+#define ERR_COLOR_TEMPLATE_IMAGE_LUMINANCE_CONTRAST_TOO_LOW -1074395390 // The contrast in the luminance plane of the image is too low to learn shape features.
+#define ERR_COLOR_LEARN_SETUP_DATA -1074395389 // Invalid color learn setup data.
+#define ERR_COLOR_LEARN_SETUP_DATA_SHAPE -1074395388 // Invalid color learn setup data.
+#define ERR_COLOR_MATCH_SETUP_DATA -1074395387 // Invalid color match setup data.
+#define ERR_COLOR_MATCH_SETUP_DATA_SHAPE -1074395386 // Invalid color match setup data.
+#define ERR_COLOR_ROTATION_REQUIRES_SHAPE_FEATURE -1074395385 // Rotation-invariant color pattern matching requires a feature mode including shape.
+#define ERR_COLOR_TEMPLATE_DESCRIPTOR -1074395384 // Invalid color template image.
+#define ERR_COLOR_TEMPLATE_DESCRIPTOR_1 -1074395383 // Invalid color template image.
+#define ERR_COLOR_TEMPLATE_DESCRIPTOR_2 -1074395382 // Invalid color template image.
+#define ERR_COLOR_TEMPLATE_DESCRIPTOR_3 -1074395381 // Invalid color template image.
+#define ERR_COLOR_TEMPLATE_DESCRIPTOR_4 -1074395380 // Invalid color template image.
+#define ERR_COLOR_TEMPLATE_DESCRIPTOR_5 -1074395379 // Invalid color template image.
+#define ERR_COLOR_TEMPLATE_DESCRIPTOR_6 -1074395378 // Invalid color template image.
+#define ERR_COLOR_TEMPLATE_DESCRIPTOR_SHIFT -1074395377 // Invalid color template image.
+#define ERR_COLOR_TEMPLATE_DESCRIPTOR_NOSHIFT -1074395376 // The color template image does not contain data required for shift-invariant color matching.
+#define ERR_COLOR_TEMPLATE_DESCRIPTOR_SHIFT_1 -1074395375 // Invalid color template image.
+#define ERR_COLOR_TEMPLATE_DESCRIPTOR_SHIFT_2 -1074395374 // Invalid color template image.
+#define ERR_COLOR_TEMPLATE_DESCRIPTOR_ROTATION -1074395373 // Invalid color template image.
+#define ERR_COLOR_TEMPLATE_DESCRIPTOR_NOROTATION -1074395372 // The color template image does not contain data required for rotation-invariant color matching.
+#define ERR_COLOR_TEMPLATE_DESCRIPTOR_ROTATION_1 -1074395371 // Invalid color template image.
+#define ERR_COLOR_TEMPLATE_DESCRIPTOR_ROTATION_2 -1074395370 // Invalid color template image.
+#define ERR_COLOR_TEMPLATE_DESCRIPTOR_ROTATION_3 -1074395369 // Invalid color template image.
+#define ERR_COLOR_TEMPLATE_DESCRIPTOR_ROTATION_4 -1074395368 // Invalid color template image.
+#define ERR_COLOR_TEMPLATE_DESCRIPTOR_ROTATION_5 -1074395367 // Invalid color template image.
+#define ERR_COLOR_TEMPLATE_DESCRIPTOR_NOSHAPE -1074395366 // The color template image does not contain data required for color matching in shape feature mode.
+#define ERR_COLOR_TEMPLATE_DESCRIPTOR_NOSPECTRUM -1074395365 // The color template image does not contain data required for color matching in color feature mode.
+#define ERR_IGNORE_COLOR_SPECTRUM_SET -1074395364 // The ignore color spectra array is invalid.
+#define ERR_INVALID_SUBSAMPLING_RATIO -1074395363 // Invalid subsampling ratio.
+#define ERR_INVALID_WIDTH -1074395362 // Invalid pixel width.
+#define ERR_INVALID_STEEPNESS -1074395361 // Invalid steepness.
+#define ERR_COMPLEX_PLANE -1074395360 // Invalid complex plane.
+#define ERR_INVALID_COLOR_IGNORE_MODE -1074395357 // Invalid color ignore mode.
+#define ERR_INVALID_MIN_MATCH_SCORE -1074395356 // Invalid minimum match score. Acceptable values range from 0 to 1000.
+#define ERR_INVALID_NUM_MATCHES_REQUESTED -1074395355 // Invalid number of matches requested. You must request a minimum of one match.
+#define ERR_INVALID_COLOR_WEIGHT -1074395354 // Invalid color weight. Acceptable values range from 0 to 1000.
+#define ERR_INVALID_SEARCH_STRATEGY -1074395353 // Invalid search strategy.
+#define ERR_INVALID_FEATURE_MODE -1074395352 // Invalid feature mode.
+#define ERR_INVALID_RECT -1074395351 // NI Vision does not support rectangles with negative widths or negative heights.
+#define ERR_INVALID_VISION_INFO -1074395350 // NI Vision does not support the vision information type you supplied.
+#define ERR_INVALID_SKELETONMETHOD -1074395349 // NI Vision does not support the SkeletonMethod value you supplied.
+#define ERR_INVALID_3DPLANE -1074395348 // NI Vision does not support the 3DPlane value you supplied.
+#define ERR_INVALID_3DDIRECTION -1074395347 // NI Vision does not support the 3DDirection value you supplied.
+#define ERR_INVALID_INTERPOLATIONMETHOD_FOR_ROTATE -1074395346 // imaqRotate does not support the InterpolationMethod value you supplied.
+#define ERR_INVALID_FLIPAXIS -1074395345 // NI Vision does not support the axis of symmetry you supplied.
+#define ERR_FILE_FILENAME_NULL -1074395343 // You must pass a valid file name. Do not pass in NULL.
+#define ERR_INVALID_SIZETYPE -1074395340 // NI Vision does not support the SizeType value you supplied.
+#define ERR_UNKNOWN_ALGORITHM -1074395336 // You specified the dispatch status of an unknown algorithm.
+#define ERR_DISPATCH_STATUS_CONFLICT -1074395335 // You are attempting to set the same algorithm to dispatch and to not dispatch. Remove one of the conflicting settings.
+#define ERR_INVALID_CONVERSIONSTYLE -1074395334 // NI Vision does not support the Conversion Method value you supplied.
+#define ERR_INVALID_VERTICAL_TEXT_ALIGNMENT -1074395333 // NI Vision does not support the VerticalTextAlignment value you supplied.
+#define ERR_INVALID_COMPAREFUNCTION -1074395332 // NI Vision does not support the CompareFunction value you supplied.
+#define ERR_INVALID_BORDERMETHOD -1074395331 // NI Vision does not support the BorderMethod value you supplied.
+#define ERR_INVALID_BORDER_SIZE -1074395330 // Invalid border size. Acceptable values range from 0 to 50.
+#define ERR_INVALID_OUTLINEMETHOD -1074395329 // NI Vision does not support the OutlineMethod value you supplied.
+#define ERR_INVALID_INTERPOLATIONMETHOD -1074395328 // NI Vision does not support the InterpolationMethod value you supplied.
+#define ERR_INVALID_SCALINGMODE -1074395327 // NI Vision does not support the ScalingMode value you supplied.
+#define ERR_INVALID_DRAWMODE_FOR_LINE -1074395326 // imaqDrawLineOnImage does not support the DrawMode value you supplied.
+#define ERR_INVALID_DRAWMODE -1074395325 // NI Vision does not support the DrawMode value you supplied.
+#define ERR_INVALID_SHAPEMODE -1074395324 // NI Vision does not support the ShapeMode value you supplied.
+#define ERR_INVALID_FONTCOLOR -1074395323 // NI Vision does not support the FontColor value you supplied.
+#define ERR_INVALID_TEXTALIGNMENT -1074395322 // NI Vision does not support the TextAlignment value you supplied.
+#define ERR_INVALID_MORPHOLOGYMETHOD -1074395321 // NI Vision does not support the MorphologyMethod value you supplied.
+#define ERR_TEMPLATE_EMPTY -1074395320 // The template image is empty.
+#define ERR_INVALID_SUBPIX_TYPE -1074395319 // NI Vision does not support the interpolation type you supplied.
+#define ERR_INSF_POINTS -1074395318 // You supplied an insufficient number of points to perform this operation.
+#define ERR_UNDEF_POINT -1074395317 // You specified a point that lies outside the image.
+#define ERR_INVALID_KERNEL_CODE -1074395316 // Invalid kernel code.
+#define ERR_INEFFICIENT_POINTS -1074395315 // You supplied an inefficient set of points to match the minimum score.
+#define ERR_WRITE_FILE_NOT_SUPPORTED -1074395313 // Writing files is not supported on this device.
+#define ERR_LCD_CALIBRATE -1074395312 // The input image does not seem to be a valid LCD or LED calibration image.
+#define ERR_INVALID_COLOR_SPECTRUM -1074395311 // The color spectrum array you provided has an invalid number of elements or contains an element set to not-a-number (NaN).
+#define ERR_INVALID_PALETTE_TYPE -1074395310 // NI Vision does not support the PaletteType value you supplied.
+#define ERR_INVALID_WINDOW_THREAD_POLICY -1074395309 // NI Vision does not support the WindowThreadPolicy value you supplied.
+#define ERR_INVALID_COLORSENSITIVITY -1074395308 // NI Vision does not support the ColorSensitivity value you supplied.
+#define ERR_PRECISION_NOT_GTR_THAN_0 -1074395307 // The precision parameter must be greater than 0.
+#define ERR_INVALID_TOOL -1074395306 // NI Vision does not support the Tool value you supplied.
+#define ERR_INVALID_REFERENCEMODE -1074395305 // NI Vision does not support the ReferenceMode value you supplied.
+#define ERR_INVALID_MATHTRANSFORMMETHOD -1074395304 // NI Vision does not support the MathTransformMethod value you supplied.
+#define ERR_INVALID_NUM_OF_CLASSES -1074395303 // Invalid number of classes for auto threshold. Acceptable values range from 2 to 256.
+#define ERR_INVALID_THRESHOLDMETHOD -1074395302 // NI Vision does not support the threshold method value you supplied.
+#define ERR_ROI_NOT_2_LINES -1074395301 // The ROI you passed into imaqGetMeterArc must consist of two lines.
+#define ERR_INVALID_METERARCMODE -1074395300 // NI Vision does not support the MeterArcMode value you supplied.
+#define ERR_INVALID_COMPLEXPLANE -1074395299 // NI Vision does not support the ComplexPlane value you supplied.
+#define ERR_COMPLEXPLANE_NOT_REAL_OR_IMAGINARY -1074395298 // You can perform this operation on a real or an imaginary ComplexPlane only.
+#define ERR_INVALID_PARTICLEINFOMODE -1074395297 // NI Vision does not support the ParticleInfoMode value you supplied.
+#define ERR_INVALID_BARCODETYPE -1074395296 // NI Vision does not support the BarcodeType value you supplied.
+#define ERR_INVALID_INTERPOLATIONMETHOD_INTERPOLATEPOINTS -1074395295 // imaqInterpolatePoints does not support the InterpolationMethod value you supplied.
+#define ERR_CONTOUR_INDEX_OUT_OF_RANGE -1074395294 // The contour index you supplied is larger than the number of contours in the ROI.
+#define ERR_CONTOURID_NOT_FOUND -1074395293 // The supplied ContourID did not correlate to a contour inside the ROI.
+#define ERR_POINTS_ARE_COLLINEAR -1074395292 // Do not supply collinear points for this operation.
+#define ERR_SHAPEMATCH_BADIMAGEDATA -1074395291 // Shape Match requires the image to contain only pixel values of 0 or 1.
+#define ERR_SHAPEMATCH_BADTEMPLATE -1074395290 // The template you supplied for ShapeMatch contains no shape information.
+#define ERR_CONTAINER_CAPACITY_EXCEEDED_UINT_MAX -1074395289 // The operation would have exceeded the capacity of an internal container, which is limited to 4294967296 unique elements.
+#define ERR_CONTAINER_CAPACITY_EXCEEDED_INT_MAX -1074395288 // The operation would have exceeded the capacity of an internal container, which is limited to 2147483648 unique elements.
+#define ERR_INVALID_LINE -1074395287 // The line you provided contains two identical points, or one of the coordinate locations for the line is not a number (NaN).
+#define ERR_INVALID_CONCENTRIC_RAKE_DIRECTION -1074395286 // Invalid concentric rake direction.
+#define ERR_INVALID_SPOKE_DIRECTION -1074395285 // Invalid spoke direction.
+#define ERR_INVALID_EDGE_PROCESS -1074395284 // Invalid edge process.
+#define ERR_INVALID_RAKE_DIRECTION -1074395283 // Invalid rake direction.
+#define ERR_CANT_DRAW_INTO_VIEWER -1074395282 // Unable to draw to viewer. You must have the latest version of the control.
+#define ERR_IMAGE_SMALLER_THAN_BORDER -1074395281 // Your image must be larger than its border size for this operation.
+#define ERR_ROI_NOT_RECT -1074395280 // The ROI must only have a single Rectangle contour.
+#define ERR_ROI_NOT_POLYGON -1074395279 // ROI is not a polygon.
+#define ERR_LCD_NOT_NUMERIC -1074395278 // LCD image is not a number.
+#define ERR_BARCODE_CHECKSUM -1074395277 // The decoded barcode information did not pass the checksum test.
+#define ERR_LINES_PARALLEL -1074395276 // You specified parallel lines for the meter ROI.
+#define ERR_INVALID_BROWSER_IMAGE -1074395275 // Invalid browser image.
+#define ERR_DIV_BY_ZERO -1074395270 // Cannot divide by zero.
+#define ERR_NULL_POINTER -1074395269 // Null pointer.
+#define ERR_LINEAR_COEFF -1074395268 // The linear equations are not independent.
+#define ERR_COMPLEX_ROOT -1074395267 // The roots of the equation are complex.
+#define ERR_BARCODE -1074395265 // The barcode does not match the type you specified.
+#define ERR_LCD_NO_SEGMENTS -1074395263 // No lit segment.
+#define ERR_LCD_BAD_MATCH -1074395262 // The LCD does not form a known digit.
+#define ERR_GIP_RANGE -1074395261 // An internal error occurred while attempting to access an invalid coordinate on an image.
+#define ERR_HEAP_TRASHED -1074395260 // An internal memory error occurred.
+#define ERR_BAD_FILTER_WIDTH -1074395258 // The filter width must be odd for the Canny operator.
+#define ERR_INVALID_EDGE_DIR -1074395257 // You supplied an invalid edge direction in the Canny operator.
+#define ERR_EVEN_WINDOW_SIZE -1074395256 // The window size must be odd for the Canny operator.
+#define ERR_INVALID_LEARN_MODE -1074395253 // Invalid learn mode.
+#define ERR_LEARN_SETUP_DATA -1074395252 // Invalid learn setup data.
+#define ERR_INVALID_MATCH_MODE -1074395251 // Invalid match mode.
+#define ERR_MATCH_SETUP_DATA -1074395250 // Invalid match setup data.
+#define ERR_ROTATION_ANGLE_RANGE_TOO_LARGE -1074395249 // At least one range in the array of rotation angle ranges exceeds 360 degrees.
+#define ERR_TOO_MANY_ROTATION_ANGLE_RANGES -1074395248 // The array of rotation angle ranges contains too many ranges.
+#define ERR_TEMPLATE_DESCRIPTOR -1074395247 // Invalid template descriptor.
+#define ERR_TEMPLATE_DESCRIPTOR_1 -1074395246 // Invalid template descriptor.
+#define ERR_TEMPLATE_DESCRIPTOR_2 -1074395245 // Invalid template descriptor.
+#define ERR_TEMPLATE_DESCRIPTOR_3 -1074395244 // Invalid template descriptor.
+#define ERR_TEMPLATE_DESCRIPTOR_4 -1074395243 // The template descriptor was created with a newer version of NI Vision. Upgrade to the latest version of NI Vision to use this template.
+#define ERR_TEMPLATE_DESCRIPTOR_ROTATION -1074395242 // Invalid template descriptor.
+#define ERR_TEMPLATE_DESCRIPTOR_NOROTATION -1074395241 // The template descriptor does not contain data required for rotation-invariant matching.
+#define ERR_TEMPLATE_DESCRIPTOR_ROTATION_1 -1074395240 // Invalid template descriptor.
+#define ERR_TEMPLATE_DESCRIPTOR_SHIFT -1074395239 // Invalid template descriptor.
+#define ERR_TEMPLATE_DESCRIPTOR_NOSHIFT -1074395238 // The template descriptor does not contain data required for shift-invariant matching.
+#define ERR_TEMPLATE_DESCRIPTOR_SHIFT_1 -1074395237 // Invalid template descriptor.
+#define ERR_TEMPLATE_DESCRIPTOR_NOSCALE -1074395236 // The template descriptor does not contain data required for scale-invariant matching.
+#define ERR_TEMPLATE_IMAGE_CONTRAST_TOO_LOW -1074395235 // The template image does not contain enough contrast.
+#define ERR_TEMPLATE_IMAGE_TOO_SMALL -1074395234 // The template image is too small.
+#define ERR_TEMPLATE_IMAGE_TOO_LARGE -1074395233 // The template image is too large.
+#define ERR_TOO_MANY_OCR_SESSIONS -1074395214 // There are too many OCR sessions open. You must close a session before you can open another one.
+#define ERR_OCR_TEMPLATE_WRONG_SIZE -1074395212 // The size of the template string must match the size of the string you are trying to correct.
+#define ERR_OCR_BAD_TEXT_TEMPLATE -1074395211 // The supplied text template contains nonstandard characters that cannot be generated by OCR.
+#define ERR_OCR_CANNOT_MATCH_TEXT_TEMPLATE -1074395210 // At least one character in the text template was of a lexical class that did not match the supplied character reports.
+#define ERR_OCR_LIB_INIT -1074395203 // The OCR library cannot be initialized correctly.
+#define ERR_OCR_LOAD_LIBRARY -1074395201 // There was a failure when loading one of the internal OCR engine or LabView libraries.
+#define ERR_OCR_INVALID_PARAMETER -1074395200 // One of the parameters supplied to the OCR function that generated this error is invalid.
+#define ERR_MARKER_INFORMATION_NOT_SUPPLIED -1074395199 // Marker image and points are not supplied
+#define ERR_INCOMPATIBLE_MARKER_IMAGE_SIZE -1074395198 // Source Image and Marker Image should be of same size.
+#define ERR_BOTH_MARKER_INPUTS_SUPPLIED -1074395197 // Both Marker Image and Points are supplied.
+#define ERR_INVALID_MORPHOLOGICAL_OPERATION -1074395196 // Invalid Morphological Operation.
+#define ERR_IMAGE_CONTAINS_NAN_VALUES -1074395195 // Float image contains NaN values
+#define ERR_OVERLAY_EXTRAINFO_OPENING_NEW_VERSION -1074395194 // The overlay information you tried to open was created with a newer version of NI Vision. Upgrade to the latest version of NI Vision to read this file.
+#define ERR_NO_CLAMP_FOUND -1074395193 // No valid clamp was found with the current configuration
+#define ERR_NO_CLAMP_WITHIN_ANGLE_RANGE -1074395192 // Supplied angle range for clamp is insufficient
+#define ERR_GHT_INVALID_USE_ALL_CURVES_VALUE -1074395188 // The use all curves advanced option specified during learn is not supported
+#define ERR_INVALID_GAUSS_SIGMA_VALUE -1074395187 // The sigma value specified for the Gaussian filter is too small.
+#define ERR_INVALID_GAUSS_FILTER_TYPE -1074395186 // The specified Gaussian filter type is not supported.
+#define ERR_INVALID_CONTRAST_REVERSAL_MODE -1074395185 // The contrast reversal mode specified during matching is invalid.
+#define ERR_INVALID_ROTATION_RANGE -1074395184 // Invalid roation angle range. The upper bound must be greater than or equal to the lower bound.
+#define ERR_GHT_INVALID_MINIMUM_LEARN_ANGLE_VALUE -1074395183 // The minimum rotation angle value specifed during learning of the template is not supported.
+#define ERR_GHT_INVALID_MAXIMUM_LEARN_ANGLE_VALUE -1074395182 // The maximum rotation angle value specifed during learning of the template is not supported.
+#define ERR_GHT_INVALID_MAXIMUM_LEARN_SCALE_FACTOR -1074395181 // The maximum scale factor specifed during learning of the template is not supported.
+#define ERR_GHT_INVALID_MINIMUM_LEARN_SCALE_FACTOR -1074395180 // The minimum scale factor specifed during learning of the template is not supported.
+#define ERR_OCR_PREPROCESSING_FAILED -1074395179 // The OCR engine failed during the preprocessing stage.
+#define ERR_OCR_RECOGNITION_FAILED -1074395178 // The OCR engine failed during the recognition stage.
+#define ERR_OCR_BAD_USER_DICTIONARY -1074395175 // The provided filename is not valid user dictionary filename.
+#define ERR_OCR_INVALID_AUTOORIENTMODE -1074395174 // NI Vision does not support the AutoOrientMode value you supplied.
+#define ERR_OCR_INVALID_LANGUAGE -1074395173 // NI Vision does not support the Language value you supplied.
+#define ERR_OCR_INVALID_CHARACTERSET -1074395172 // NI Vision does not support the CharacterSet value you supplied.
+#define ERR_OCR_INI_FILE_NOT_FOUND -1074395171 // The system could not locate the initialization file required for OCR initialization.
+#define ERR_OCR_INVALID_CHARACTERTYPE -1074395170 // NI Vision does not support the CharacterType value you supplied.
+#define ERR_OCR_INVALID_RECOGNITIONMODE -1074395169 // NI Vision does not support the RecognitionMode value you supplied.
+#define ERR_OCR_INVALID_AUTOCORRECTIONMODE -1074395168 // NI Vision does not support the AutoCorrectionMode value you supplied.
+#define ERR_OCR_INVALID_OUTPUTDELIMITER -1074395167 // NI Vision does not support the OutputDelimiter value you supplied.
+#define ERR_OCR_BIN_DIR_NOT_FOUND -1074395166 // The system could not locate the OCR binary directory required for OCR initialization.
+#define ERR_OCR_WTS_DIR_NOT_FOUND -1074395165 // The system could not locate the OCR weights directory required for OCR initialization.
+#define ERR_OCR_ADD_WORD_FAILED -1074395164 // The supplied word could not be added to the user dictionary.
+#define ERR_OCR_INVALID_CHARACTERPREFERENCE -1074395163 // NI Vision does not support the CharacterPreference value you supplied.
+#define ERR_OCR_INVALID_CORRECTIONMODE -1074395162 // NI Vision does not support the CorrectionMethod value you supplied.
+#define ERR_OCR_INVALID_CORRECTIONLEVEL -1074395161 // NI Vision does not support the CorrectionLevel value you supplied.
+#define ERR_OCR_INVALID_MAXPOINTSIZE -1074395160 // NI Vision does not support the maximum point size you supplied. Valid values range from 4 to 72.
+#define ERR_OCR_INVALID_TOLERANCE -1074395159 // NI Vision does not support the tolerance value you supplied. Valid values are non-negative.
+#define ERR_OCR_INVALID_CONTRASTMODE -1074395158 // NI Vision does not support the ContrastMode value you supplied.
+#define ERR_OCR_SKEW_DETECT_FAILED -1074395156 // The OCR attempted to detected the text skew and failed.
+#define ERR_OCR_ORIENT_DETECT_FAILED -1074395155 // The OCR attempted to detected the text orientation and failed.
+#define ERR_FONT_FILE_FORMAT -1074395153 // Invalid font file format.
+#define ERR_FONT_FILE_NOT_FOUND -1074395152 // Font file not found.
+#define ERR_OCR_CORRECTION_FAILED -1074395151 // The OCR engine failed during the correction stage.
+#define ERR_INVALID_ROUNDING_MODE -1074395150 // NI Vision does not support the RoundingMode value you supplied.
+#define ERR_DUPLICATE_TRANSFORM_TYPE -1074395149 // Found a duplicate transform type in the properties array. Each properties array may only contain one behavior for each transform type.
+#define ERR_OVERLAY_GROUP_NOT_FOUND -1074395148 // Overlay Group Not Found.
+#define ERR_BARCODE_RSSLIMITED -1074395147 // The barcode is not a valid RSS Limited symbol
+#define ERR_QR_DETECTION_VERSION -1074395146 // Couldn't determine the correct version of the QR code.
+#define ERR_QR_INVALID_READ -1074395145 // Invalid read of the QR code.
+#define ERR_QR_INVALID_BARCODE -1074395144 // The barcode that was read contains invalid parameters.
+#define ERR_QR_DETECTION_MODE -1074395143 // The data stream that was demodulated could not be read because the mode was not detected.
+#define ERR_QR_DETECTION_MODELTYPE -1074395142 // Couldn't determine the correct model of the QR code.
+#define ERR_OCR_NO_TEXT_FOUND -1074395141 // The OCR engine could not find any text in the supplied region.
+#define ERR_OCR_CHAR_REPORT_CORRUPTED -1074395140 // One of the character reports is no longer usable by the system.
+#define ERR_IMAQ_QR_DIMENSION_INVALID -1074395139 // Invalid Dimensions.
+#define ERR_OCR_REGION_TOO_SMALL -1074395138 // The OCR region provided was too small to have contained any characters.
+#define _FIRST_ERR ERR_SYSTEM_ERROR
+#define _LAST_ERR ERR_OCR_REGION_TOO_SMALL
+
+//============================================================================
+// Enumerated Types
+//============================================================================
+typedef enum PointSymbol_enum {
+ IMAQ_POINT_AS_PIXEL = 0, //A single pixel represents a point in the overlay.
+ IMAQ_POINT_AS_CROSS = 1, //A cross represents a point in the overlay.
+ IMAQ_POINT_USER_DEFINED = 2, //The pattern supplied by the user represents a point in the overlay.
+ IMAQ_POINT_SYMBOL_SIZE_GUARD = 0xFFFFFFFF
+} PointSymbol;
+
+typedef enum MeasurementValue_enum {
+ IMAQ_AREA = 0, //Surface area of the particle in pixels.
+ IMAQ_AREA_CALIBRATED = 1, //Surface area of the particle in calibrated units.
+ IMAQ_NUM_HOLES = 2, //Number of holes in the particle.
+ IMAQ_AREA_OF_HOLES = 3, //Surface area of the holes in calibrated units.
+ IMAQ_TOTAL_AREA = 4, //Total surface area (holes and particle) in calibrated units.
+ IMAQ_IMAGE_AREA = 5, //Surface area of the entire image in calibrated units.
+ IMAQ_PARTICLE_TO_IMAGE = 6, //Ratio, expressed as a percentage, of the surface area of a particle in relation to the total area of the particle.
+ IMAQ_PARTICLE_TO_TOTAL = 7, //Ratio, expressed as a percentage, of the surface area of a particle in relation to the total area of the particle.
+ IMAQ_CENTER_MASS_X = 8, //X-coordinate of the center of mass.
+ IMAQ_CENTER_MASS_Y = 9, //Y-coordinate of the center of mass.
+ IMAQ_LEFT_COLUMN = 10, //Left edge of the bounding rectangle.
+ IMAQ_TOP_ROW = 11, //Top edge of the bounding rectangle.
+ IMAQ_RIGHT_COLUMN = 12, //Right edge of the bounding rectangle.
+ IMAQ_BOTTOM_ROW = 13, //Bottom edge of bounding rectangle.
+ IMAQ_WIDTH = 14, //Width of bounding rectangle in calibrated units.
+ IMAQ_HEIGHT = 15, //Height of bounding rectangle in calibrated units.
+ IMAQ_MAX_SEGMENT_LENGTH = 16, //Length of longest horizontal line segment.
+ IMAQ_MAX_SEGMENT_LEFT_COLUMN = 17, //Leftmost x-coordinate of longest horizontal line segment.
+ IMAQ_MAX_SEGMENT_TOP_ROW = 18, //Y-coordinate of longest horizontal line segment.
+ IMAQ_PERIMETER = 19, //Outer perimeter of the particle.
+ IMAQ_PERIMETER_OF_HOLES = 20, //Perimeter of all holes within the particle.
+ IMAQ_SIGMA_X = 21, //Sum of the particle pixels on the x-axis.
+ IMAQ_SIGMA_Y = 22, //Sum of the particle pixels on the y-axis.
+ IMAQ_SIGMA_XX = 23, //Sum of the particle pixels on the x-axis squared.
+ IMAQ_SIGMA_YY = 24, //Sum of the particle pixels on the y-axis squared.
+ IMAQ_SIGMA_XY = 25, //Sum of the particle pixels on the x-axis and y-axis.
+ IMAQ_PROJ_X = 26, //Projection corrected in X.
+ IMAQ_PROJ_Y = 27, //Projection corrected in Y.
+ IMAQ_INERTIA_XX = 28, //Inertia matrix coefficient in XX.
+ IMAQ_INERTIA_YY = 29, //Inertia matrix coefficient in YY.
+ IMAQ_INERTIA_XY = 30, //Inertia matrix coefficient in XY.
+ IMAQ_MEAN_H = 31, //Mean length of horizontal segments.
+ IMAQ_MEAN_V = 32, //Mean length of vertical segments.
+ IMAQ_MAX_INTERCEPT = 33, //Length of longest segment of the convex hull.
+ IMAQ_MEAN_INTERCEPT = 34, //Mean length of the chords in an object perpendicular to its max intercept.
+ IMAQ_ORIENTATION = 35, //The orientation based on the inertia of the pixels in the particle.
+ IMAQ_EQUIV_ELLIPSE_MINOR = 36, //Total length of the axis of the ellipse having the same area as the particle and a major axis equal to half the max intercept.
+ IMAQ_ELLIPSE_MAJOR = 37, //Total length of major axis having the same area and perimeter as the particle in calibrated units.
+ IMAQ_ELLIPSE_MINOR = 38, //Total length of minor axis having the same area and perimeter as the particle in calibrated units.
+ IMAQ_ELLIPSE_RATIO = 39, //Fraction of major axis to minor axis.
+ IMAQ_RECT_LONG_SIDE = 40, //Length of the long side of a rectangle having the same area and perimeter as the particle in calibrated units.
+ IMAQ_RECT_SHORT_SIDE = 41, //Length of the short side of a rectangle having the same area and perimeter as the particle in calibrated units.
+ IMAQ_RECT_RATIO = 42, //Ratio of rectangle long side to rectangle short side.
+ IMAQ_ELONGATION = 43, //Max intercept/mean perpendicular intercept.
+ IMAQ_COMPACTNESS = 44, //Particle area/(height x width).
+ IMAQ_HEYWOOD = 45, //Particle perimeter/perimeter of the circle having the same area as the particle.
+ IMAQ_TYPE_FACTOR = 46, //A complex factor relating the surface area to the moment of inertia.
+ IMAQ_HYDRAULIC = 47, //Particle area/particle perimeter.
+ IMAQ_WADDLE_DISK = 48, //Diameter of the disk having the same area as the particle in user units.
+ IMAQ_DIAGONAL = 49, //Diagonal of an equivalent rectangle in user units.
+ IMAQ_MEASUREMENT_VALUE_SIZE_GUARD = 0xFFFFFFFF
+} MeasurementValue;
+
+typedef enum ScalingMode_enum {
+ IMAQ_SCALE_LARGER = 0, //The function duplicates pixels to make the image larger.
+ IMAQ_SCALE_SMALLER = 1, //The function subsamples pixels to make the image smaller.
+ IMAQ_SCALING_MODE_SIZE_GUARD = 0xFFFFFFFF
+} ScalingMode;
+
+typedef enum ScalingMethod_enum {
+ IMAQ_SCALE_TO_PRESERVE_AREA = 0, //Correction functions scale the image such that the features in the corrected image have the same area as the features in the input image.
+ IMAQ_SCALE_TO_FIT = 1, //Correction functions scale the image such that the corrected image is the same size as the input image.
+ IMAQ_SCALING_METHOD_SIZE_GUARD = 0xFFFFFFFF
+} ScalingMethod;
+
+typedef enum ReferenceMode_enum {
+ IMAQ_COORD_X_Y = 0, //This method requires three elements in the points array.
+ IMAQ_COORD_ORIGIN_X = 1, //This method requires two elements in the points array.
+ IMAQ_REFERENCE_MODE_SIZE_GUARD = 0xFFFFFFFF
+} ReferenceMode;
+
+typedef enum RectOrientation_enum {
+ IMAQ_BASE_INSIDE = 0, //Specifies that the base of the rectangular image lies along the inside edge of the annulus.
+ IMAQ_BASE_OUTSIDE = 1, //Specifies that the base of the rectangular image lies along the outside edge of the annulus.
+ IMAQ_TEXT_ORIENTATION_SIZE_GUARD = 0xFFFFFFFF
+} RectOrientation;
+
+typedef enum ShapeMode_enum {
+ IMAQ_SHAPE_RECT = 1, //The function draws a rectangle.
+ IMAQ_SHAPE_OVAL = 2, //The function draws an oval.
+ IMAQ_SHAPE_MODE_SIZE_GUARD = 0xFFFFFFFF
+} ShapeMode;
+
+typedef enum PolarityType_enum {
+ IMAQ_EDGE_RISING = 1, //The edge is a rising edge.
+ IMAQ_EDGE_FALLING = -1, //The edge is a falling edge.
+ IMAQ_POLARITY_TYPE_SIZE_GUARD = 0xFFFFFFFF
+} PolarityType;
+
+typedef enum SizeType_enum {
+ IMAQ_KEEP_LARGE = 0, //The function keeps large particles remaining after the erosion.
+ IMAQ_KEEP_SMALL = 1, //The function keeps small particles eliminated by the erosion.
+ IMAQ_SIZE_TYPE_SIZE_GUARD = 0xFFFFFFFF
+} SizeType;
+
+typedef enum Plane3D_enum {
+ IMAQ_3D_REAL = 0, //The function shows the real part of complex images.
+ IMAQ_3D_IMAGINARY = 1, //The function shows the imaginary part of complex images.
+ IMAQ_3D_MAGNITUDE = 2, //The function shows the magnitude part of complex images.
+ IMAQ_3D_PHASE = 3, //The function shows the phase part of complex images.
+ IMAQ_PLANE_3D_SIZE_GUARD = 0xFFFFFFFF
+} Plane3D;
+
+typedef enum PhotometricMode_enum {
+ IMAQ_WHITE_IS_ZERO = 0, //The function interprets zero-value pixels as white.
+ IMAQ_BLACK_IS_ZERO = 1, //The function interprets zero-value pixels as black.
+ IMAQ_PHOTOMETRIC_MODE_SIZE_GUARD = 0xFFFFFFFF
+} PhotometricMode;
+
+typedef enum ParticleInfoMode_enum {
+ IMAQ_BASIC_INFO = 0, //The function returns only the following elements of each report: area, calibratedArea, boundingRect.
+ IMAQ_ALL_INFO = 1, //The function returns all the information about each particle.
+ IMAQ_PARTICLE_INFO_MODE_SIZE_GUARD = 0xFFFFFFFF
+} ParticleInfoMode;
+
+typedef enum OutlineMethod_enum {
+ IMAQ_EDGE_DIFFERENCE = 0, //The function uses a method that produces continuous contours by highlighting each pixel where an intensity variation occurs between itself and its three upper-left neighbors.
+ IMAQ_EDGE_GRADIENT = 1, //The function uses a method that outlines contours where an intensity variation occurs along the vertical axis.
+ IMAQ_EDGE_PREWITT = 2, //The function uses a method that extracts the outer contours of objects.
+ IMAQ_EDGE_ROBERTS = 3, //The function uses a method that outlines the contours that highlight pixels where an intensity variation occurs along the diagonal axes.
+ IMAQ_EDGE_SIGMA = 4, //The function uses a method that outlines contours and details by setting pixels to the mean value found in their neighborhood, if their deviation from this value is not significant.
+ IMAQ_EDGE_SOBEL = 5, //The function uses a method that extracts the outer contours of objects.
+ IMAQ_OUTLINE_METHOD_SIZE_GUARD = 0xFFFFFFFF
+} OutlineMethod;
+
+typedef enum MorphologyMethod_enum {
+ IMAQ_AUTOM = 0, //The function uses a transformation that generates simpler particles that contain fewer details.
+ IMAQ_CLOSE = 1, //The function uses a transformation that fills tiny holes and smooths boundaries.
+ IMAQ_DILATE = 2, //The function uses a transformation that eliminates tiny holes isolated in particles and expands the contour of the particles according to the template defined by the structuring element.
+ IMAQ_ERODE = 3, //The function uses a transformation that eliminates pixels isolated in the background and erodes the contour of particles according to the template defined by the structuring element.
+ IMAQ_GRADIENT = 4, //The function uses a transformation that leaves only the pixels that would be added by the dilation process or eliminated by the erosion process.
+ IMAQ_GRADIENTOUT = 5, //The function uses a transformation that leaves only the pixels that would be added by the dilation process.
+ IMAQ_GRADIENTIN = 6, //The function uses a transformation that leaves only the pixels that would be eliminated by the erosion process.
+ IMAQ_HITMISS = 7, //The function uses a transformation that extracts each pixel located in a neighborhood exactly matching the template defined by the structuring element.
+ IMAQ_OPEN = 8, //The function uses a transformation that removes small particles and smooths boundaries.
+ IMAQ_PCLOSE = 9, //The function uses a transformation that fills tiny holes and smooths the inner contour of particles according to the template defined by the structuring element.
+ IMAQ_POPEN = 10, //The function uses a transformation that removes small particles and smooths the contour of particles according to the template defined by the structuring element.
+ IMAQ_THICK = 11, //The function uses a transformation that adds to an image those pixels located in a neighborhood that matches a template specified by the structuring element.
+ IMAQ_THIN = 12, //The function uses a transformation that eliminates pixels that are located in a neighborhood matching a template specified by the structuring element.
+ IMAQ_MORPHOLOGY_METHOD_SIZE_GUARD = 0xFFFFFFFF
+} MorphologyMethod;
+
+typedef enum MeterArcMode_enum {
+ IMAQ_METER_ARC_ROI = 0, //The function uses the roi parameter and ignores the base, start, and end parameters.
+ IMAQ_METER_ARC_POINTS = 1, //The function uses the base,start, and end parameters and ignores the roi parameter.
+ IMAQ_METER_ARC_MODE_SIZE_GUARD = 0xFFFFFFFF
+} MeterArcMode;
+
+typedef enum RakeDirection_enum {
+ IMAQ_LEFT_TO_RIGHT = 0, //The function searches from the left side of the search area to the right side of the search area.
+ IMAQ_RIGHT_TO_LEFT = 1, //The function searches from the right side of the search area to the left side of the search area.
+ IMAQ_TOP_TO_BOTTOM = 2, //The function searches from the top side of the search area to the bottom side of the search area.
+ IMAQ_BOTTOM_TO_TOP = 3, //The function searches from the bottom side of the search area to the top side of the search area.
+ IMAQ_RAKE_DIRECTION_SIZE_GUARD = 0xFFFFFFFF
+} RakeDirection;
+
+typedef enum TruncateMode_enum {
+ IMAQ_TRUNCATE_LOW = 0, //The function truncates low frequencies.
+ IMAQ_TRUNCATE_HIGH = 1, //The function truncates high frequencies.
+ IMAQ_TRUNCATE_MODE_SIZE_GUARD = 0xFFFFFFFF
+} TruncateMode;
+
+typedef enum AttenuateMode_enum {
+ IMAQ_ATTENUATE_LOW = 0, //The function attenuates low frequencies.
+ IMAQ_ATTENUATE_HIGH = 1, //The function attenuates high frequencies.
+ IMAQ_ATTENUATE_MODE_SIZE_GUARD = 0xFFFFFFFF
+} AttenuateMode;
+
+typedef enum WindowThreadPolicy_enum {
+ IMAQ_CALLING_THREAD = 0, //Using this policy, NI Vision creates windows in the thread that makes the first display function call for a given window number.
+ IMAQ_SEPARATE_THREAD = 1, //Using this policy, NI Vision creates windows in a separate thread and processes messages for the windows automatically.
+ IMAQ_WINDOW_THREAD_POLICY_SIZE_GUARD = 0xFFFFFFFF
+} WindowThreadPolicy;
+
+typedef enum WindowOptions_enum {
+ IMAQ_WIND_RESIZABLE = 1, //When present, the user may resize the window interactively.
+ IMAQ_WIND_TITLEBAR = 2, //When present, the title bar on the window is visible.
+ IMAQ_WIND_CLOSEABLE = 4, //When present, the close box is available.
+ IMAQ_WIND_TOPMOST = 8, //When present, the window is always on top.
+ IMAQ_WINDOW_OPTIONS_SIZE_GUARD = 0xFFFFFFFF
+} WindowOptions;
+
+typedef enum WindowEventType_enum {
+ IMAQ_NO_EVENT = 0, //No event occurred since the last call to imaqGetLastEvent().
+ IMAQ_CLICK_EVENT = 1, //The user clicked on a window.
+ IMAQ_DRAW_EVENT = 2, //The user drew an ROI in a window.
+ IMAQ_MOVE_EVENT = 3, //The user moved a window.
+ IMAQ_SIZE_EVENT = 4, //The user sized a window.
+ IMAQ_SCROLL_EVENT = 5, //The user scrolled a window.
+ IMAQ_ACTIVATE_EVENT = 6, //The user activated a window.
+ IMAQ_CLOSE_EVENT = 7, //The user closed a window.
+ IMAQ_DOUBLE_CLICK_EVENT = 8, //The user double-clicked in a window.
+ IMAQ_WINDOW_EVENT_TYPE_SIZE_GUARD = 0xFFFFFFFF
+} WindowEventType;
+
+typedef enum VisionInfoType_enum {
+ IMAQ_ANY_VISION_INFO = 0, //The function checks if any extra vision information is associated with the image.
+ IMAQ_PATTERN_MATCHING_INFO = 1, //The function checks if any pattern matching template information is associated with the image.
+ IMAQ_CALIBRATION_INFO = 2, //The function checks if any calibration information is associated with the image.
+ IMAQ_OVERLAY_INFO = 3, //The function checks if any overlay information is associated with the image.
+ IMAQ_VISION_INFO_TYPE_SIZE_GUARD = 0xFFFFFFFF
+} VisionInfoType;
+
+typedef enum SearchStrategy_enum {
+ IMAQ_CONSERVATIVE = 1, //Instructs the pattern matching algorithm to use the largest possible amount of information from the image at the expense of slowing down the speed of the algorithm.
+ IMAQ_BALANCED = 2, //Instructs the pattern matching algorithm to balance the amount of information from the image it uses with the speed of the algorithm.
+ IMAQ_AGGRESSIVE = 3, //Instructs the pattern matching algorithm to use a lower amount of information from the image, which allows the algorithm to run quickly but at the expense of accuracy.
+ IMAQ_VERY_AGGRESSIVE = 4, //Instructs the pattern matching algorithm to use the smallest possible amount of information from the image, which allows the algorithm to run at the highest speed possible but at the expense of accuracy.
+ IMAQ_SEARCH_STRATEGY_SIZE_GUARD = 0xFFFFFFFF
+} SearchStrategy;
+
+typedef enum TwoEdgePolarityType_enum {
+ IMAQ_NONE = 0, //The function ignores the polarity of the edges.
+ IMAQ_RISING_FALLING = 1, //The polarity of the first edge is rising (dark to light) and the polarity of the second edge is falling (light to dark).
+ IMAQ_FALLING_RISING = 2, //The polarity of the first edge is falling (light to dark) and the polarity of the second edge is rising (dark to light).
+ IMAQ_RISING_RISING = 3, //The polarity of the first edge is rising (dark to light) and the polarity of the second edge is rising (dark to light).
+ IMAQ_FALLING_FALLING = 4, //The polarity of the first edge is falling (light to dark) and the polarity of the second edge is falling (light to dark).
+ IMAQ_TWO_EDGE_POLARITY_TYPE_SIZE_GUARD = 0xFFFFFFFF
+} TwoEdgePolarityType;
+
+typedef enum ObjectType_enum {
+ IMAQ_BRIGHT_OBJECTS = 0, //The function detects bright objects.
+ IMAQ_DARK_OBJECTS = 1, //The function detects dark objects.
+ IMAQ_OBJECT_TYPE_SIZE_GUARD = 0xFFFFFFFF
+} ObjectType;
+
+typedef enum Tool_enum {
+ IMAQ_NO_TOOL = -1, //No tool is in the selected state.
+ IMAQ_SELECTION_TOOL = 0, //The selection tool selects an existing ROI in an image.
+ IMAQ_POINT_TOOL = 1, //The point tool draws a point on the image.
+ IMAQ_LINE_TOOL = 2, //The line tool draws a line on the image.
+ IMAQ_RECTANGLE_TOOL = 3, //The rectangle tool draws a rectangle on the image.
+ IMAQ_OVAL_TOOL = 4, //The oval tool draws an oval on the image.
+ IMAQ_POLYGON_TOOL = 5, //The polygon tool draws a polygon on the image.
+ IMAQ_CLOSED_FREEHAND_TOOL = 6, //The closed freehand tool draws closed freehand shapes on the image.
+ IMAQ_ANNULUS_TOOL = 7, //The annulus tool draws annuluses on the image.
+ IMAQ_ZOOM_TOOL = 8, //The zoom tool controls the zoom of an image.
+ IMAQ_PAN_TOOL = 9, //The pan tool shifts the view of the image.
+ IMAQ_POLYLINE_TOOL = 10, //The polyline tool draws a series of connected straight lines on the image.
+ IMAQ_FREEHAND_TOOL = 11, //The freehand tool draws freehand lines on the image.
+ IMAQ_ROTATED_RECT_TOOL = 12, //The rotated rectangle tool draws rotated rectangles on the image.
+ IMAQ_ZOOM_OUT_TOOL = 13, //The zoom out tool controls the zoom of an image.
+ IMAQ_TOOL_SIZE_GUARD = 0xFFFFFFFF
+} Tool;
+
+typedef enum TIFFCompressionType_enum {
+ IMAQ_NO_COMPRESSION = 0, //The function does not compress the TIFF file.
+ IMAQ_JPEG = 1, //The function uses the JPEG compression algorithm to compress the TIFF file.
+ IMAQ_RUN_LENGTH = 2, //The function uses a run length compression algorithm to compress the TIFF file.
+ IMAQ_ZIP = 3, //The function uses the ZIP compression algorithm to compress the TIFF file.
+ IMAQ_TIFF_COMPRESSION_TYPE_SIZE_GUARD = 0xFFFFFFFF
+} TIFFCompressionType;
+
+typedef enum ThresholdMethod_enum {
+ IMAQ_THRESH_CLUSTERING = 0, //The function uses a method that sorts the histogram of the image within a discrete number of classes corresponding to the number of phases perceived in an image.
+ IMAQ_THRESH_ENTROPY = 1, //The function uses a method that is best for detecting particles that are present in minuscule proportions on the image.
+ IMAQ_THRESH_METRIC = 2, //The function uses a method that is well-suited for images in which classes are not too disproportionate.
+ IMAQ_THRESH_MOMENTS = 3, //The function uses a method that is suited for images that have poor contrast.
+ IMAQ_THRESH_INTERCLASS = 4, //The function uses a method that is well-suited for images in which classes have well separated pixel value distributions.
+ IMAQ_THRESHOLD_METHOD_SIZE_GUARD = 0xFFFFFFFF
+} ThresholdMethod;
+
+typedef enum TextAlignment_enum {
+ IMAQ_LEFT = 0, //Left aligns the text at the reference point.
+ IMAQ_CENTER = 1, //Centers the text around the reference point.
+ IMAQ_RIGHT = 2, //Right aligns the text at the reference point.
+ IMAQ_TEXT_ALIGNMENT_SIZE_GUARD = 0xFFFFFFFF
+} TextAlignment;
+
+typedef enum SpokeDirection_enum {
+ IMAQ_OUTSIDE_TO_INSIDE = 0, //The function searches from the outside of the search area to the inside of the search area.
+ IMAQ_INSIDE_TO_OUTSIDE = 1, //The function searches from the inside of the search area to the outside of the search area.
+ IMAQ_SPOKE_DIRECTION_SIZE_GUARD = 0xFFFFFFFF
+} SpokeDirection;
+
+typedef enum SkeletonMethod_enum {
+ IMAQ_SKELETON_L = 0, //Uses an L-shaped structuring element in the skeleton function.
+ IMAQ_SKELETON_M = 1, //Uses an M-shaped structuring element in the skeleton function.
+ IMAQ_SKELETON_INVERSE = 2, //Uses an L-shaped structuring element on an inverse of the image in the skeleton function.
+ IMAQ_SKELETON_METHOD_SIZE_GUARD = 0xFFFFFFFF
+} SkeletonMethod;
+
+typedef enum VerticalTextAlignment_enum {
+ IMAQ_BOTTOM = 0, //Aligns the bottom of the text at the reference point.
+ IMAQ_TOP = 1, //Aligns the top of the text at the reference point.
+ IMAQ_BASELINE = 2, //Aligns the baseline of the text at the reference point.
+ IMAQ_VERTICAL_TEXT_ALIGNMENT_SIZE_GUARD = 0xFFFFFFFF
+} VerticalTextAlignment;
+
+typedef enum CalibrationROI_enum {
+ IMAQ_FULL_IMAGE = 0, //The correction function corrects the whole image, regardless of the user-defined or calibration-defined ROIs.
+ IMAQ_CALIBRATION_ROI = 1, //The correction function corrects the area defined by the calibration ROI.
+ IMAQ_USER_ROI = 2, //The correction function corrects the area defined by the user-defined ROI.
+ IMAQ_CALIBRATION_AND_USER_ROI = 3, //The correction function corrects the area defined by the intersection of the user-defined ROI and the calibration ROI.
+ IMAQ_CALIBRATION_OR_USER_ROI = 4, //The correction function corrects the area defined by the union of the user-defined ROI and the calibration ROI.
+ IMAQ_CALIBRATION_ROI_SIZE_GUARD = 0xFFFFFFFF
+} CalibrationROI;
+
+typedef enum ContourType_enum {
+ IMAQ_EMPTY_CONTOUR = 0, //The contour is empty.
+ IMAQ_POINT = 1, //The contour represents a point.
+ IMAQ_LINE = 2, //The contour represents a line.
+ IMAQ_RECT = 3, //The contour represents a rectangle.
+ IMAQ_OVAL = 4, //The contour represents an oval.
+ IMAQ_CLOSED_CONTOUR = 5, //The contour represents a series of connected points where the last point connects to the first.
+ IMAQ_OPEN_CONTOUR = 6, //The contour represents a series of connected points where the last point does not connect to the first.
+ IMAQ_ANNULUS = 7, //The contour represents an annulus.
+ IMAQ_ROTATED_RECT = 8, //The contour represents a rotated rectangle.
+ IMAQ_CONTOUR_TYPE_SIZE_GUARD = 0xFFFFFFFF
+} ContourType;
+
+typedef enum MathTransformMethod_enum {
+ IMAQ_TRANSFORM_LINEAR = 0, //The function uses linear remapping.
+ IMAQ_TRANSFORM_LOG = 1, //The function uses logarithmic remapping.
+ IMAQ_TRANSFORM_EXP = 2, //The function uses exponential remapping.
+ IMAQ_TRANSFORM_SQR = 3, //The function uses square remapping.
+ IMAQ_TRANSFORM_SQRT = 4, //The function uses square root remapping.
+ IMAQ_TRANSFORM_POWX = 5, //The function uses power X remapping.
+ IMAQ_TRANSFORM_POW1X = 6, //The function uses power 1/X remapping.
+ IMAQ_MATH_TRANSFORM_METHOD_SIZE_GUARD = 0xFFFFFFFF
+} MathTransformMethod;
+
+typedef enum ComplexPlane_enum {
+ IMAQ_REAL = 0, //The function operates on the real plane of the complex image.
+ IMAQ_IMAGINARY = 1, //The function operates on the imaginary plane of the complex image.
+ IMAQ_MAGNITUDE = 2, //The function operates on the magnitude plane of the complex image.
+ IMAQ_PHASE = 3, //The function operates on the phase plane of the complex image.
+ IMAQ_COMPLEX_PLANE_SIZE_GUARD = 0xFFFFFFFF
+} ComplexPlane;
+
+typedef enum PaletteType_enum {
+ IMAQ_PALETTE_GRAY = 0, //The function uses a palette that has a gradual gray-level variation from black to white.
+ IMAQ_PALETTE_BINARY = 1, //The function uses a palette of 16 cycles of 16 different colors that is useful with binary images.
+ IMAQ_PALETTE_GRADIENT = 2, //The function uses a palette that has a gradation from red to white with a prominent range of light blue in the upper value range.
+ IMAQ_PALETTE_RAINBOW = 3, //The function uses a palette that has a gradation from blue to red with a prominent range of greens in the middle value range.
+ IMAQ_PALETTE_TEMPERATURE = 4, //The function uses a palette that has a gradation from light brown to dark brown.
+ IMAQ_PALETTE_USER = 5, //The function uses a palette defined by the user.
+ IMAQ_PALETTE_TYPE_SIZE_GUARD = 0xFFFFFFFF
+} PaletteType;
+
+typedef enum ColorSensitivity_enum {
+ IMAQ_SENSITIVITY_LOW = 0, //Instructs the algorithm to divide the hue plane into a low number of sectors, allowing for simple color analysis.
+ IMAQ_SENSITIVITY_MED = 1, //Instructs the algorithm to divide the hue plane into a medium number of sectors, allowing for color analysis that balances sensitivity and complexity.
+ IMAQ_SENSITIVITY_HIGH = 2, //Instructs the algorithm to divide the hue plane into a high number of sectors, allowing for complex, sensitive color analysis.
+ IMAQ_COLOR_SENSITIVITY_SIZE_GUARD = 0xFFFFFFFF
+} ColorSensitivity;
+
+typedef enum ColorMode_enum {
+ IMAQ_RGB = 0, //The function operates in the RGB (Red, Blue, Green) color space.
+ IMAQ_HSL = 1, //The function operates in the HSL (Hue, Saturation, Luminance) color space.
+ IMAQ_HSV = 2, //The function operates in the HSV (Hue, Saturation, Value) color space.
+ IMAQ_HSI = 3, //The function operates in the HSI (Hue, Saturation, Intensity) color space.
+ IMAQ_CIE = 4, //The function operates in the CIE L*a*b* color space.
+ IMAQ_CIEXYZ = 5, //The function operates in the CIE XYZ color space.
+ IMAQ_COLOR_MODE_SIZE_GUARD = 0xFFFFFFFF
+} ColorMode;
+
+typedef enum DetectionMode_enum {
+ IMAQ_DETECT_PEAKS = 0, //The function detects peaks.
+ IMAQ_DETECT_VALLEYS = 1, //The function detects valleys.
+ IMAQ_DETECTION_MODE_SIZE_GUARD = 0xFFFFFFFF
+} DetectionMode;
+
+typedef enum CalibrationUnit_enum {
+ IMAQ_UNDEFINED = 0, //The image does not have a defined unit of measurement.
+ IMAQ_ANGSTROM = 1, //The unit of measure for the image is angstroms.
+ IMAQ_MICROMETER = 2, //The unit of measure for the image is micrometers.
+ IMAQ_MILLIMETER = 3, //The unit of measure for the image is millimeters.
+ IMAQ_CENTIMETER = 4, //The unit of measure for the image is centimeters.
+ IMAQ_METER = 5, //The unit of measure for the image is meters.
+ IMAQ_KILOMETER = 6, //The unit of measure for the image is kilometers.
+ IMAQ_MICROINCH = 7, //The unit of measure for the image is microinches.
+ IMAQ_INCH = 8, //The unit of measure for the image is inches.
+ IMAQ_FOOT = 9, //The unit of measure for the image is feet.
+ IMAQ_NAUTICMILE = 10, //The unit of measure for the image is nautical miles.
+ IMAQ_GROUNDMILE = 11, //The unit of measure for the image is ground miles.
+ IMAQ_STEP = 12, //The unit of measure for the image is steps.
+ IMAQ_CALIBRATION_UNIT_SIZE_GUARD = 0xFFFFFFFF
+} CalibrationUnit;
+
+typedef enum ConcentricRakeDirection_enum {
+ IMAQ_COUNTER_CLOCKWISE = 0, //The function searches the search area in a counter-clockwise direction.
+ IMAQ_CLOCKWISE = 1, //The function searches the search area in a clockwise direction.
+ IMAQ_CONCENTRIC_RAKE_DIRECTION_SIZE_GUARD = 0xFFFFFFFF
+} ConcentricRakeDirection;
+
+typedef enum CalibrationMode_enum {
+ IMAQ_PERSPECTIVE = 0, //Functions correct for distortion caused by the camera's perspective.
+ IMAQ_NONLINEAR = 1, //Functions correct for distortion caused by the camera's lens.
+ IMAQ_SIMPLE_CALIBRATION = 2, //Functions do not correct for distortion.
+ IMAQ_CORRECTED_IMAGE = 3, //The image is already corrected.
+ IMAQ_CALIBRATION_MODE_SIZE_GUARD = 0xFFFFFFFF
+} CalibrationMode;
+
+typedef enum BrowserLocation_enum {
+ IMAQ_INSERT_FIRST_FREE = 0, //Inserts the thumbnail in the first available cell.
+ IMAQ_INSERT_END = 1, //Inserts the thumbnail after the last occupied cell.
+ IMAQ_BROWSER_LOCATION_SIZE_GUARD = 0xFFFFFFFF
+} BrowserLocation;
+
+typedef enum BrowserFrameStyle_enum {
+ IMAQ_RAISED_FRAME = 0, //Each thumbnail has a raised frame.
+ IMAQ_BEVELLED_FRAME = 1, //Each thumbnail has a beveled frame.
+ IMAQ_OUTLINE_FRAME = 2, //Each thumbnail has an outlined frame.
+ IMAQ_HIDDEN_FRAME = 3, //Each thumbnail has a hidden frame.
+ IMAQ_STEP_FRAME = 4, //Each thumbnail has a stepped frame.
+ IMAQ_RAISED_OUTLINE_FRAME = 5, //Each thumbnail has a raised, outlined frame.
+ IMAQ_BROWSER_FRAME_STYLE_SIZE_GUARD = 0xFFFFFFFF
+} BrowserFrameStyle;
+
+typedef enum BorderMethod_enum {
+ IMAQ_BORDER_MIRROR = 0, //Symmetrically copies pixel values from the image into the border.
+ IMAQ_BORDER_COPY = 1, //Copies the value of the pixel closest to the edge of the image into the border.
+ IMAQ_BORDER_CLEAR = 2, //Sets all pixels in the border to 0.
+ IMAQ_BORDER_METHOD_SIZE_GUARD = 0xFFFFFFFF
+} BorderMethod;
+
+typedef enum BarcodeType_enum {
+ IMAQ_INVALID = -1, //The barcode is not of a type known by NI Vision.
+ IMAQ_CODABAR = 1, //The barcode is of type Codabar.
+ IMAQ_CODE39 = 2, //The barcode is of type Code 39.
+ IMAQ_CODE93 = 4, //The barcode is of type Code 93.
+ IMAQ_CODE128 = 8, //The barcode is of type Code 128.
+ IMAQ_EAN8 = 16, //The barcode is of type EAN 8.
+ IMAQ_EAN13 = 32, //The barcode is of type EAN 13.
+ IMAQ_I2_OF_5 = 64, //The barcode is of type Code 25.
+ IMAQ_MSI = 128, //The barcode is of type MSI code.
+ IMAQ_UPCA = 256, //The barcode is of type UPC A.
+ IMAQ_PHARMACODE = 512, //The barcode is of type Pharmacode.
+ IMAQ_RSS_LIMITED = 1024, //The barcode is of type RSS Limited.
+ IMAQ_BARCODE_TYPE_SIZE_GUARD = 0xFFFFFFFF
+} BarcodeType;
+
+typedef enum AxisOrientation_enum {
+ IMAQ_DIRECT = 0, //The y-axis direction corresponds to the y-axis direction of the Cartesian coordinate system.
+ IMAQ_INDIRECT = 1, //The y-axis direction corresponds to the y-axis direction of an image.
+ IMAQ_AXIS_ORIENTATION_SIZE_GUARD = 0xFFFFFFFF
+} AxisOrientation;
+
+typedef enum ColorIgnoreMode_enum {
+ IMAQ_IGNORE_NONE = 0, //Specifies that the function does not ignore any pixels.
+ IMAQ_IGNORE_BLACK = 1, //Specifies that the function ignores black pixels.
+ IMAQ_IGNORE_WHITE = 2, //Specifies that the function ignores white pixels.
+ IMAQ_IGNORE_BLACK_AND_WHITE = 3, //Specifies that the function ignores black pixels and white pixels.
+ IMAQ_BLACK_WHITE_IGNORE_MODE_SIZE_GUARD = 0xFFFFFFFF
+} ColorIgnoreMode;
+
+typedef enum LevelType_enum {
+ IMAQ_ABSOLUTE = 0, //The function evaluates the threshold and hysteresis values as absolute values.
+ IMAQ_RELATIVE = 1, //The function evaluates the threshold and hysteresis values relative to the dynamic range of the given path.
+ IMAQ_LEVEL_TYPE_SIZE_GUARD = 0xFFFFFFFF
+} LevelType;
+
+typedef enum MatchingMode_enum {
+ IMAQ_MATCH_SHIFT_INVARIANT = 1, //Searches for occurrences of the template image anywhere in the searchRect, assuming that the pattern is not rotated more than plus or minus 4 degrees.
+ IMAQ_MATCH_ROTATION_INVARIANT = 2, //Searches for occurrences of the pattern in the image with no restriction on the rotation of the pattern.
+ IMAQ_MATCHING_MODE_SIZE_GUARD = 0xFFFFFFFF
+} MatchingMode;
+
+typedef enum MappingMethod_enum {
+ IMAQ_FULL_DYNAMIC = 0, //(Obsolete) When the image bit depth is 0, the function maps the full dynamic range of the 16-bit image to an 8-bit scale.
+ IMAQ_DOWNSHIFT = 1, //(Obsolete) When the image bit depth is 0, the function shifts the 16-bit image pixels to the right the number of times specified by the shiftCount element of the DisplayMapping structure.
+ IMAQ_RANGE = 2, //(Obsolete) When the image bit depth is 0, the function maps the pixel values in the range specified by the minimumValue and maximumValue elements of the DisplayMapping structure to an 8-bit scale.
+ IMAQ_90_PCT_DYNAMIC = 3, //(Obsolete) When the image bit depth to 0, the function maps the dynamic range containing the middle 90 percent of the cumulated histogram of the image to an 8-bit (256 grayscale values) scale.
+ IMAQ_PERCENT_RANGE = 4, //(Obsolete) When the image bit depth is 0, the function maps the pixel values in the relative percentage range (0 to 100) of the cumulated histogram specified by minimumValue and maximumValue to an 8-bit scale.
+ IMAQ_DEFAULT_MAPPING = 10, //If the bit depth is 0, the function maps the 16-bit image to 8 bits by following the IMAQ_FULL_DYNAMIC_ALWAYS behavior; otherwise, the function shifts the image data to the right according to the IMAQ_MOST_SIGNIFICANT behavior.
+ IMAQ_MOST_SIGNIFICANT = 11, //The function shifts the 16-bit image pixels to the right until the 8 most significant bits of the image data are remaining.
+ IMAQ_FULL_DYNAMIC_ALWAYS = 12, //The function maps the full dynamic range of the 16-bit image to an 8-bit scale.
+ IMAQ_DOWNSHIFT_ALWAYS = 13, //The function shifts the 16-bit image pixels to the right the number of times specified by the shiftCount element of the DisplayMapping structure.
+ IMAQ_RANGE_ALWAYS = 14, //The function maps the pixel values in the range specified by the minimumValue and maximumValue elements of the DisplayMapping structure to an 8-bit scale.
+ IMAQ_90_PCT_DYNAMIC_ALWAYS = 15, //The function maps the dynamic range containing the middle 90 percent of the cumulated histogram of the image to an 8-bit (256 grayscale values) scale.
+ IMAQ_PERCENT_RANGE_ALWAYS = 16, //The function maps the pixel values in the relative percentage range (0 to 100) of the cumulated histogram specified by minimumValue and maximumValue to an 8-bit scale.
+ IMAQ_MAPPING_METHOD_SIZE_GUARD = 0xFFFFFFFF
+} MappingMethod;
+
+typedef enum ComparisonFunction_enum {
+ IMAQ_CLEAR_LESS = 0, //The comparison is true if the source pixel value is less than the comparison image pixel value.
+ IMAQ_CLEAR_LESS_OR_EQUAL = 1, //The comparison is true if the source pixel value is less than or equal to the comparison image pixel value.
+ IMAQ_CLEAR_EQUAL = 2, //The comparison is true if the source pixel value is equal to the comparison image pixel value.
+ IMAQ_CLEAR_GREATER_OR_EQUAL = 3, //The comparison is true if the source pixel value is greater than or equal to the comparison image pixel value.
+ IMAQ_CLEAR_GREATER = 4, //The comparison is true if the source pixel value is greater than the comparison image pixel value.
+ IMAQ_COMPARE_FUNCTION_SIZE_GUARD = 0xFFFFFFFF
+} ComparisonFunction;
+
+typedef enum LineGaugeMethod_enum {
+ IMAQ_EDGE_TO_EDGE = 0, //Measures from the first edge on the line to the last edge on the line.
+ IMAQ_EDGE_TO_POINT = 1, //Measures from the first edge on the line to the end point of the line.
+ IMAQ_POINT_TO_EDGE = 2, //Measures from the start point of the line to the first edge on the line.
+ IMAQ_POINT_TO_POINT = 3, //Measures from the start point of the line to the end point of the line.
+ IMAQ_LINE_GAUGE_METHOD_SIZE_GUARD = 0xFFFFFFFF
+} LineGaugeMethod;
+
+typedef enum Direction3D_enum {
+ IMAQ_3D_NW = 0, //The viewing angle for the 3D image is from the northwest.
+ IMAQ_3D_SW = 1, //The viewing angle for the 3D image is from the southwest.
+ IMAQ_3D_SE = 2, //The viewing angle for the 3D image is from the southeast.
+ IMAQ_3D_NE = 3, //The viewing angle for the 3D image is from the northeast.
+ IMAQ_DIRECTION_3D_SIZE_GUARD = 0xFFFFFFFF
+} Direction3D;
+
+typedef enum LearningMode_enum {
+ IMAQ_LEARN_ALL = 0, //The function extracts information for shift- and rotation-invariant matching.
+ IMAQ_LEARN_SHIFT_INFORMATION = 1, //The function extracts information for shift-invariant matching.
+ IMAQ_LEARN_ROTATION_INFORMATION = 2, //The function extracts information for rotation-invariant matching.
+ IMAQ_LEARNING_MODE_SIZE_GUARD = 0xFFFFFFFF
+} LearningMode;
+
+typedef enum KernelFamily_enum {
+ IMAQ_GRADIENT_FAMILY = 0, //The kernel is in the gradient family.
+ IMAQ_LAPLACIAN_FAMILY = 1, //The kernel is in the Laplacian family.
+ IMAQ_SMOOTHING_FAMILY = 2, //The kernel is in the smoothing family.
+ IMAQ_GAUSSIAN_FAMILY = 3, //The kernel is in the Gaussian family.
+ IMAQ_KERNEL_FAMILY_SIZE_GUARD = 0xFFFFFFFF
+} KernelFamily;
+
+typedef enum InterpolationMethod_enum {
+ IMAQ_ZERO_ORDER = 0, //The function uses an interpolation method that interpolates new pixel values using the nearest valid neighboring pixel.
+ IMAQ_BILINEAR = 1, //The function uses an interpolation method that interpolates new pixel values using a bidirectional average of the neighboring pixels.
+ IMAQ_QUADRATIC = 2, //The function uses an interpolation method that interpolates new pixel values using a quadratic approximating polynomial.
+ IMAQ_CUBIC_SPLINE = 3, //The function uses an interpolation method that interpolates new pixel values by fitting them to a cubic spline curve, where the curve is based on known pixel values from the image.
+ IMAQ_BILINEAR_FIXED = 4, //The function uses an interpolation method that interpolates new pixel values using a bidirectional average of the neighboring pixels.
+ IMAQ_INTERPOLATION_METHOD_SIZE_GUARD = 0xFFFFFFFF
+} InterpolationMethod;
+
+typedef enum ImageType_enum {
+ IMAQ_IMAGE_U8 = 0, //The image type is 8-bit unsigned integer grayscale.
+ IMAQ_IMAGE_U16 = 7, //The image type is 16-bit unsigned integer grayscale.
+ IMAQ_IMAGE_I16 = 1, //The image type is 16-bit signed integer grayscale.
+ IMAQ_IMAGE_SGL = 2, //The image type is 32-bit floating-point grayscale.
+ IMAQ_IMAGE_COMPLEX = 3, //The image type is complex.
+ IMAQ_IMAGE_RGB = 4, //The image type is RGB color.
+ IMAQ_IMAGE_HSL = 5, //The image type is HSL color.
+ IMAQ_IMAGE_RGB_U64 = 6, //The image type is 64-bit unsigned RGB color.
+ IMAQ_IMAGE_TYPE_SIZE_GUARD = 0xFFFFFFFF
+} ImageType;
+
+typedef enum ImageFeatureMode_enum {
+ IMAQ_COLOR_AND_SHAPE_FEATURES = 0, //Instructs the function to use the color and the shape features of the color pattern.
+ IMAQ_COLOR_FEATURES = 1, //Instructs the function to use the color features of the color pattern.
+ IMAQ_SHAPE_FEATURES = 2, //Instructs the function to use the shape features of the color pattern.
+ IMAQ_FEATURE_MODE_SIZE_GUARD = 0xFFFFFFFF
+} ImageFeatureMode;
+
+typedef enum FontColor_enum {
+ IMAQ_WHITE = 0, //Draws text in white.
+ IMAQ_BLACK = 1, //Draws text in black.
+ IMAQ_INVERT = 2, //Inverts the text pixels.
+ IMAQ_BLACK_ON_WHITE = 3, //Draws text in black with a white background.
+ IMAQ_WHITE_ON_BLACK = 4, //Draws text in white with a black background.
+ IMAQ_FONT_COLOR_SIZE_GUARD = 0xFFFFFFFF
+} FontColor;
+
+typedef enum FlipAxis_enum {
+ IMAQ_HORIZONTAL_AXIS = 0, //Flips the image over the central horizontal axis.
+ IMAQ_VERTICAL_AXIS = 1, //Flips the image over the central vertical axis.
+ IMAQ_CENTER_AXIS = 2, //Flips the image over both the central vertical and horizontal axes.
+ IMAQ_DIAG_L_TO_R_AXIS = 3, //Flips the image over an axis from the upper left corner to lower right corner.
+ IMAQ_DIAG_R_TO_L_AXIS = 4, //Flips the image over an axis from the upper right corner to lower left corner.
+ IMAQ_FLIP_AXIS_SIZE_GUARD = 0xFFFFFFFF
+} FlipAxis;
+
+typedef enum EdgeProcess_enum {
+ IMAQ_FIRST = 0, //The function looks for the first edge.
+ IMAQ_FIRST_AND_LAST = 1, //The function looks for the first and last edge.
+ IMAQ_ALL = 2, //The function looks for all edges.
+ IMAQ_BEST = 3, //The function looks for the best edge.
+ IMAQ_EDGE_PROCESS_SIZE_GUARD = 0xFFFFFFFF
+} EdgeProcess;
+
+typedef enum DrawMode_enum {
+ IMAQ_DRAW_VALUE = 0, //Draws the boundary of the object with the specified pixel value.
+ IMAQ_DRAW_INVERT = 2, //Inverts the pixel values of the boundary of the object.
+ IMAQ_PAINT_VALUE = 1, //Fills the object with the given pixel value.
+ IMAQ_PAINT_INVERT = 3, //Inverts the pixel values of the object.
+ IMAQ_HIGHLIGHT_VALUE = 4, //The function fills the object by highlighting the enclosed pixels with the color of the object.
+ IMAQ_DRAW_MODE_SIZE_GUARD = 0xFFFFFFFF
+} DrawMode;
+
+typedef enum NearestNeighborMetric_enum {
+ IMAQ_METRIC_MAXIMUM = 0, //The maximum metric.
+ IMAQ_METRIC_SUM = 1, //The sum metric.
+ IMAQ_METRIC_EUCLIDEAN = 2, //The Euclidean metric.
+ IMAQ_NEAREST_NEIGHBOR_METRIC_SIZE_GUARD = 0xFFFFFFFF
+} NearestNeighborMetric;
+
+typedef enum ReadResolution_enum {
+ IMAQ_LOW_RESOLUTION = 0, //Configures NI Vision to use low resolution during the read process.
+ IMAQ_MEDIUM_RESOLUTION = 1, //Configures NI Vision to use medium resolution during the read process.
+ IMAQ_HIGH_RESOLUTION = 2, //Configures NI Vision to use high resolution during the read process.
+ IMAQ_READ_RESOLUTION_SIZE_GUARD = 0xFFFFFFFF
+} ReadResolution;
+
+typedef enum ThresholdMode_enum {
+ IMAQ_FIXED_RANGE = 0, //Performs thresholding using the values you provide in the lowThreshold and highThreshold elements of OCRProcessingOptions.
+ IMAQ_COMPUTED_UNIFORM = 1, //Calculates a single threshold value for the entire ROI.
+ IMAQ_COMPUTED_LINEAR = 2, //Calculates a value on the left side of the ROI, calculates a value on the right side of the ROI, and linearly fills the middle values from left to right.
+ IMAQ_COMPUTED_NONLINEAR = 3, //Divides the ROI into the number of blocks specified by the blockCount element of OCRProcessingOptions and calculates a threshold value for each block.
+ IMAQ_THRESHOLD_MODE_SIZE_GUARD = 0xFFFFFFFF
+} ThresholdMode;
+
+typedef enum ReadStrategy_enum {
+ IMAQ_READ_AGGRESSIVE = 0, //Configures NI Vision to perform fewer checks when analyzing objects to determine if they match trained characters.
+ IMAQ_READ_CONSERVATIVE = 1, //Configures NI Vision to perform more checks to determine if an object matches a trained character.
+ IMAQ_READ_STRATEGY_SIZE_GUARD = 0xFFFFFFFF
+} ReadStrategy;
+
+typedef enum MeasurementType_enum {
+ IMAQ_MT_CENTER_OF_MASS_X = 0, //X-coordinate of the point representing the average position of the total particle mass, assuming every point in the particle has a constant density.
+ IMAQ_MT_CENTER_OF_MASS_Y = 1, //Y-coordinate of the point representing the average position of the total particle mass, assuming every point in the particle has a constant density.
+ IMAQ_MT_FIRST_PIXEL_X = 2, //X-coordinate of the highest, leftmost particle pixel.
+ IMAQ_MT_FIRST_PIXEL_Y = 3, //Y-coordinate of the highest, leftmost particle pixel.
+ IMAQ_MT_BOUNDING_RECT_LEFT = 4, //X-coordinate of the leftmost particle point.
+ IMAQ_MT_BOUNDING_RECT_TOP = 5, //Y-coordinate of highest particle point.
+ IMAQ_MT_BOUNDING_RECT_RIGHT = 6, //X-coordinate of the rightmost particle point.
+ IMAQ_MT_BOUNDING_RECT_BOTTOM = 7, //Y-coordinate of the lowest particle point.
+ IMAQ_MT_MAX_FERET_DIAMETER_START_X = 8, //X-coordinate of the start of the line segment connecting the two perimeter points that are the furthest apart.
+ IMAQ_MT_MAX_FERET_DIAMETER_START_Y = 9, //Y-coordinate of the start of the line segment connecting the two perimeter points that are the furthest apart.
+ IMAQ_MT_MAX_FERET_DIAMETER_END_X = 10, //X-coordinate of the end of the line segment connecting the two perimeter points that are the furthest apart.
+ IMAQ_MT_MAX_FERET_DIAMETER_END_Y = 11, //Y-coordinate of the end of the line segment connecting the two perimeter points that are the furthest apart.
+ IMAQ_MT_MAX_HORIZ_SEGMENT_LENGTH_LEFT = 12, //X-coordinate of the leftmost pixel in the longest row of contiguous pixels in the particle.
+ IMAQ_MT_MAX_HORIZ_SEGMENT_LENGTH_RIGHT = 13, //X-coordinate of the rightmost pixel in the longest row of contiguous pixels in the particle.
+ IMAQ_MT_MAX_HORIZ_SEGMENT_LENGTH_ROW = 14, //Y-coordinate of all of the pixels in the longest row of contiguous pixels in the particle.
+ IMAQ_MT_BOUNDING_RECT_WIDTH = 16, //Distance between the x-coordinate of the leftmost particle point and the x-coordinate of the rightmost particle point.
+ IMAQ_MT_BOUNDING_RECT_HEIGHT = 17, //Distance between the y-coordinate of highest particle point and the y-coordinate of the lowest particle point.
+ IMAQ_MT_BOUNDING_RECT_DIAGONAL = 18, //Distance between opposite corners of the bounding rectangle.
+ IMAQ_MT_PERIMETER = 19, //Length of the outer boundary of the particle.
+ IMAQ_MT_CONVEX_HULL_PERIMETER = 20, //Perimeter of the smallest convex polygon containing all points in the particle.
+ IMAQ_MT_HOLES_PERIMETER = 21, //Sum of the perimeters of each hole in the particle.
+ IMAQ_MT_MAX_FERET_DIAMETER = 22, //Distance between the start and end of the line segment connecting the two perimeter points that are the furthest apart.
+ IMAQ_MT_EQUIVALENT_ELLIPSE_MAJOR_AXIS = 23, //Length of the major axis of the ellipse with the same perimeter and area as the particle.
+ IMAQ_MT_EQUIVALENT_ELLIPSE_MINOR_AXIS = 24, //Length of the minor axis of the ellipse with the same perimeter and area as the particle.
+ IMAQ_MT_EQUIVALENT_ELLIPSE_MINOR_AXIS_FERET = 25, //Length of the minor axis of the ellipse with the same area as the particle, and Major Axis equal in length to the Max Feret Diameter.
+ IMAQ_MT_EQUIVALENT_RECT_LONG_SIDE = 26, //Longest side of the rectangle with the same perimeter and area as the particle.
+ IMAQ_MT_EQUIVALENT_RECT_SHORT_SIDE = 27, //Shortest side of the rectangle with the same perimeter and area as the particle.
+ IMAQ_MT_EQUIVALENT_RECT_DIAGONAL = 28, //Distance between opposite corners of the rectangle with the same perimeter and area as the particle.
+ IMAQ_MT_EQUIVALENT_RECT_SHORT_SIDE_FERET = 29, //Shortest side of the rectangle with the same area as the particle, and longest side equal in length to the Max Feret Diameter.
+ IMAQ_MT_AVERAGE_HORIZ_SEGMENT_LENGTH = 30, //Average length of a horizontal segment in the particle.
+ IMAQ_MT_AVERAGE_VERT_SEGMENT_LENGTH = 31, //Average length of a vertical segment in the particle.
+ IMAQ_MT_HYDRAULIC_RADIUS = 32, //The particle area divided by the particle perimeter.
+ IMAQ_MT_WADDEL_DISK_DIAMETER = 33, //Diameter of a disk with the same area as the particle.
+ IMAQ_MT_AREA = 35, //Area of the particle.
+ IMAQ_MT_HOLES_AREA = 36, //Sum of the areas of each hole in the particle.
+ IMAQ_MT_PARTICLE_AND_HOLES_AREA = 37, //Area of a particle that completely covers the image.
+ IMAQ_MT_CONVEX_HULL_AREA = 38, //Area of the smallest convex polygon containing all points in the particle.
+ IMAQ_MT_IMAGE_AREA = 39, //Area of the image.
+ IMAQ_MT_NUMBER_OF_HOLES = 41, //Number of holes in the particle.
+ IMAQ_MT_NUMBER_OF_HORIZ_SEGMENTS = 42, //Number of horizontal segments in the particle.
+ IMAQ_MT_NUMBER_OF_VERT_SEGMENTS = 43, //Number of vertical segments in the particle.
+ IMAQ_MT_ORIENTATION = 45, //The angle of the line that passes through the particle Center of Mass about which the particle has the lowest moment of inertia.
+ IMAQ_MT_MAX_FERET_DIAMETER_ORIENTATION = 46, //The angle of the line segment connecting the two perimeter points that are the furthest apart.
+ IMAQ_MT_AREA_BY_IMAGE_AREA = 48, //Percentage of the particle Area covering the Image Area.
+ IMAQ_MT_AREA_BY_PARTICLE_AND_HOLES_AREA = 49, //Percentage of the particle Area in relation to its Particle and Holes Area.
+ IMAQ_MT_RATIO_OF_EQUIVALENT_ELLIPSE_AXES = 50, //Equivalent Ellipse Major Axis divided by Equivalent Ellipse Minor Axis.
+ IMAQ_MT_RATIO_OF_EQUIVALENT_RECT_SIDES = 51, //Equivalent Rect Long Side divided by Equivalent Rect Short Side.
+ IMAQ_MT_ELONGATION_FACTOR = 53, //Max Feret Diameter divided by Equivalent Rect Short Side (Feret).
+ IMAQ_MT_COMPACTNESS_FACTOR = 54, //Area divided by the product of Bounding Rect Width and Bounding Rect Height.
+ IMAQ_MT_HEYWOOD_CIRCULARITY_FACTOR = 55, //Perimeter divided by the circumference of a circle with the same area.
+ IMAQ_MT_TYPE_FACTOR = 56, //Factor relating area to moment of inertia.
+ IMAQ_MT_SUM_X = 58, //The sum of all x-coordinates in the particle.
+ IMAQ_MT_SUM_Y = 59, //The sum of all y-coordinates in the particle.
+ IMAQ_MT_SUM_XX = 60, //The sum of all x-coordinates squared in the particle.
+ IMAQ_MT_SUM_XY = 61, //The sum of all x-coordinates times y-coordinates in the particle.
+ IMAQ_MT_SUM_YY = 62, //The sum of all y-coordinates squared in the particle.
+ IMAQ_MT_SUM_XXX = 63, //The sum of all x-coordinates cubed in the particle.
+ IMAQ_MT_SUM_XXY = 64, //The sum of all x-coordinates squared times y-coordinates in the particle.
+ IMAQ_MT_SUM_XYY = 65, //The sum of all x-coordinates times y-coordinates squared in the particle.
+ IMAQ_MT_SUM_YYY = 66, //The sum of all y-coordinates cubed in the particle.
+ IMAQ_MT_MOMENT_OF_INERTIA_XX = 68, //The moment of inertia in the x-direction twice.
+ IMAQ_MT_MOMENT_OF_INERTIA_XY = 69, //The moment of inertia in the x and y directions.
+ IMAQ_MT_MOMENT_OF_INERTIA_YY = 70, //The moment of inertia in the y-direction twice.
+ IMAQ_MT_MOMENT_OF_INERTIA_XXX = 71, //The moment of inertia in the x-direction three times.
+ IMAQ_MT_MOMENT_OF_INERTIA_XXY = 72, //The moment of inertia in the x-direction twice and the y-direction once.
+ IMAQ_MT_MOMENT_OF_INERTIA_XYY = 73, //The moment of inertia in the x-direction once and the y-direction twice.
+ IMAQ_MT_MOMENT_OF_INERTIA_YYY = 74, //The moment of inertia in the y-direction three times.
+ IMAQ_MT_NORM_MOMENT_OF_INERTIA_XX = 75, //The normalized moment of inertia in the x-direction twice.
+ IMAQ_MT_NORM_MOMENT_OF_INERTIA_XY = 76, //The normalized moment of inertia in the x- and y-directions.
+ IMAQ_MT_NORM_MOMENT_OF_INERTIA_YY = 77, //The normalized moment of inertia in the y-direction twice.
+ IMAQ_MT_NORM_MOMENT_OF_INERTIA_XXX = 78, //The normalized moment of inertia in the x-direction three times.
+ IMAQ_MT_NORM_MOMENT_OF_INERTIA_XXY = 79, //The normalized moment of inertia in the x-direction twice and the y-direction once.
+ IMAQ_MT_NORM_MOMENT_OF_INERTIA_XYY = 80, //The normalized moment of inertia in the x-direction once and the y-direction twice.
+ IMAQ_MT_NORM_MOMENT_OF_INERTIA_YYY = 81, //The normalized moment of inertia in the y-direction three times.
+ IMAQ_MT_HU_MOMENT_1 = 82, //The first Hu moment.
+ IMAQ_MT_HU_MOMENT_2 = 83, //The second Hu moment.
+ IMAQ_MT_HU_MOMENT_3 = 84, //The third Hu moment.
+ IMAQ_MT_HU_MOMENT_4 = 85, //The fourth Hu moment.
+ IMAQ_MT_HU_MOMENT_5 = 86, //The fifth Hu moment.
+ IMAQ_MT_HU_MOMENT_6 = 87, //The sixth Hu moment.
+ IMAQ_MT_HU_MOMENT_7 = 88, //The seventh Hu moment.
+ IMAQ_MEASUREMENT_TYPE_SIZE_GUARD = 0xFFFFFFFF
+} MeasurementType;
+
+typedef enum GeometricMatchingMode_enum {
+ IMAQ_GEOMETRIC_MATCH_SHIFT_INVARIANT = 0, //Searches for occurrences of the pattern in the image, assuming that the pattern is not rotated more than plus or minus 5 degrees.
+ IMAQ_GEOMETRIC_MATCH_ROTATION_INVARIANT = 1, //Searches for occurrences of the pattern in the image with reduced restriction on the rotation of the pattern.
+ IMAQ_GEOMETRIC_MATCH_SCALE_INVARIANT = 2, //Searches for occurrences of the pattern in the image with reduced restriction on the size of the pattern.
+ IMAQ_GEOMETRIC_MATCH_OCCLUSION_INVARIANT = 4, //Searches for occurrences of the pattern in the image, allowing for a specified percentage of the pattern to be occluded.
+ IMAQ_GEOMETRIC_MATCHING_MODE_SIZE_GUARD = 0xFFFFFFFF
+} GeometricMatchingMode;
+
+typedef enum ButtonLabel_enum {
+ IMAQ_BUTTON_OK = 0, //The label "OK".
+ IMAQ_BUTTON_SAVE = 1, //The label "Save".
+ IMAQ_BUTTON_SELECT = 2, //The label "Select".
+ IMAQ_BUTTON_LOAD = 3, //The label "Load".
+ IMAQ_BUTTON_LABEL_SIZE_GUARD = 0xFFFFFFFF
+} ButtonLabel;
+
+typedef enum NearestNeighborMethod_enum {
+ IMAQ_MINIMUM_MEAN_DISTANCE = 0, //The minimum mean distance method.
+ IMAQ_K_NEAREST_NEIGHBOR = 1, //The k-nearest neighbor method.
+ IMAQ_NEAREST_PROTOTYPE = 2, //The nearest prototype method.
+ IMAQ_NEAREST_NEIGHBOR_METHOD_SIZE_GUARD = 0xFFFFFFFF
+} NearestNeighborMethod;
+
+typedef enum QRMirrorMode_enum {
+ IMAQ_QR_MIRROR_MODE_AUTO_DETECT = -2, //The function should determine if the QR code is mirrored.
+ IMAQ_QR_MIRROR_MODE_MIRRORED = 1, //The function should expect the QR code to appear mirrored.
+ IMAQ_QR_MIRROR_MODE_NORMAL = 0, //The function should expect the QR code to appear normal.
+ IMAQ_QR_MIRROR_MODE_SIZE_GUARD = 0xFFFFFFFF
+} QRMirrorMode;
+
+typedef enum ColumnProcessingMode_enum {
+ IMAQ_AVERAGE_COLUMNS = 0, //Averages the data extracted for edge detection.
+ IMAQ_MEDIAN_COLUMNS = 1, //Takes the median of the data extracted for edge detection.
+ IMAQ_COLUMN_PROCESSING_MODE_SIZE_GUARD = 0xFFFFFFFF
+} ColumnProcessingMode;
+
+typedef enum FindReferenceDirection_enum {
+ IMAQ_LEFT_TO_RIGHT_DIRECT = 0, //Searches from the left side of the search area to the right side of the search area for a direct axis.
+ IMAQ_LEFT_TO_RIGHT_INDIRECT = 1, //Searches from the left side of the search area to the right side of the search area for an indirect axis.
+ IMAQ_TOP_TO_BOTTOM_DIRECT = 2, //Searches from the top of the search area to the bottom of the search area for a direct axis.
+ IMAQ_TOP_TO_BOTTOM_INDIRECT = 3, //Searches from the top of the search area to the bottom of the search area for an indirect axis.
+ IMAQ_RIGHT_TO_LEFT_DIRECT = 4, //Searches from the right side of the search area to the left side of the search area for a direct axis.
+ IMAQ_RIGHT_TO_LEFT_INDIRECT = 5, //Searches from the right side of the search area to the left side of the search area for an indirect axis.
+ IMAQ_BOTTOM_TO_TOP_DIRECT = 6, //Searches from the bottom of the search area to the top of the search area for a direct axis.
+ IMAQ_BOTTOM_TO_TOP_INDIRECT = 7, //Searches from the bottom of the search area to the top of the search area for an indirect axis.
+ IMAQ_FIND_COORD_SYS_DIR_SIZE_GUARD = 0xFFFFFFFF
+} FindReferenceDirection;
+
+typedef enum MulticoreOperation_enum {
+ IMAQ_GET_CORES = 0, //The number of processor cores NI Vision is currently using.
+ IMAQ_SET_CORES = 1, //The number of processor cores for NI Vision to use.
+ IMAQ_USE_MAX_AVAILABLE = 2, //Use the maximum number of available processor cores.
+ IMAQ_MULTICORE_OPERATION_SIZE_GUARD = 0xFFFFFFFF
+} MulticoreOperation;
+
+typedef enum GroupBehavior_enum {
+ IMAQ_GROUP_CLEAR = 0, //Sets the behavior of the overlay group to clear the current settings when an image is transformed.
+ IMAQ_GROUP_KEEP = 1, //Sets the behavior of the overlay group to keep the current settings when an image is transformed.
+ IMAQ_GROUP_TRANSFORM = 2, //Sets the behavior of the overlay group to transform with the image.
+ IMAQ_GROUP_BEHAVIOR_SIZE_GUARD = 0xFFFFFFFF
+} GroupBehavior;
+
+typedef enum QRDimensions_enum {
+ IMAQ_QR_DIMENSIONS_AUTO_DETECT = 0, //The function will automatically determine the dimensions of the QR code.
+ IMAQ_QR_DIMENSIONS_11x11 = 11, //Specifies the dimensions of the QR code as 11 x 11.
+ IMAQ_QR_DIMENSIONS_13x13 = 13, //Specifies the dimensions of the QR code as 13 x 13.
+ IMAQ_QR_DIMENSIONS_15x15 = 15, //Specifies the dimensions of the QR code as 15 x 15.
+ IMAQ_QR_DIMENSIONS_17x17 = 17, //Specifies the dimensions of the QR code as 17 x 17.
+ IMAQ_QR_DIMENSIONS_21x21 = 21, //Specifies the dimensions of the QR code as 21 x 21.
+ IMAQ_QR_DIMENSIONS_25x25 = 25, //Specifies the dimensions of the QR code as 25 x 25.
+ IMAQ_QR_DIMENSIONS_29x29 = 29, //Specifies the dimensions of the QR code as 29 x 29.
+ IMAQ_QR_DIMENSIONS_33x33 = 33, //Specifies the dimensions of the QR code as 33 x 33.
+ IMAQ_QR_DIMENSIONS_37x37 = 37, //Specifies the dimensions of the QR code as 37 x 37.
+ IMAQ_QR_DIMENSIONS_41x41 = 41, //Specifies the dimensions of the QR code as 41 x 41.
+ IMAQ_QR_DIMENSIONS_45x45 = 45, //Specifies the dimensions of the QR code as 45 x 45.
+ IMAQ_QR_DIMENSIONS_49x49 = 49, //Specifies the dimensions of the QR code as 49 x 49.
+ IMAQ_QR_DIMENSIONS_53x53 = 53, //Specifies the dimensions of the QR code as 53 x 53.
+ IMAQ_QR_DIMENSIONS_57x57 = 57, //Specifies the dimensions of the QR code as 57 x 57.
+ IMAQ_QR_DIMENSIONS_61x61 = 61, //Specifies the dimensions of the QR code as 61 x 61.
+ IMAQ_QR_DIMENSIONS_65x65 = 65, //Specifies the dimensions of the QR code as 65 x 65.
+ IMAQ_QR_DIMENSIONS_69x69 = 69, //Specifies the dimensions of the QR code as 69 x 69.
+ IMAQ_QR_DIMENSIONS_73x73 = 73, //Specifies the dimensions of the QR code as 73 x 73.
+ IMAQ_QR_DIMENSIONS_77x77 = 77, //Specifies the dimensions of the QR code as 77 x 77.
+ IMAQ_QR_DIMENSIONS_81x81 = 81, //Specifies the dimensions of the QR code as 81 x 81.
+ IMAQ_QR_DIMENSIONS_85x85 = 85, //Specifies the dimensions of the QR code as 85 x 85.
+ IMAQ_QR_DIMENSIONS_89x89 = 89, //Specifies the dimensions of the QR code as 89 x 89.
+ IMAQ_QR_DIMENSIONS_93x93 = 93, //Specifies the dimensions of the QR code as 93 x 93.
+ IMAQ_QR_DIMENSIONS_97x97 = 97, //Specifies the dimensions of the QR code as 97 x 97.
+ IMAQ_QR_DIMENSIONS_101x101 = 101, //Specifies the dimensions of the QR code as 101 x 101.
+ IMAQ_QR_DIMENSIONS_105x105 = 105, //Specifies the dimensions of the QR code as 105 x 105.
+ IMAQ_QR_DIMENSIONS_109x109 = 109, //Specifies the dimensions of the QR code as 109 x 109.
+ IMAQ_QR_DIMENSIONS_113x113 = 113, //Specifies the dimensions of the QR code as 113 x 113.
+ IMAQ_QR_DIMENSIONS_117x117 = 117, //Specifies the dimensions of the QR code as 117 x 117.
+ IMAQ_QR_DIMENSIONS_121x121 = 121, //Specifies the dimensions of the QR code as 121 x 121.
+ IMAQ_QR_DIMENSIONS_125x125 = 125, //Specifies the dimensions of the QR code as 125 x 125.
+ IMAQ_QR_DIMENSIONS_129x129 = 129, //Specifies the dimensions of the QR code as 129 x 129.
+ IMAQ_QR_DIMENSIONS_133x133 = 133, //Specifies the dimensions of the QR code as 133 x 133.
+ IMAQ_QR_DIMENSIONS_137x137 = 137, //Specifies the dimensions of the QR code as 137 x 137.
+ IMAQ_QR_DIMENSIONS_141x141 = 141, //Specifies the dimensions of the QR code as 141 x 141.
+ IMAQ_QR_DIMENSIONS_145x145 = 145, //Specifies the dimensions of the QR code as 145 x 145.
+ IMAQ_QR_DIMENSIONS_149x149 = 149, //Specifies the dimensions of the QR code as 149 x 149.
+ IMAQ_QR_DIMENSIONS_153x153 = 153, //Specifies the dimensions of the QR code as 153 x 153.
+ IMAQ_QR_DIMENSIONS_157x157 = 157, //Specifies the dimensions of the QR code as 157 x 1537.
+ IMAQ_QR_DIMENSIONS_161x161 = 161, //Specifies the dimensions of the QR code as 161 x 161.
+ IMAQ_QR_DIMENSIONS_165x165 = 165, //Specifies the dimensions of the QR code as 165 x 165.
+ IMAQ_QR_DIMENSIONS_169x169 = 169, //Specifies the dimensions of the QR code as 169 x 169.
+ IMAQ_QR_DIMENSIONS_173x173 = 173, //Specifies the dimensions of the QR code as 173 x 173.
+ IMAQ_QR_DIMENSIONS_177x177 = 177, //Specifies the dimensions of the QR code as 177 x 177.
+ IMAQ_QR_DIMENSIONS_SIZE_GUARD = 0xFFFFFFFF
+} QRDimensions;
+
+typedef enum QRCellFilterMode_enum {
+ IMAQ_QR_CELL_FILTER_MODE_AUTO_DETECT = -2, //The function will try all filter modes and uses the one that decodes the QR code within the fewest iterations and utilizing the least amount of error correction.
+ IMAQ_QR_CELL_FILTER_MODE_AVERAGE = 0, //The function sets the pixel value for the cell to the average of the sampled pixels.
+ IMAQ_QR_CELL_FILTER_MODE_MEDIAN = 1, //The function sets the pixel value for the cell to the median of the sampled pixels.
+ IMAQ_QR_CELL_FILTER_MODE_CENTRAL_AVERAGE = 2, //The function sets the pixel value for the cell to the average of the pixels in the center of the cell sample.
+ IMAQ_QR_CELL_FILTER_MODE_HIGH_AVERAGE = 3, //The function sets the pixel value for the cell to the average value of the half of the sampled pixels with the highest pixel values.
+ IMAQ_QR_CELL_FILTER_MODE_LOW_AVERAGE = 4, //The function sets the pixel value for the cell to the average value of the half of the sampled pixels with the lowest pixel values.
+ IMAQ_QR_CELL_FILTER_MODE_VERY_HIGH_AVERAGE = 5, //The function sets the pixel value for the cell to the average value of the ninth of the sampled pixels with the highest pixel values.
+ IMAQ_QR_CELL_FILTER_MODE_VERY_LOW_AVERAGE = 6, //The function sets the pixel value for the cell to the average value of the ninth of the sampled pixels with the lowest pixel values.
+ IMAQ_QR_CELL_FILTER_MODE_ALL = 8, //The function tries each filter mode, starting with IMAQ_QR_CELL_FILTER_MODE_AVERAGE and ending with IMAQ_QR_CELL_FILTER_MODE_VERY_LOW_AVERAGE, stopping once a filter mode decodes correctly.
+ IMAQ_QR_CELL_FILTER_MODE_SIZE_GUARD = 0xFFFFFFFF
+} QRCellFilterMode;
+
+typedef enum RoundingMode_enum {
+ IMAQ_ROUNDING_MODE_OPTIMIZE = 0, //Rounds the result of a division using the best available method.
+ IMAQ_ROUNDING_MODE_TRUNCATE = 1, //Truncates the result of a division.
+ IMAQ_ROUNDING_MODE_SIZE_GUARD = 0xFFFFFFFF
+} RoundingMode;
+
+typedef enum QRDemodulationMode_enum {
+ IMAQ_QR_DEMODULATION_MODE_AUTO_DETECT = -2, //The function will try each demodulation mode and use the one which decodes the QR code within the fewest iterations and utilizing the least amount of error correction.
+ IMAQ_QR_DEMODULATION_MODE_HISTOGRAM = 0, //The function uses a histogram of all of the QR cells to calculate a threshold.
+ IMAQ_QR_DEMODULATION_MODE_LOCAL_CONTRAST = 1, //The function examines each of the cell's neighbors to determine if the cell is on or off.
+ IMAQ_QR_DEMODULATION_MODE_COMBINED = 2, //The function uses the histogram of the QR code to calculate a threshold.
+ IMAQ_QR_DEMODULATION_MODE_ALL = 3, //The function tries IMAQ_QR_DEMODULATION_MODE_HISTOGRAM, then IMAQ_QR_DEMODULATION_MODE_LOCAL_CONTRAST and then IMAQ_QR_DEMODULATION_MODE_COMBINED, stopping once one mode is successful.
+ IMAQ_QR_DEMODULATION_MODE_SIZE_GUARD = 0xFFFFFFFF
+} QRDemodulationMode;
+
+typedef enum ContrastMode_enum {
+ IMAQ_ORIGINAL_CONTRAST = 0, //Instructs the geometric matching algorithm to find matches with the same contrast as the template.
+ IMAQ_REVERSED_CONTRAST = 1, //Instructs the geometric matching algorithm to find matches with the inverted contrast of the template.
+ IMAQ_BOTH_CONTRASTS = 2, //Instructs the geometric matching algorithm to find matches with the same and inverted contrast of the template.
+} ContrastMode;
+
+typedef enum QRPolarities_enum {
+ IMAQ_QR_POLARITY_AUTO_DETECT = -2, //The function should determine the polarity of the QR code.
+ IMAQ_QR_POLARITY_BLACK_ON_WHITE = 0, //The function should search for a QR code with dark data on a bright background.
+ IMAQ_QR_POLARITY_WHITE_ON_BLACK = 1, //The function should search for a QR code with bright data on a dark background.
+ IMAQ_QR_POLARITY_MODE_SIZE_GUARD = 0xFFFFFFFF
+} QRPolarities;
+
+typedef enum QRRotationMode_enum {
+ IMAQ_QR_ROTATION_MODE_UNLIMITED = 0, //The function allows for unlimited rotation.
+ IMAQ_QR_ROTATION_MODE_0_DEGREES = 1, //The function allows for ??? 5 degrees of rotation.
+ IMAQ_QR_ROTATION_MODE_90_DEGREES = 2, //The function allows for between 85 and 95 degrees of rotation.
+ IMAQ_QR_ROTATION_MODE_180_DEGREES = 3, //The function allows for between 175 and 185 degrees of rotation.
+ IMAQ_QR_ROTATION_MODE_270_DEGREES = 4, //The function allows for between 265 and 275 degrees of rotation.
+ IMAQ_QR_ROTATION_MODE_SIZE_GUARD = 0xFFFFFFFF
+} QRRotationMode;
+
+typedef enum QRGradingMode_enum {
+ IMAQ_QR_NO_GRADING = 0, //The function does not make any preparatory calculations.
+ IMAQ_QR_GRADING_MODE_SIZE_GUARD = 0xFFFFFFFF
+} QRGradingMode;
+
+typedef enum StraightEdgeSearchMode_enum {
+ IMAQ_USE_FIRST_RAKE_EDGES = 0, //Fits a straight edge on the first points detected using a rake.
+ IMAQ_USE_BEST_RAKE_EDGES = 1, //Fits a straight edge on the best points detected using a rake.
+ IMAQ_USE_BEST_HOUGH_LINE = 2, //Finds the strongest straight edge using all points detected on a rake.
+ IMAQ_USE_FIRST_PROJECTION_EDGE = 3, //Uses the location of the first projected edge as the straight edge.
+ IMAQ_USE_BEST_PROJECTION_EDGE = 4, //Finds the strongest projected edge location to determine the straight edge.
+ IMAQ_STRAIGHT_EDGE_SEARCH_SIZE_GUARD = 0xFFFFFFFF
+} StraightEdgeSearchMode;
+
+typedef enum SearchDirection_enum {
+ IMAQ_SEARCH_DIRECTION_LEFT_TO_RIGHT = 0, //Searches from the left side of the search area to the right side of the search area.
+ IMAQ_SEARCH_DIRECTION_RIGHT_TO_LEFT = 1, //Searches from the right side of the search area to the left side of the search area.
+ IMAQ_SEARCH_DIRECTION_TOP_TO_BOTTOM = 2, //Searches from the top side of the search area to the bottom side of the search area.
+ IMAQ_SEARCH_DIRECTION_BOTTOM_TO_TOP = 3, //Searches from the bottom side of the search area to the top side of the search area.
+ IMAQ_SEARCH_DIRECTION_SIZE_GUARD = 0xFFFFFFFF
+} SearchDirection;
+
+typedef enum QRStreamMode_enum {
+ IMAQ_QR_MODE_NUMERIC = 0, //Specifies that the data was encoded using numeric mode.
+ IMAQ_QR_MODE_ALPHANUMERIC = 1, //Specifies that the data was encoded using alpha-numeric mode.
+ IMAQ_QR_MODE_RAW_BYTE = 2, //Specifies that the data was not encoded but is only raw binary bytes, or encoded in JIS-8.
+ IMAQ_QR_MODE_EAN128_TOKEN = 3, //Specifies that the data has a special meaning represented by the application ID.
+ IMAQ_QR_MODE_EAN128_DATA = 4, //Specifies that the data has a special meaning represented by the application ID.
+ IMAQ_QR_MODE_ECI = 5, //Specifies that the data was meant to be read using the language represented in the language ID.
+ IMAQ_QR_MODE_KANJI = 6, //Specifies that the data was encoded in Shift-JIS16 Japanese.
+ IMAQ_QR_MODE_SIZE_GUARD = 0xFFFFFFFF
+} QRStreamMode;
+
+typedef enum ParticleClassifierType_enum {
+ IMAQ_PARTICLE_LARGEST = 0, //Use only the largest particle in the image.
+ IMAQ_PARTICLE_ALL = 1, //Use all particles in the image.
+ IMAQ_PARTICLE_CLASSIFIER_TYPE_SIZE_GUARD = 0xFFFFFFFF
+} ParticleClassifierType;
+
+typedef enum QRCellSampleSize_enum {
+ IMAQ_QR_CELL_SAMPLE_SIZE_AUTO_DETECT = -2, //The function will try each sample size and use the one which decodes the QR code within the fewest iterations and utilizing the least amount of error correction.
+ IMAQ_QR_CELL_SAMPLE_SIZE1X1 = 1, //The function will use a 1x1 sized sample from each cell.
+ IMAQ_QR_CELL_SAMPLE_SIZE2X2 = 2, //The function will use a 2x2 sized sample from each cell.
+ IMAQ_QR_CELL_SAMPLE_SIZE3X3 = 3, //The function will use a 3x3 sized sample from each cell.
+ IMAQ_QR_CELL_SAMPLE_SIZE4X4 = 4, //The function will use a 4x4 sized sample from each cell.
+ IMAQ_QR_CELL_SAMPLE_SIZE5X5 = 5, //The function will use a 5x5 sized sample from each cell.
+ IMAQ_QR_CELL_SAMPLE_SIZE6X6 = 6, //The function will use a 6x6 sized sample from each cell.
+ IMAQ_QR_CELL_SAMPLE_SIZE7X7 = 7, //The function will use a 7x7 sized sample from each cell.
+ IMAQ_QR_CELL_SAMPLE_TYPE_SIZE_GUARD = 0xFFFFFFFF
+} QRCellSampleSize;
+
+typedef enum RakeProcessType_enum {
+ IMAQ_GET_FIRST_EDGES = 0,
+ IMAQ_GET_FIRST_AND_LAST_EDGES = 1,
+ IMAQ_GET_ALL_EDGES = 2,
+ IMAQ_GET_BEST_EDGES = 3,
+ IMAQ_RAKE_PROCESS_TYPE_SIZE_GUARD = 0xFFFFFFFF
+} RakeProcessType;
+
+typedef enum GeometricSetupDataItem_enum {
+ IMAQ_CURVE_EXTRACTION_MODE = 0, //Specifies how the function identifies curves in the image.
+ IMAQ_CURVE_EDGE_THRSHOLD = 1, //Specifies the minimum contrast an edge pixel must have for it to be considered part of a curve.
+ IMAQ_CURVE_EDGE_FILTER = 2, //Specifies the width of the edge filter that the function uses to identify curves in the image.
+ IMAQ_MINIMUM_CURVE_LENGTH = 3, //Specifies the length, in pixels, of the smallest curve that you want the function to identify.
+ IMAQ_CURVE_ROW_SEARCH_STEP_SIZE = 4, //Specifies the distance, in the y direction, between the image rows that the algorithm inspects for curve seed points.
+ IMAQ_CURVE_COL_SEARCH_STEP_SIZE = 5, //Specifies the distance, in the x direction, between the image columns that the algorithm inspects for curve seed points.
+ IMAQ_CURVE_MAX_END_POINT_GAP = 6, //Specifies the maximum gap, in pixels, between the endpoints of a curve that the function identifies as a closed curve.
+ IMAQ_EXTRACT_CLOSED_CURVES = 7, //Specifies whether to identify only closed curves in the image.
+ IMAQ_ENABLE_SUBPIXEL_CURVE_EXTRACTION = 8, //The function ignores this option.
+ IMAQ_ENABLE_CORRELATION_SCORE = 9, //Specifies that the function should calculate the Correlation Score and return it for each match result.
+ IMAQ_ENABLE_SUBPIXEL_ACCURACY = 10, //Determines whether to return the match results with subpixel accuracy.
+ IMAQ_SUBPIXEL_ITERATIONS = 11, //Specifies the maximum number of incremental improvements used to refine matches using subpixel information.
+ IMAQ_SUBPIXEL_TOLERANCE = 12, //Specifies the maximum amount of change, in pixels, between consecutive incremental improvements in the match position before the function stops refining the match position.
+ IMAQ_INITIAL_MATCH_LIST_LENGTH = 13, //Specifies the maximum size of the match list.
+ IMAQ_ENABLE_TARGET_TEMPLATE_CURVESCORE = 14, //Specifies whether the function should calculate the match curve to template curve score and return it for each match result.
+ IMAQ_MINIMUM_MATCH_SEPARATION_DISTANCE = 15, //Specifies the minimum separation distance, in pixels, between the origins of two matches that have unique positions.
+ IMAQ_MINIMUM_MATCH_SEPARATION_ANGLE = 16, //Specifies the minimum angular difference, in degrees, between two matches that have unique angles.
+ IMAQ_MINIMUM_MATCH_SEPARATION_SCALE = 17, //Specifies the minimum difference in scale, expressed as a percentage, between two matches that have unique scales.
+ IMAQ_MAXIMUM_MATCH_OVERLAP = 18, //Specifies whether you want the algorithm to spend less time accurately estimating the location of a match.
+ IMAQ_ENABLE_COARSE_RESULT = 19, //Specifies whether you want the algorithm to spend less time accurately estimating the location of a match.
+ IMAQ_ENABLE_CALIBRATION_SUPPORT = 20, //Specifies whether or not the algorithm treat the inspection image as a calibrated image.
+ IMAQ_ENABLE_CONTRAST_REVERSAL = 21, //Specifies the contrast of the matches to search for.
+ IMAQ_SEARCH_STRATEGY = 22, //Specifies the aggressiveness of the strategy used to find matches in the image.
+ IMAQ_REFINEMENT_MATCH_FACTOR = 23, //Specifies the factor applied to the number of matches requested to determine how many matches are refined in the pyramid stage.
+ IMAQ_SUBPIXEL_MATCH_FACTOR = 24, //Specifies the factor applied to the number for matches requested to determine how many matches are used for the final (subpixel) stage.
+ IMAQ_MAX_REFINEMENT_ITERATIONS = 25, //Specifies maximum refinement iteration.
+} GeometricSetupDataItem;
+
+typedef enum DistortionModel_enum {
+ IMAQ_POLYNOMIAL_MODEL = 0, //Polynomial model.
+ IMAQ_DIVISION_MODEL = 1, //Division Model.
+ IMAQ_NO_DISTORTION_MODEL = -1, //Not a distortion model.
+} DistortionModel;
+
+typedef enum CalibrationThumbnailType_enum {
+ IMAQ_CAMARA_MODEL_TYPE = 0, //Camara model thumbnail type.
+ IMAQ_PERSPECTIVE_TYPE = 1, //Perspective thumbnail type.
+ IMAQ_MICRO_PLANE_TYPE = 2, //Micro Plane thumbnail type.
+ IMAQ_CALIBRATION_THUMBNAIL_TYPE_SIZE_GUARD = 0xFFFFFFFF
+} CalibrationThumbnailType;
+
+typedef enum SettingType_enum {
+ IMAQ_ROTATION_ANGLE_RANGE = 0, //Set a range for this option to specify the angles at which you expect the Function to find template matches in the inspection image.
+ IMAQ_SCALE_RANGE = 1, //Set a range for this option to specify the sizes at which you expect the Function to find template matches in the inspection image.
+ IMAQ_OCCLUSION_RANGE = 2, //Set a range for this option to specify the amount of occlusion you expect for a match in the inspection image.
+ IMAQ_SETTING_TYPE_SIZE_GUARD = 0xFFFFFFFF
+} SettingType;
+
+typedef enum SegmentationDistanceLevel_enum {
+ IMAQ_SEGMENTATION_LEVEL_CONSERVATIVE = 0, //Uses extensive criteria to determine the Maximum Distance.
+ IMAQ_SEGMENTATION_LEVEL_AGGRESSIVE = 1, //Uses few criteria to determine the Maximum Distance.
+ IMAQ_SEGMENTATION_LEVEL_SIZE_GUARD = 0xFFFFFFFF
+} SegmentationDistanceLevel;
+
+typedef enum ExtractContourSelection_enum {
+ IMAQ_CLOSEST = 0, //Selects the curve closest to the ROI.
+ IMAQ_LONGEST = 1, //Selects the longest curve.
+ IMAQ_STRONGEST = 2, //Selects the curve with the highest edge strength averaged from each point on the curve.
+ IMAQ_EXTRACT_CONTOUR_SELECTION_SIZE_GUARD = 0xFFFFFFFF
+} ExtractContourSelection;
+
+typedef enum FindTransformMode_enum {
+ IMAQ_FIND_REFERENCE = 0, //Update both parts of the coordinate system.
+ IMAQ_UPDATE_TRANSFORM = 1, //Update only the new reference system.
+ IMAQ_FIND_TRANSFORM_MODE_SIZE_GUARD = 0xFFFFFFFF
+} FindTransformMode;
+
+typedef enum ExtractContourDirection_enum {
+ IMAQ_RECT_LEFT_RIGHT = 0, //Searches the ROI from left to right.
+ IMAQ_RECT_RIGHT_LEFT = 1, //Searches the ROI from right to left.
+ IMAQ_RECT_TOP_BOTTOM = 2, //Searches the ROI from top to bottom.
+ IMAQ_RECT_BOTTOM_TOP = 3, //Searches the ROI from bottom to top.
+ IMAQ_ANNULUS_INNER_OUTER = 4, //Searches the ROI from the inner radius to the outer radius.
+ IMAQ_ANNULUS_OUTER_INNER = 5, //Searches the ROI from the outer radius to the inner radius.
+ IMAQ_ANNULUS_START_STOP = 6, //Searches the ROI from start angle to end angle.
+ IMAQ_ANNULUS_STOP_START = 7, //Searches the ROI from end angle to start angle.
+ IMAQ_EXTRACT_CONTOUR_DIRECTION_SIZE_GUARD = 0xFFFFFFFF
+} ExtractContourDirection;
+
+typedef enum EdgePolaritySearchMode_enum {
+ IMAQ_SEARCH_FOR_ALL_EDGES = 0, //Searches for all edges.
+ IMAQ_SEARCH_FOR_RISING_EDGES = 1, //Searches for rising edges only.
+ IMAQ_SEARCH_FOR_FALLING_EDGES = 2, //Searches for falling edges only.
+ IMAQ_EDGE_POLARITY_MODE_SIZE_GUARD = 0xFFFFFFFF
+} EdgePolaritySearchMode;
+
+typedef enum Connectivity_enum {
+ IMAQ_FOUR_CONNECTED = 0, //Morphological reconstruction is performed in connectivity mode 4.
+ IMAQ_EIGHT_CONNECTED = 1, //Morphological reconstruction is performed in connectivity mode 8.
+ IMAQ_CONNECTIVITY_SIZE_GUARD = 0xFFFFFFFF
+} Connectivity;
+
+typedef enum MorphologyReconstructOperation_enum {
+ IMAQ_DILATE_RECONSTRUCT = 0, //Performs Reconstruction by dilation.
+ IMAQ_ERODE_RECONSTRUCT = 1, //Performs Reconstruction by erosion.
+ IMAQ_MORPHOLOGY_RECONSTRUCT_OPERATION_SIZE_GUARD = 0xFFFFFFFF
+} MorphologyReconstructOperation;
+
+typedef enum WaveletType_enum {
+ IMAQ_DB02 = 0,
+ IMAQ_DB03 = 1,
+ IMAQ_DB04 = 2, //Specifies the Wavelet Type as DB02.
+ IMAQ_DB05 = 3,
+ IMAQ_DB06 = 4,
+ IMAQ_DB07 = 5,
+ IMAQ_DB08 = 6,
+ IMAQ_DB09 = 7,
+ IMAQ_DB10 = 8,
+ IMAQ_DB11 = 9,
+ IMAQ_DB12 = 10,
+ IMAQ_DB13 = 11,
+ IMAQ_DB14 = 12,
+ IMAQ_HAAR = 13,
+ IMAQ_BIOR1_3 = 14,
+ IMAQ_BIOR1_5 = 15,
+ IMAQ_BIOR2_2 = 16,
+ IMAQ_BIOR2_4 = 17,
+ IMAQ_BIOR2_6 = 18,
+ IMAQ_BIOR2_8 = 19,
+ IMAQ_BIOR3_1 = 20,
+ IMAQ_BIOR3_3 = 21,
+ IMAQ_BIOR3_5 = 22,
+ IMAQ_BIOR3_7 = 23,
+ IMAQ_BIOR3_9 = 24,
+ IMAQ_BIOR4_4 = 25,
+ IMAQ_COIF1 = 26,
+ IMAQ_COIF2 = 27,
+ IMAQ_COIF3 = 28,
+ IMAQ_COIF4 = 29,
+ IMAQ_COIF5 = 30,
+ IMAQ_SYM2 = 31,
+ IMAQ_SYM3 = 32,
+ IMAQ_SYM4 = 33,
+ IMAQ_SYM5 = 34,
+ IMAQ_SYM6 = 35,
+ IMAQ_SYM7 = 36,
+ IMAQ_SYM8 = 37,
+ IMAQ_BIOR5_5 = 38,
+ IMAQ_BIOR6_8 = 39,
+ IMAQ_WAVE_TYPE_SIZE_GUARD = 0xFFFFFFFF
+} WaveletType;
+
+typedef enum ParticleClassifierThresholdType_enum {
+ IMAQ_THRESHOLD_MANUAL = 0, //The classifier performs a manual threshold on the image during preprocessing.
+ IMAQ_THRESHOLD_AUTO = 1, //The classifier performs an auto threshold on the image during preprocessing.
+ IMAQ_THRESHOLD_LOCAL = 2, //The classifier performs a local threshold on the image during preprocessing.
+} ParticleClassifierThresholdType;
+
+typedef enum MeasureParticlesCalibrationMode_enum {
+ IMAQ_CALIBRATION_MODE_PIXEL = 0, //The function takes only pixel measurements on the particles in the image.
+ IMAQ_CALIBRATION_MODE_CALIBRATED = 1, //The function takes only calibrated measurements on the particles in the image.
+ IMAQ_CALIBRATION_MODE_BOTH = 2, //The function takes both pixel and calibrated measurements on the particles in the image.
+ IMAQ_MEASURE_PARTICLES_CALIBRATION_MODE_SIZE_GUARD = 0xFFFFFFFF
+} MeasureParticlesCalibrationMode;
+
+typedef enum GeometricMatchingSearchStrategy_enum {
+ IMAQ_GEOMETRIC_MATCHING_CONSERVATIVE = 0, //Instructs the pattern matching algorithm to use the largest possible amount of information from the image at the expense of slowing down the speed of the algorithm.
+ IMAQ_GEOMETRIC_MATCHING_BALANCED = 1, //Instructs the pattern matching algorithm to balance the amount of information from the image it uses with the speed of the algorithm.
+ IMAQ_GEOMETRIC_MATCHING_AGGRESSIVE = 2, //Instructs the pattern matching algorithm to use a lower amount of information from the image, which allows the algorithm to run quickly but at the expense of accuracy.
+ IMAQ_GEOMETRIC_MATCHING_SEARCH_STRATEGY_SIZE_GUARD = 0xFFFFFFFF
+} GeometricMatchingSearchStrategy;
+
+typedef enum ColorClassificationResolution_enum {
+ IMAQ_CLASSIFIER_LOW_RESOLUTION = 0, //Low resolution version of the color classifier.
+ IMAQ_CLASSIFIER_MEDIUM_RESOLUTION = 1, //Medium resolution version of the color classifier.
+ IMAQ_CLASSIFIER_HIGH_RESOLUTION = 2, //High resolution version of the color classifier.
+ IMAQ_CLASSIFIER_RESOLUTION_SIZE_GUARD = 0xFFFFFFFF
+} ColorClassificationResolution;
+
+typedef enum ConnectionConstraintType_enum {
+ IMAQ_DISTANCE_CONSTRAINT = 0, //Specifies the distance, in pixels, within which the end points of two curves must lie in order to be considered part of a contour.
+ IMAQ_ANGLE_CONSTRAINT = 1, //Specifies the range, in degrees, within which the difference between the angle of two curves, measured at the end points, must lie in order for the two curves to be considered part of a contour.
+ IMAQ_CONNECTIVITY_CONSTRAINT = 2, //Specifies the distance, in pixels, within which a line extended from the end point of a curve must pass the end point of another curve in order for the two curves to be considered part of a contour.
+ IMAQ_GRADIENT_CONSTRAINT = 3, //Specifies the range, in degrees, within which the gradient angles of two curves, measured at the end points, must lie in order for the two curves to be considered part of a contour.
+ IMAQ_NUM_CONNECTION_CONSTRAINT_TYPES = 4, //.
+ IMAQ_CONNECTION_CONSTRAINT_SIZE_GUARD = 0xFFFFFFFF
+} ConnectionConstraintType;
+
+typedef enum Barcode2DContrast_enum {
+ IMAQ_ALL_BARCODE_2D_CONTRASTS = 0, //The function searches for barcodes of each contrast type.
+ IMAQ_BLACK_ON_WHITE_BARCODE_2D = 1, //The function searches for 2D barcodes containing black data on a white background.
+ IMAQ_WHITE_ON_BLACK_BARCODE_2D = 2, //The function searches for 2D barcodes containing white data on a black background.
+ IMAQ_BARCODE_2D_CONTRAST_SIZE_GUARD = 0xFFFFFFFF
+} Barcode2DContrast;
+
+typedef enum QRModelType_enum {
+ IMAQ_QR_MODELTYPE_AUTO_DETECT = 0, //Specifies that the function will auto-detect the type of QR code.
+ IMAQ_QR_MODELTYPE_MICRO = 1, //Specifies the QR code is of a micro type.
+ IMAQ_QR_MODELTYPE_MODEL1 = 2, //Specifies the QR code is of a model1 type.
+ IMAQ_QR_MODELTYPE_MODEL2 = 3, //Specifies the QR code is of a model2 type.
+ IMAQ_QR_MODEL_TYPE_SIZE_GUARD = 0xFFFFFFFF
+} QRModelType;
+
+typedef enum WindowBackgroundFillStyle_enum {
+ IMAQ_FILL_STYLE_SOLID = 0, //Fill the display window with a solid color.
+ IMAQ_FILL_STYLE_HATCH = 2, //Fill the display window with a pattern defined by WindowBackgroundHatchStyle.
+ IMAQ_FILL_STYLE_DEFAULT = 3, //Fill the display window with the NI Vision default pattern.
+ IMAQ_FILL_STYLE_SIZE_GUARD = 0xFFFFFFFF
+} WindowBackgroundFillStyle;
+
+typedef enum ExtractionMode_enum {
+ IMAQ_NORMAL_IMAGE = 0, //Specifies that the function makes no assumptions about the uniformity of objects in the image or the image background.
+ IMAQ_UNIFORM_REGIONS = 1, //Specifies that the function assumes that either the objects in the image or the image background consists of uniform pixel values.
+ IMAQ_EXTRACTION_MODE_SIZE_GUARD = 0xFFFFFFFF
+} ExtractionMode;
+
+typedef enum EdgeFilterSize_enum {
+ IMAQ_FINE = 0, //Specifies that the function uses a fine (narrow) edge filter.
+ IMAQ_NORMAL = 1, //Specifies that the function uses a normal edge filter.
+ IMAQ_CONTOUR_TRACING = 2, //Sets the Edge Filter Size to contour tracing, which provides the best results for contour extraction but increases the time required to process the image.
+ IMAQ_EDGE_FILTER_SIZE_SIZE_GUARD = 0xFFFFFFFF
+} EdgeFilterSize;
+
+typedef enum Barcode2DSearchMode_enum {
+ IMAQ_SEARCH_MULTIPLE = 0, //The function searches for multiple 2D barcodes.
+ IMAQ_SEARCH_SINGLE_CONSERVATIVE = 1, //The function searches for 2D barcodes using the same searching algorithm as IMAQ_SEARCH_MULTIPLE but stops searching after locating one valid barcode.
+ IMAQ_SEARCH_SINGLE_AGGRESSIVE = 2, //The function searches for a single 2D barcode using a method that assumes the barcode occupies a majority of the search region.
+ IMAQ_BARCODE_2D_SEARCH_MODE_SIZE_GUARD = 0xFFFFFFFF
+} Barcode2DSearchMode;
+
+typedef enum DataMatrixSubtype_enum {
+ IMAQ_ALL_DATA_MATRIX_SUBTYPES = 0, //The function searches for Data Matrix barcodes of all subtypes.
+ IMAQ_DATA_MATRIX_SUBTYPES_ECC_000_ECC_140 = 1, //The function searches for Data Matrix barcodes of subtypes ECC 000, ECC 050, ECC 080, ECC 100 and ECC 140.
+ IMAQ_DATA_MATRIX_SUBTYPE_ECC_200 = 2, //The function searches for Data Matrix ECC 200 barcodes.
+ IMAQ_DATA_MATRIX_SUBTYPE_SIZE_GUARD = 0xFFFFFFFF
+} DataMatrixSubtype;
+
+typedef enum FeatureType_enum {
+ IMAQ_NOT_FOUND_FEATURE = 0, //Specifies the feature is not found.
+ IMAQ_CIRCLE_FEATURE = 1, //Specifies the feature is a circle.
+ IMAQ_ELLIPSE_FEATURE = 2, //Specifies the feature is an ellipse.
+ IMAQ_CONST_CURVE_FEATURE = 3, //Specifies the features is a constant curve.
+ IMAQ_RECTANGLE_FEATURE = 4, //Specifies the feature is a rectangle.
+ IMAQ_LEG_FEATURE = 5, //Specifies the feature is a leg.
+ IMAQ_CORNER_FEATURE = 6, //Specifies the feature is a corner.
+ IMAQ_PARALLEL_LINE_PAIR_FEATURE = 7, //Specifies the feature is a parallel line pair.
+ IMAQ_PAIR_OF_PARALLEL_LINE_PAIRS_FEATURE = 8, //Specifies the feature is a pair of parallel line pairs.
+ IMAQ_LINE_FEATURE = 9, //Specifies the feature is a line.
+ IMAQ_CLOSED_CURVE_FEATURE = 10, //Specifies the feature is a closed curve.
+ IMAQ_FEATURE_TYPE_SIZE_GUARD = 0xFFFFFFFF
+} FeatureType;
+
+typedef enum Barcode2DCellShape_enum {
+ IMAQ_SQUARE_CELLS = 0, //The function uses an algorithm for decoding the 2D barcode that works with square data cells.
+ IMAQ_ROUND_CELLS = 1, //The function uses an algorithm for decoding the 2D barcode that works with round data cells.
+ IMAQ_BARCODE_2D_CELL_SHAPE_SIZE_GUARD = 0xFFFFFFFF
+} Barcode2DCellShape;
+
+typedef enum LocalThresholdMethod_enum {
+ IMAQ_NIBLACK = 0, //The function computes thresholds for each pixel based on its local statistics using the Niblack local thresholding algorithm.
+ IMAQ_BACKGROUND_CORRECTION = 1, //The function performs background correction first to eliminate non-uniform lighting effects, then performs thresholding using the Otsu thresholding algorithm.
+ IMAQ_LOCAL_THRESHOLD_METHOD_SIZE_GUARD = 0xFFFFFFFF
+} LocalThresholdMethod;
+
+typedef enum Barcode2DType_enum {
+ IMAQ_PDF417 = 0, //The 2D barcode is of type PDF417.
+ IMAQ_DATA_MATRIX_ECC_000 = 1, //The 2D barcode is of type Data Matrix ECC 000.
+ IMAQ_DATA_MATRIX_ECC_050 = 2, //The 2D barcode is of type Data Matrix ECC 050.
+ IMAQ_DATA_MATRIX_ECC_080 = 3, //The 2D barcode is of type Data Matrix ECC 080.
+ IMAQ_DATA_MATRIX_ECC_100 = 4, //The 2D barcode is of type Data Matrix ECC 100.
+ IMAQ_DATA_MATRIX_ECC_140 = 5, //The 2D barcode is of type Data Matrix ECC 140.
+ IMAQ_DATA_MATRIX_ECC_200 = 6, //The 2D barcode is of type Data Matrix ECC 200.
+ IMAQ_BARCODE_2D_TYPE_SIZE_GUARD = 0xFFFFFFFF
+} Barcode2DType;
+
+typedef enum ClassifierEngineType_enum {
+ IMAQ_ENGINE_NONE = 0, //No engine has been set on this classifier session.
+ IMAQ_ENGINE_NEAREST_NEIGHBOR = 1, //Nearest neighbor engine.
+ IMAQ_ENGINE_SUPPORT_VECTOR_MACHINE = 2,
+ IMAQ_CLASSIFIER_ENGINE_TYPE_SIZE_GUARD = 0xFFFFFFFF
+} ClassifierEngineType;
+
+typedef enum ClassifierType_enum {
+ IMAQ_CLASSIFIER_CUSTOM = 0, //The classifier session classifies vectors of doubles.
+ IMAQ_CLASSIFIER_PARTICLE = 1, //The classifier session classifies particles in binary images.
+ IMAQ_CLASSIFIER_COLOR = 2, //The classifier session classifies an image based on its color.
+ IMAQ_CLASSIFIER_TEXTURE = 3, //The classifier session classifies an image based on its texture.
+ IMAQ_CLASSIFIER_TYPE_SIZE_GUARD = 0xFFFFFFFF
+} ClassifierType;
+
+typedef enum ParticleType_enum {
+ IMAQ_PARTICLE_BRIGHT = 0, //Bright particles.
+ IMAQ_PARTICLE_DARK = 1, //Dark particles.
+ IMAQ_PARTICLE_TYPE_SIZE_GUARD = 0xFFFFFFFF
+} ParticleType;
+
+typedef enum VisionInfoType2_enum {
+ IMAQ_VISIONINFO_CALIBRATION = 0x01, //Used to indicate interaction with the Calibration information in an image.
+ IMAQ_VISIONINFO_OVERLAY = 0x02, //Used to indicate interaction with the Overlay information in an image.
+ IMAQ_VISIONINFO_GRAYTEMPLATE = 0x04, //Used to indicate interaction with the grayscale template information in an image.
+ IMAQ_VISIONINFO_COLORTEMPLATE = 0x08, //Used to indicate interaction with the color template information in an image.
+ IMAQ_VISIONINFO_GEOMETRICTEMPLATE = 0x10, //Used to indicate interaction with the geometric template information in an image.
+ IMAQ_VISIONINFO_CUSTOMDATA = 0x20, //Used to indicate interaction with the binary or text Custom Data in an image.
+ IMAQ_VISIONINFO_GOLDENTEMPLATE = 0x40, //Used to indicate interaction with the golden template information in an image.
+ IMAQ_VISIONINFO_GEOMETRICTEMPLATE2 = 0x80, //Used to indicate interaction with the geometric template 2 information in an image.
+ IMAQ_VISIONINFO_ALL = 0xFFFFFFFF, //Removes, checks for, or indicates the presence of all types of extra information in an image.
+} VisionInfoType2;
+
+typedef enum ReadClassifierFileMode_enum {
+ IMAQ_CLASSIFIER_READ_ALL = 0, //Read all information from the classifier file.
+ IMAQ_CLASSIFIER_READ_SAMPLES = 1, //Read only the samples from the classifier file.
+ IMAQ_CLASSIFIER_READ_PROPERTIES = 2, //Read only the properties from the classifier file.
+ IMAQ_READ_CLASSIFIER_FILE_MODES_SIZE_GUARD = 0xFFFFFFFF
+} ReadClassifierFileMode;
+
+typedef enum WriteClassifierFileMode_enum {
+ IMAQ_CLASSIFIER_WRITE_ALL = 0, //Writes all information to the classifier file.
+ IMAQ_CLASSIFIER_WRITE_CLASSIFY_ONLY = 1, //Write only the information needed to classify to the classifier file.
+ IMAQ_WRITE_CLASSIFIER_FILE_MODES_SIZE_GUARD = 0xFFFFFFFF
+} WriteClassifierFileMode;
+
+typedef enum Barcode2DShape_enum {
+ IMAQ_SQUARE_BARCODE_2D = 0, //The function searches for square 2D barcodes.
+ IMAQ_RECTANGULAR_BARCODE_2D = 1, //The function searches for rectangular 2D barcodes.
+ IMAQ_BARCODE_2D_SHAPE_SIZE_GUARD = 0xFFFFFFFF
+} Barcode2DShape;
+
+typedef enum DataMatrixRotationMode_enum {
+ IMAQ_UNLIMITED_ROTATION = 0, //The function allows for unlimited rotation.
+ IMAQ_0_DEGREES = 1, //The function allows for between -5 and 5 degrees of rotation.
+ IMAQ_90_DEGREES = 2, //The function allows for between 85 and 95 degrees of rotation.
+ IMAQ_180_DEGREES = 3, //The function allows for between 175 and 185 degrees of rotation.
+ IMAQ_270_DEGREES = 4, //The function allows for between 265 and 275 degrees of rotation.
+ IMAQ_DATA_MATRIX_ROTATION_MODE_SIZE_GUARD = 0xFFFFFFFF
+} DataMatrixRotationMode;
+
+typedef enum AIMGrade_enum {
+ IMAQ_AIM_GRADE_F = 0, //The Data Matrix barcode received a grade of F.
+ IMAQ_AIM_GRADE_D = 1, //The Data Matrix barcode received a grade of D.
+ IMAQ_AIM_GRADE_C = 2, //The Data Matrix barcode received a grade of C.
+ IMAQ_AIM_GRADE_B = 3, //The Data Matrix barcode received a grade of B.
+ IMAQ_AIM_GRADE_A = 4, //The Data Matrix barcode received a grade of A.
+ IMAQ_DATA_MATRIX_AIM_GRADE_SIZE_GUARD = 0xFFFFFFFF
+} AIMGrade;
+
+typedef enum DataMatrixCellFillMode_enum {
+ IMAQ_AUTO_DETECT_CELL_FILL_MODE = -2, //Sets the function to determine the Data Matrix barcode cell fill percentage automatically.
+ IMAQ_LOW_FILL = 0, //Sets the function to read Data Matrix barcodes with a cell fill percentage of less than 30 percent.
+ IMAQ_NORMAL_FILL = 1, //Sets the function to read Data Matrix barcodes with a cell fill percentage greater than or equal to 30 percent.
+ IMAQ_DATA_MATRIX_CELL_FILL_MODE_SIZE_GUARD = 0xFFFFFFFF
+} DataMatrixCellFillMode;
+
+typedef enum DataMatrixDemodulationMode_enum {
+ IMAQ_AUTO_DETECT_DEMODULATION_MODE = -2, //The function will try each demodulation mode and use the one which decodes the Data Matrix barcode within the fewest iterations and utilizing the least amount of error correction.
+ IMAQ_HISTOGRAM = 0, //The function uses a histogram of all of the Data Matrix cells to calculate a threshold.
+ IMAQ_LOCAL_CONTRAST = 1, //The function examines each of the cell's neighbors to determine if the cell is on or off.
+ IMAQ_COMBINED = 2, //The function uses the histogram of the Data Matrix barcode to calculate a threshold.
+ IMAQ_ALL_DEMODULATION_MODES = 3, //The function tries IMAQ_HISTOGRAM, then IMAQ_LOCAL_CONTRAST and then IMAQ_COMBINATION, stopping once one mode is successful.
+ IMAQ_DATA_MATRIX_DEMODULATION_MODE_SIZE_GUARD = 0xFFFFFFFF
+} DataMatrixDemodulationMode;
+
+typedef enum DataMatrixECC_enum {
+ IMAQ_AUTO_DETECT_ECC = -2, //Sets the function to determine the Data Matrix barcode ECC automatically.
+ IMAQ_ECC_000 = 0, //Sets the function to read Data Matrix barcodes of ECC 000 only.
+ IMAQ_ECC_050 = 50, //Sets the function to read Data Matrix barcodes of ECC 050 only.
+ IMAQ_ECC_080 = 80, //Sets the function to read Data Matrix barcodes of ECC 080 only.
+ IMAQ_ECC_100 = 100, //Sets the function to read Data Matrix barcodes of ECC 100 only.
+ IMAQ_ECC_140 = 140, //Sets the function to read Data Matrix barcodes of ECC 140 only.
+ IMAQ_ECC_000_140 = 190, //Sets the function to read Data Matrix barcodes of ECC 000, ECC 050, ECC 080, ECC 100, and ECC 140 only.
+ IMAQ_ECC_200 = 200, //Sets the function to read Data Matrix barcodes of ECC 200 only.
+ IMAQ_DATA_MATRIX_ECC_SIZE_GUARD = 0xFFFFFFFF
+} DataMatrixECC;
+
+typedef enum DataMatrixPolarity_enum {
+ IMAQ_AUTO_DETECT_POLARITY = -2, //Sets the function to determine the Data Matrix barcode polarity automatically.
+ IMAQ_BLACK_DATA_ON_WHITE_BACKGROUND = 0, //Sets the function to read Data Matrix barcodes with dark data on a bright background.
+ IMAQ_WHITE_DATA_ON_BLACK_BACKGROUND = 1, //Sets the function to read Data Matrix barcodes with bright data on a dark background.
+ IMAQ_DATA_MATRIX_POLARITY_SIZE_GUARD = 0xFFFFFFFF
+} DataMatrixPolarity;
+
+typedef enum DataMatrixCellFilterMode_enum {
+ IMAQ_AUTO_DETECT_CELL_FILTER_MODE = -2, //The function will try all filter modes and uses the one that decodes the Data Matrix barcode within the fewest iterations and utilizing the least amount of error correction.
+ IMAQ_AVERAGE_FILTER = 0, //The function sets the pixel value for the cell to the average of the sampled pixels.
+ IMAQ_MEDIAN_FILTER = 1, //The function sets the pixel value for the cell to the median of the sampled pixels.
+ IMAQ_CENTRAL_AVERAGE_FILTER = 2, //The function sets the pixel value for the cell to the average of the pixels in the center of the cell sample.
+ IMAQ_HIGH_AVERAGE_FILTER = 3, //The function sets the pixel value for the cell to the average value of the half of the sampled pixels with the highest pixel values.
+ IMAQ_LOW_AVERAGE_FILTER = 4, //The function sets the pixel value for the cell to the average value of the half of the sampled pixels with the lowest pixel values.
+ IMAQ_VERY_HIGH_AVERAGE_FILTER = 5, //The function sets the pixel value for the cell to the average value of the ninth of the sampled pixels with the highest pixel values.
+ IMAQ_VERY_LOW_AVERAGE_FILTER = 6, //The function sets the pixel value for the cell to the average value of the ninth of the sampled pixels with the lowest pixel values.
+ IMAQ_ALL_CELL_FILTERS = 8, //The function tries each filter mode, starting with IMAQ_AVERAGE_FILTER and ending with IMAQ_VERY_LOW_AVERAGE_FILTER, stopping once a filter mode decodes correctly.
+ IMAQ_DATA_MATRIX_CELL_FILTER_MODE_SIZE_GUARD = 0xFFFFFFFF
+} DataMatrixCellFilterMode;
+
+typedef enum WindowBackgroundHatchStyle_enum {
+ IMAQ_HATCH_STYLE_HORIZONTAL = 0, //The background of the display window will be horizontal bars.
+ IMAQ_HATCH_STYLE_VERTICAL = 1, //The background of the display window will be vertical bars.
+ IMAQ_HATCH_STYLE_FORWARD_DIAGONAL = 2, //The background of the display window will be diagonal bars.
+ IMAQ_HATCH_STYLE_BACKWARD_DIAGONAL = 3, //The background of the display window will be diagonal bars.
+ IMAQ_HATCH_STYLE_CROSS = 4, //The background of the display window will be intersecting horizontal and vertical bars.
+ IMAQ_HATCH_STYLE_CROSS_HATCH = 5, //The background of the display window will be intersecting forward and backward diagonal bars.
+ IMAQ_HATCH_STYLE_SIZE_GUARD = 0xFFFFFFFF
+} WindowBackgroundHatchStyle;
+
+typedef enum DataMatrixMirrorMode_enum {
+ IMAQ_AUTO_DETECT_MIRROR = -2, //Specifies that the function should determine if the Data Matrix barcode is mirrored.
+ IMAQ_APPEARS_NORMAL = 0, //Specifies that the function should expect the Data Matrix barcode to appear normal.
+ IMAQ_APPEARS_MIRRORED = 1, //Specifies that the function should expect the Data Matrix barcode to appear mirrored.
+ IMAQ_DATA_MATRIX_MIRROR_MODE_SIZE_GUARD = 0xFFFFFFFF
+} DataMatrixMirrorMode;
+
+typedef enum CalibrationMode2_enum {
+ IMAQ_PERSPECTIVE_MODE = 0, //Functions correct for distortion caused by the camera's perspective.
+ IMAQ_MICROPLANE_MODE = 1, //Functions correct for distortion caused by the camera's lens.
+ IMAQ_SIMPLE_CALIBRATION_MODE = 2, //Functions do not correct for distortion.
+ IMAQ_CORRECTED_IMAGE_MODE = 3, //The image is already corrected.
+ IMAQ_NO_CALIBRATION_MODE = 4, //Image with No calibration.
+ IMAQ_CALIBRATION_MODE2_SIZE_GUARD = 0xFFFFFFFF
+} CalibrationMode2;
+
+typedef enum DataMatrixGradingMode_enum {
+ IMAQ_NO_GRADING = 0, //The function does not make any preparatory calculations.
+ IMAQ_PREPARE_FOR_AIM = 1, //The function prepares the image for grading using the AIM Print Quality metrics.
+ IMAQ_DATA_MATRIX_GRADING_MODE_SIZE_GUARD = 0xFFFFFFFF
+} DataMatrixGradingMode;
+
+typedef enum WaveletTransformMode_enum {
+ IMAQ_WAVELET_TRANSFORM_INTEGER = 0, //Uses a 5-3 reversible integer transform.
+ IMAQ_WAVELET_TRANSFORM_FLOATING_POINT = 1, //Performs a 9-7 irreversible floating-point transform.
+ IMAQ_WAVELET_TRANSFORM_MODE_SIZE_GUARD = 0xFFFFFFFF
+} WaveletTransformMode;
+
+typedef enum NormalizationMethod_enum {
+ IMAQ_NORMALIZATION_NONE = 0, //No normalization.
+ IMAQ_NORMALIZATION_HISTOGRAM_MATCHING = 1, //Adjust image so its histogram is similar to the golden template's histogram.
+ IMAQ_NORMALIZATION_AVERAGE_MATCHING = 2, //Adjust image so its mean pixel value equals the golden template's mean pixel value.
+ IMAQ_NORMALIZATION_SIZE_GUARD = 0xFFFFFFFF
+} NormalizationMethod;
+
+typedef enum RegistrationMethod_enum {
+ IMAQ_REGISTRATION_NONE = 0, //No registration.
+ IMAQ_REGISTRATION_PERSPECTIVE = 1, //Adjust image to correct for minor variations in alignment or perspective.
+ IMAQ_REGISTRATION_SIZE_GUARD = 0xFFFFFFFF
+} RegistrationMethod;
+
+typedef enum LinearAveragesMode_enum {
+ IMAQ_COLUMN_AVERAGES = 1, //Specifies that the function calculates the mean pixel value of each column.
+ IMAQ_ROW_AVERAGES = 2, //Specifies that the function calculates the mean pixel value of each row.
+ IMAQ_RISING_DIAGONAL_AVERAGES = 4, //Specifies that the function calculates the mean pixel value of each diagonal running from the lower left to the upper right of the inspected area of the image.
+ IMAQ_FALLING_DIAGONAL_AVERAGES = 8, //Specifies that the function calculates the mean pixel value of each diagonal running from the upper left to the lower right of the inspected area of the image.
+ IMAQ_ALL_LINEAR_AVERAGES = 15, //Specifies that the function calculates all four linear mean pixel values.
+ IMAQ_LINEAR_AVERAGES_MODE_SIZE_GUARD = 0xFFFFFFFF
+} LinearAveragesMode;
+
+typedef enum CompressionType_enum {
+ IMAQ_COMPRESSION_NONE = 0, //Specifies that the function should not compress the image.
+ IMAQ_COMPRESSION_JPEG = 1, //Specifies that the function should use lossy JPEG compression on the image.
+ IMAQ_COMPRESSION_PACKED_BINARY = 2, //Specifies that the function should use lossless binary packing on the image.
+ IMAQ_COMPRESSION_TYPE_SIZE_GUARD = 0xFFFFFFFF
+} CompressionType;
+
+typedef enum FlattenType_enum {
+ IMAQ_FLATTEN_IMAGE = 0, //Flattens just the image data.
+ IMAQ_FLATTEN_IMAGE_AND_VISION_INFO = 1, //Flattens the image data and any Vision information associated with the image.
+ IMAQ_FLATTEN_TYPE_SIZE_GUARD = 0xFFFFFFFF
+} FlattenType;
+
+typedef enum DataMatrixCellSampleSize_enum {
+ IMAQ_AUTO_DETECT_CELL_SAMPLE_SIZE = -2, //The function will try each sample size and use the one which decodes the Data Matrix barcode within the fewest iterations and utilizing the least amount of error correction.
+ IMAQ_1x1 = 1, //The function will use a 1x1 sized sample from each cell.
+ IMAQ_2x2 = 2, //The function will use a 2x2 sized sample from each cell.
+ IMAQ_3x3 = 3, //The function will use a 3x3 sized sample from each cell.
+ IMAQ_4x4 = 4, //The function will use a 4x4 sized sample from each cell.
+ IMAQ_5x5 = 5, //The function will use a 5x5 sized sample from each cell.
+ IMAQ_6x6 = 6, //The function will use a 6x6 sized sample from each cell.
+ IMAQ_7x7 = 7, //The function will use a 7x7 sized sample from each cell.
+ IMAQ_DATA_MATRIX_CELL_SAMPLE_SIZE_SIZE_GUARD = 0xFFFFFFFF
+} DataMatrixCellSampleSize;
+
+
+//============================================================================
+// Forward Declare Data Structures
+//============================================================================
+typedef struct Image_struct Image;
+typedef struct ROI_struct ROI;
+typedef struct Overlay_struct Overlay;
+typedef struct ClassifierSession_struct ClassifierSession;
+typedef struct MultipleGeometricPattern_struct MultipleGeometricPattern;
+typedef int ContourID;
+typedef unsigned long SESSION_ID;
+typedef int AVISession;
+typedef char* FilterName;
+typedef char String255[256];
+typedef struct CharSet_struct CharSet;
+typedef struct OCRSpacingOptions_struct OCRSpacingOptions;
+typedef struct OCRProcessingOptions_struct OCRProcessingOptions;
+typedef struct ReadTextOptions_struct ReadTextOptions;
+typedef struct CharInfo_struct CharInfo;
+typedef struct CharReport_struct CharReport;
+typedef struct ReadTextReport_struct ReadTextReport;
+typedef struct DivisionModel_struct DivisionModel;
+typedef struct FocalLength_struct FocalLength;
+typedef struct PolyModel_struct PolyModel;
+typedef struct DistortionModelParams_struct DistortionModelParams;
+typedef struct PointFloat_struct PointFloat;
+typedef struct InternalParameters_struct InternalParameters;
+typedef struct MaxGridSize_struct MaxGridSize;
+typedef struct ImageSize_struct ImageSize;
+typedef struct CalibrationReferencePoints_struct CalibrationReferencePoints;
+typedef struct GetCameraParametersReport_struct GetCameraParametersReport;
+typedef struct CalibrationAxisInfo_struct CalibrationAxisInfo;
+typedef struct CalibrationLearnSetupInfo_struct CalibrationLearnSetupInfo;
+typedef struct GridDescriptor_struct GridDescriptor;
+typedef struct ErrorStatistics_struct ErrorStatistics;
+typedef struct GetCalibrationInfoReport_struct GetCalibrationInfoReport;
+typedef struct EdgePolarity_struct EdgePolarity;
+typedef struct ClampSettings_struct ClampSettings;
+typedef struct PointDouble_struct PointDouble;
+typedef struct PointDoublePair_struct PointDoublePair;
+typedef struct ClampResults_struct ClampResults;
+typedef struct ClampPoints_struct ClampPoints;
+typedef struct RGBValue_struct RGBValue;
+typedef struct ClampOverlaySettings_struct ClampOverlaySettings;
+typedef struct ClampMax2Report_struct ClampMax2Report;
+typedef struct ContourFitSplineReport_struct ContourFitSplineReport;
+typedef struct LineFloat_struct LineFloat;
+typedef struct LineEquation_struct LineEquation;
+typedef struct ContourFitLineReport_struct ContourFitLineReport;
+typedef struct ContourFitPolynomialReport_struct ContourFitPolynomialReport;
+typedef struct PartialCircle_struct PartialCircle;
+typedef struct PartialEllipse_struct PartialEllipse;
+typedef struct SetupMatchPatternData_struct SetupMatchPatternData;
+typedef struct RangeSettingDouble_struct RangeSettingDouble;
+typedef struct GeometricAdvancedSetupDataOption_struct GeometricAdvancedSetupDataOption;
+typedef struct ContourInfoReport_struct ContourInfoReport;
+typedef struct ROILabel_struct ROILabel;
+typedef struct SupervisedColorSegmentationReport_struct SupervisedColorSegmentationReport;
+typedef struct LabelToROIReport_struct LabelToROIReport;
+typedef struct ColorSegmenationOptions_struct ColorSegmenationOptions;
+typedef struct ClassifiedCurve_struct ClassifiedCurve;
+typedef struct RangeDouble_struct RangeDouble;
+typedef struct RangeLabel_struct RangeLabel;
+typedef struct CurvatureAnalysisReport_struct CurvatureAnalysisReport;
+typedef struct Disparity_struct Disparity;
+typedef struct ComputeDistancesReport_struct ComputeDistancesReport;
+typedef struct MatchMode_struct MatchMode;
+typedef struct ClassifiedDisparity_struct ClassifiedDisparity;
+typedef struct ClassifyDistancesReport_struct ClassifyDistancesReport;
+typedef struct ContourComputeCurvatureReport_struct ContourComputeCurvatureReport;
+typedef struct ContourOverlaySettings_struct ContourOverlaySettings;
+typedef struct CurveParameters_struct CurveParameters;
+typedef struct ExtractContourReport_struct ExtractContourReport;
+typedef struct ConnectionConstraint_struct ConnectionConstraint;
+typedef struct ExtractTextureFeaturesReport_struct ExtractTextureFeaturesReport;
+typedef struct WaveletBandsReport_struct WaveletBandsReport;
+typedef struct CircleFitOptions_struct CircleFitOptions;
+typedef struct EdgeOptions2_struct EdgeOptions2;
+typedef struct FindCircularEdgeOptions_struct FindCircularEdgeOptions;
+typedef struct FindConcentricEdgeOptions_struct FindConcentricEdgeOptions;
+typedef struct ConcentricEdgeFitOptions_struct ConcentricEdgeFitOptions;
+typedef struct FindConcentricEdgeReport_struct FindConcentricEdgeReport;
+typedef struct FindCircularEdgeReport_struct FindCircularEdgeReport;
+typedef struct WindowSize_struct WindowSize;
+typedef struct DisplacementVector_struct DisplacementVector;
+typedef struct WaveletOptions_struct WaveletOptions;
+typedef struct CooccurrenceOptions_struct CooccurrenceOptions;
+typedef struct ParticleClassifierLocalThresholdOptions_struct ParticleClassifierLocalThresholdOptions;
+typedef struct RangeFloat_struct RangeFloat;
+typedef struct ParticleClassifierAutoThresholdOptions_struct ParticleClassifierAutoThresholdOptions;
+typedef struct ParticleClassifierPreprocessingOptions2_struct ParticleClassifierPreprocessingOptions2;
+typedef struct MeasureParticlesReport_struct MeasureParticlesReport;
+typedef struct GeometricPatternMatch3_struct GeometricPatternMatch3;
+typedef struct MatchGeometricPatternAdvancedOptions3_struct MatchGeometricPatternAdvancedOptions3;
+typedef struct ColorOptions_struct ColorOptions;
+typedef struct SampleScore_struct SampleScore;
+typedef struct ClassifierReportAdvanced_struct ClassifierReportAdvanced;
+typedef struct LearnGeometricPatternAdvancedOptions2_struct LearnGeometricPatternAdvancedOptions2;
+typedef struct ParticleFilterOptions2_struct ParticleFilterOptions2;
+typedef struct FindEdgeOptions2_struct FindEdgeOptions2;
+typedef struct FindEdgeReport_struct FindEdgeReport;
+typedef struct FindTransformRectOptions2_struct FindTransformRectOptions2;
+typedef struct FindTransformRectsOptions2_struct FindTransformRectsOptions2;
+typedef struct ReadTextReport3_struct ReadTextReport3;
+typedef struct CharacterStatistics_struct CharacterStatistics;
+typedef struct CharReport3_struct CharReport3;
+typedef struct ArcInfo2_struct ArcInfo2;
+typedef struct EdgeReport2_struct EdgeReport2;
+typedef struct SearchArcInfo_struct SearchArcInfo;
+typedef struct ConcentricRakeReport2_struct ConcentricRakeReport2;
+typedef struct SpokeReport2_struct SpokeReport2;
+typedef struct EdgeInfo_struct EdgeInfo;
+typedef struct SearchLineInfo_struct SearchLineInfo;
+typedef struct RakeReport2_struct RakeReport2;
+typedef struct TransformBehaviors_struct TransformBehaviors;
+typedef struct QRCodeDataToken_struct QRCodeDataToken;
+typedef struct ParticleFilterOptions_struct ParticleFilterOptions;
+typedef struct StraightEdgeReport2_struct StraightEdgeReport2;
+typedef struct StraightEdgeOptions_struct StraightEdgeOptions;
+typedef struct StraightEdge_struct StraightEdge;
+typedef struct QRCodeSearchOptions_struct QRCodeSearchOptions;
+typedef struct QRCodeSizeOptions_struct QRCodeSizeOptions;
+typedef struct QRCodeDescriptionOptions_struct QRCodeDescriptionOptions;
+typedef struct QRCodeReport_struct QRCodeReport;
+typedef struct AIMGradeReport_struct AIMGradeReport;
+typedef struct DataMatrixSizeOptions_struct DataMatrixSizeOptions;
+typedef struct DataMatrixDescriptionOptions_struct DataMatrixDescriptionOptions;
+typedef struct DataMatrixSearchOptions_struct DataMatrixSearchOptions;
+typedef struct DataMatrixReport_struct DataMatrixReport;
+typedef struct JPEG2000FileAdvancedOptions_struct JPEG2000FileAdvancedOptions;
+typedef struct MatchGeometricPatternAdvancedOptions2_struct MatchGeometricPatternAdvancedOptions2;
+typedef struct InspectionAlignment_struct InspectionAlignment;
+typedef struct InspectionOptions_struct InspectionOptions;
+typedef struct CharReport2_struct CharReport2;
+typedef struct CharInfo2_struct CharInfo2;
+typedef struct ReadTextReport2_struct ReadTextReport2;
+typedef struct EllipseFeature_struct EllipseFeature;
+typedef struct CircleFeature_struct CircleFeature;
+typedef struct ConstCurveFeature_struct ConstCurveFeature;
+typedef struct RectangleFeature_struct RectangleFeature;
+typedef struct LegFeature_struct LegFeature;
+typedef struct CornerFeature_struct CornerFeature;
+typedef struct LineFeature_struct LineFeature;
+typedef struct ParallelLinePairFeature_struct ParallelLinePairFeature;
+typedef struct PairOfParallelLinePairsFeature_struct PairOfParallelLinePairsFeature;
+typedef union GeometricFeature_union GeometricFeature;
+typedef struct FeatureData_struct FeatureData;
+typedef struct GeometricPatternMatch2_struct GeometricPatternMatch2;
+typedef struct ClosedCurveFeature_struct ClosedCurveFeature;
+typedef struct LineMatch_struct LineMatch;
+typedef struct LineDescriptor_struct LineDescriptor;
+typedef struct RectangleDescriptor_struct RectangleDescriptor;
+typedef struct RectangleMatch_struct RectangleMatch;
+typedef struct EllipseDescriptor_struct EllipseDescriptor;
+typedef struct EllipseMatch_struct EllipseMatch;
+typedef struct CircleMatch_struct CircleMatch;
+typedef struct CircleDescriptor_struct CircleDescriptor;
+typedef struct ShapeDetectionOptions_struct ShapeDetectionOptions;
+typedef struct Curve_struct Curve;
+typedef struct CurveOptions_struct CurveOptions;
+typedef struct Barcode2DInfo_struct Barcode2DInfo;
+typedef struct DataMatrixOptions_struct DataMatrixOptions;
+typedef struct ClassifierAccuracyReport_struct ClassifierAccuracyReport;
+typedef struct NearestNeighborClassResult_struct NearestNeighborClassResult;
+typedef struct NearestNeighborTrainingReport_struct NearestNeighborTrainingReport;
+typedef struct ParticleClassifierPreprocessingOptions_struct ParticleClassifierPreprocessingOptions;
+typedef struct ClassifierSampleInfo_struct ClassifierSampleInfo;
+typedef struct ClassScore_struct ClassScore;
+typedef struct ClassifierReport_struct ClassifierReport;
+typedef struct NearestNeighborOptions_struct NearestNeighborOptions;
+typedef struct ParticleClassifierOptions_struct ParticleClassifierOptions;
+typedef struct RGBU64Value_struct RGBU64Value;
+typedef struct GeometricPatternMatch_struct GeometricPatternMatch;
+typedef struct MatchGeometricPatternAdvancedOptions_struct MatchGeometricPatternAdvancedOptions;
+typedef struct MatchGeometricPatternOptions_struct MatchGeometricPatternOptions;
+typedef struct LearnGeometricPatternAdvancedOptions_struct LearnGeometricPatternAdvancedOptions;
+typedef struct FitEllipseOptions_struct FitEllipseOptions;
+typedef struct FitCircleOptions_struct FitCircleOptions;
+typedef struct ConstructROIOptions2_struct ConstructROIOptions2;
+typedef struct HSLValue_struct HSLValue;
+typedef struct HSVValue_struct HSVValue;
+typedef struct HSIValue_struct HSIValue;
+typedef struct CIELabValue_struct CIELabValue;
+typedef struct CIEXYZValue_struct CIEXYZValue;
+typedef union Color2_union Color2;
+typedef struct BestEllipse2_struct BestEllipse2;
+typedef struct LearnPatternAdvancedOptions_struct LearnPatternAdvancedOptions;
+typedef struct AVIInfo_struct AVIInfo;
+typedef struct LearnPatternAdvancedShiftOptions_struct LearnPatternAdvancedShiftOptions;
+typedef struct LearnPatternAdvancedRotationOptions_struct LearnPatternAdvancedRotationOptions;
+typedef struct MatchPatternAdvancedOptions_struct MatchPatternAdvancedOptions;
+typedef struct ParticleFilterCriteria2_struct ParticleFilterCriteria2;
+typedef struct BestCircle2_struct BestCircle2;
+typedef struct OCRSpacingOptions_struct OCRSpacingOptions;
+typedef struct OCRProcessingOptions_struct OCRProcessingOptions;
+typedef struct ReadTextOptions_struct ReadTextOptions;
+typedef struct CharInfo_struct CharInfo;
+#if !defined(USERINT_HEADER) && !defined(_CVI_RECT_DEFINED)
+typedef struct Rect_struct Rect;
+#endif
+typedef struct CharReport_struct CharReport;
+typedef struct ReadTextReport_struct ReadTextReport;
+#if !defined(USERINT_HEADER) && !defined(_CVI_POINT_DEFINED)
+typedef struct Point_struct Point;
+#endif
+typedef struct Annulus_struct Annulus;
+typedef struct EdgeLocationReport_struct EdgeLocationReport;
+typedef struct EdgeOptions_struct EdgeOptions;
+typedef struct EdgeReport_struct EdgeReport;
+typedef struct ExtremeReport_struct ExtremeReport;
+typedef struct FitLineOptions_struct FitLineOptions;
+typedef struct DisplayMapping_struct DisplayMapping;
+typedef struct DetectExtremesOptions_struct DetectExtremesOptions;
+typedef struct ImageInfo_struct ImageInfo;
+typedef struct LCDOptions_struct LCDOptions;
+typedef struct LCDReport_struct LCDReport;
+typedef struct LCDSegments_struct LCDSegments;
+typedef struct LearnCalibrationOptions_struct LearnCalibrationOptions;
+typedef struct LearnColorPatternOptions_struct LearnColorPatternOptions;
+typedef struct Line_struct Line;
+typedef struct LinearAverages_struct LinearAverages;
+typedef struct LineProfile_struct LineProfile;
+typedef struct MatchColorPatternOptions_struct MatchColorPatternOptions;
+typedef struct HistogramReport_struct HistogramReport;
+typedef struct ArcInfo_struct ArcInfo;
+typedef struct AxisReport_struct AxisReport;
+typedef struct BarcodeInfo_struct BarcodeInfo;
+typedef struct BCGOptions_struct BCGOptions;
+typedef struct BestCircle_struct BestCircle;
+typedef struct BestEllipse_struct BestEllipse;
+typedef struct BestLine_struct BestLine;
+typedef struct BrowserOptions_struct BrowserOptions;
+typedef struct CoordinateSystem_struct CoordinateSystem;
+typedef struct CalibrationInfo_struct CalibrationInfo;
+typedef struct CalibrationPoints_struct CalibrationPoints;
+typedef struct CaliperOptions_struct CaliperOptions;
+typedef struct CaliperReport_struct CaliperReport;
+typedef struct DrawTextOptions_struct DrawTextOptions;
+typedef struct CircleReport_struct CircleReport;
+typedef struct ClosedContour_struct ClosedContour;
+typedef struct ColorHistogramReport_struct ColorHistogramReport;
+typedef struct ColorInformation_struct ColorInformation;
+typedef struct Complex_struct Complex;
+typedef struct ConcentricRakeReport_struct ConcentricRakeReport;
+typedef struct ConstructROIOptions_struct ConstructROIOptions;
+typedef struct ContourInfo_struct ContourInfo;
+typedef union ContourUnion_union ContourUnion;
+typedef struct ContourInfo2_struct ContourInfo2;
+typedef struct ContourPoint_struct ContourPoint;
+typedef struct CoordinateTransform_struct CoordinateTransform;
+typedef struct CoordinateTransform2_struct CoordinateTransform2;
+typedef struct CannyOptions_struct CannyOptions;
+typedef struct Range_struct Range;
+typedef struct UserPointSymbol_struct UserPointSymbol;
+typedef struct View3DOptions_struct View3DOptions;
+typedef struct MatchPatternOptions_struct MatchPatternOptions;
+typedef struct TIFFFileOptions_struct TIFFFileOptions;
+typedef union Color_union Color;
+typedef union PixelValue_union PixelValue;
+typedef struct OpenContour_struct OpenContour;
+typedef struct OverlayTextOptions_struct OverlayTextOptions;
+typedef struct ParticleFilterCriteria_struct ParticleFilterCriteria;
+typedef struct ParticleReport_struct ParticleReport;
+typedef struct PatternMatch_struct PatternMatch;
+typedef struct QuantifyData_struct QuantifyData;
+typedef struct QuantifyReport_struct QuantifyReport;
+typedef struct RakeOptions_struct RakeOptions;
+typedef struct RakeReport_struct RakeReport;
+typedef struct TransformReport_struct TransformReport;
+typedef struct ShapeReport_struct ShapeReport;
+typedef struct MeterArc_struct MeterArc;
+typedef struct ThresholdData_struct ThresholdData;
+typedef struct StructuringElement_struct StructuringElement;
+typedef struct SpokeReport_struct SpokeReport;
+typedef struct SimpleEdgeOptions_struct SimpleEdgeOptions;
+typedef struct SelectParticleCriteria_struct SelectParticleCriteria;
+typedef struct SegmentInfo_struct SegmentInfo;
+typedef struct RotationAngleRange_struct RotationAngleRange;
+typedef struct RotatedRect_struct RotatedRect;
+typedef struct ROIProfile_struct ROIProfile;
+typedef struct ToolWindowOptions_struct ToolWindowOptions;
+typedef struct SpokeOptions_struct SpokeOptions;
+
+//============================================================================
+// Data Structures
+//============================================================================
+#if !defined __GNUC__ && !defined _M_X64
+#pragma pack(push,1)
+#endif
+typedef struct DivisionModel_struct {
+ float kappa; //The learned kappa coefficient of division model.
+} DivisionModel;
+
+typedef struct FocalLength_struct {
+ float fx; //Focal length in X direction.
+ float fy; //Focal length in Y direction.
+} FocalLength;
+
+typedef struct PolyModel_struct {
+ float* kCoeffs; //The learned radial coefficients of polynomial model.
+ unsigned int numKCoeffs; //Number of K coefficients.
+ float p1; //The P1(learned tangential coefficients of polynomial model).
+ float p2; //The P2(learned tangential coefficients of polynomial model).
+} PolyModel;
+
+typedef struct DistortionModelParams_struct {
+ DistortionModel distortionModel; //Type of learned distortion model.
+ PolyModel polyModel; //The learned coefficients of polynomial model.
+ DivisionModel divisionModel; //The learned coefficient of division model.
+} DistortionModelParams;
+
+typedef struct PointFloat_struct {
+ float x; //The x-coordinate of the point.
+ float y; //The y-coordinate of the point.
+} PointFloat;
+
+typedef struct InternalParameters_struct {
+ char isInsufficientData;
+ FocalLength focalLength;
+ PointFloat opticalCenter;
+} InternalParameters;
+
+typedef struct MaxGridSize_struct {
+ unsigned int xMax; //Maximum x limit for the grid size.
+ unsigned int yMax; //Maximum y limit for the grid size.
+} MaxGridSize;
+
+typedef struct ImageSize_struct {
+ unsigned int xRes; //X resolution of the image.
+ unsigned int yRes; //Y resolution of the image.
+} ImageSize;
+
+typedef struct CalibrationReferencePoints_struct {
+ PointDouble* pixelCoords; //Specifies the coordinates of the pixel reference points.
+ unsigned int numPixelCoords; //Number of pixel coordinates.
+ PointDouble* realCoords; //Specifies the measuring unit associated with the image.
+ unsigned int numRealCoords; //Number of real coordinates.
+ CalibrationUnit units; //Specifies the units of X Step and Y Step.
+ ImageSize imageSize; //Specifies the size of calibration template image.
+} CalibrationReferencePoints;
+
+typedef struct GetCameraParametersReport_struct {
+ double* projectionMatrix; //The projection(homography) matrix of working plane.
+ unsigned int projectionMatrixRows; //Number of rows in projection matrix.
+ unsigned int projectionMatrixCols; //Number of columns in projection matrix.
+ DistortionModelParams distortion; //Distortion model Coefficients.
+ InternalParameters internalParams; //The learned internal paramters of camera model such as focal length and optical center.
+} GetCameraParametersReport;
+
+typedef struct CalibrationAxisInfo_struct {
+ PointFloat center; //The origin of the reference coordinate system, expressed in pixel units.
+ float rotationAngle; //The angle of the x-axis of the real-world coordinate system, in relation to the horizontal.
+ AxisOrientation axisDirection; //Specifies the direction of the calibraiton axis which is either Direct or Indirect.
+} CalibrationAxisInfo;
+
+typedef struct CalibrationLearnSetupInfo_struct {
+ CalibrationMode2 calibrationMethod; //Type of calibration method used.
+ DistortionModel distortionModel; //Type of learned distortion model.
+ ScalingMethod scaleMode; //The aspect scaling to use when correcting an image.
+ CalibrationROI roiMode; //The ROI to use when correcting an image.
+ char learnCorrectionTable; //Set this input to true value if you want the correction table to be determined and stored.
+} CalibrationLearnSetupInfo;
+
+typedef struct GridDescriptor_struct {
+ float xStep; //The distance in the x direction between two adjacent pixels in units specified by unit.
+ float yStep; //The distance in the y direction between two adjacent pixels in units specified by unit.
+ CalibrationUnit unit; //The unit of measure for the image.
+} GridDescriptor;
+
+typedef struct ErrorStatistics_struct {
+ double mean; //Mean error statistics value.
+ double maximum; //Maximum value of error.
+ double standardDeviation; //The standard deviation error statistiscs value.
+ double distortion; //The distortion error statistics value.
+} ErrorStatistics;
+
+typedef struct GetCalibrationInfoReport_struct {
+ ROI* userRoi; //Specifies the ROI the user provided when learning the calibration.
+ ROI* calibrationRoi; //Specifies the ROI that corresponds to the region of the image where the calibration information is accurate.
+ CalibrationAxisInfo axisInfo; //Reference Coordinate System for the real-world coordinates.
+ CalibrationLearnSetupInfo learnSetupInfo; //Calibration learn setup information.
+ GridDescriptor gridDescriptor; //Specifies scaling constants used to calibrate the image.
+ float* errorMap; //The the error map of calibration template image.
+ unsigned int errorMapRows; //Number of rows in error map.
+ unsigned int errorMapCols; //Number of Columns in error map.
+ ErrorStatistics errorStatistics; //Error statistics of the calibration.
+} GetCalibrationInfoReport;
+
+typedef struct EdgePolarity_struct {
+ EdgePolaritySearchMode start;
+ EdgePolaritySearchMode end;
+} EdgePolarity;
+
+typedef struct ClampSettings_struct {
+ double angleRange; //Specifies the angle range.
+ EdgePolarity edgePolarity; //Specifies the edge polarity.
+} ClampSettings;
+
+typedef struct PointDouble_struct {
+ double x; //The x-coordinate of the point.
+ double y; //The y-coordinate of the point.
+} PointDouble;
+
+typedef struct PointDoublePair_struct {
+ PointDouble start; //The Start co-ordinate of the pair.
+ PointDouble end; //The End co-ordinate of the pair.
+} PointDoublePair;
+
+typedef struct ClampResults_struct {
+ double distancePix; //Defines the Pixel world distance.
+ double distanceRealWorld; //Defines the real world distance.
+ double angleAbs; //Defines the absolute angle.
+ double angleRelative; //Defines the relative angle.
+} ClampResults;
+
+typedef struct ClampPoints_struct {
+ PointDoublePair pixel; //Specifies the pixel world point pair for clamp.
+ PointDoublePair realWorld; //Specifies the real world point pair for clamp.
+} ClampPoints;
+
+typedef struct RGBValue_struct {
+ unsigned char B; //The blue value of the color.
+ unsigned char G; //The green value of the color.
+ unsigned char R; //The red value of the color.
+ unsigned char alpha; //The alpha value of the color, which represents extra information about a color image, such as gamma correction.
+} RGBValue;
+
+typedef struct ClampOverlaySettings_struct {
+ int showSearchArea; //If TRUE, the function overlays the search area on the image.
+ int showCurves; //If TRUE, the function overlays the curves on the image.
+ int showClampLocation; //If TRUE, the function overlays the clamp location on the image.
+ int showResult; //If TRUE, the function overlays the hit lines to the object and the edge used to generate the hit line on the result image.
+ RGBValue searchAreaColor; //Specifies the RGB color value to use to overlay the search area.
+ RGBValue curvesColor; //Specifies the RGB color value to use to overlay the curves.
+ RGBValue clampLocationsColor; //Specifies the RGB color value to use to overlay the clamp locations.
+ RGBValue resultColor; //Specifies the RGB color value to use to overlay the results.
+ char* overlayGroupName; //Specifies the group overlay name for the step overlays.
+} ClampOverlaySettings;
+
+typedef struct ClampMax2Report_struct {
+ ClampResults clampResults; //Specifies the Clamp results information returned by the function.
+ ClampPoints clampPoints; //Specifies the clamp points information returned by the function.
+ unsigned int calibrationValid; //Specifies if the calibration information is valid or not.
+} ClampMax2Report;
+
+typedef struct ContourFitSplineReport_struct {
+ PointDouble* points; //It returns the points of the best-fit B-spline curve.
+ int numberOfPoints; //Number of Best fit points returned.
+} ContourFitSplineReport;
+
+typedef struct LineFloat_struct {
+ PointFloat start; //The coordinate location of the start of the line.
+ PointFloat end; //The coordinate location of the end of the line.
+} LineFloat;
+
+typedef struct LineEquation_struct {
+ double a; //The a coefficient of the line equation.
+ double b; //The b coefficient of the line equation.
+ double c; //The c coefficient of the line equation.
+} LineEquation;
+
+typedef struct ContourFitLineReport_struct {
+ LineFloat lineSegment; //Line Segment represents the intersection of the line equation and the contour.
+ LineEquation lineEquation; //Line Equation is a structure of three coefficients A, B, and C of the equation in the normal form (Ax + By + C=0) of the best fit line.
+} ContourFitLineReport;
+
+typedef struct ContourFitPolynomialReport_struct {
+ PointDouble* bestFit; //It returns the points of the best-fit polynomial.
+ int numberOfPoints; //Number of Best fit points returned.
+ double* polynomialCoefficients; //Polynomial Coefficients returns the coefficients of the polynomial equation.
+ int numberOfCoefficients; //Number of Coefficients returned in the polynomial coefficients array.
+} ContourFitPolynomialReport;
+
+typedef struct PartialCircle_struct {
+ PointFloat center; //Center of the circle.
+ double radius; //Radius of the circle.
+ double startAngle; //Start angle of the fitted structure.
+ double endAngle; //End angle of the fitted structure.
+} PartialCircle;
+
+typedef struct PartialEllipse_struct {
+ PointFloat center; //Center of the Ellipse.
+ double angle; //Angle of the ellipse.
+ double majorRadius; //The length of the semi-major axis of the ellipse.
+ double minorRadius; //The length of the semi-minor axis of the ellipse.
+ double startAngle; //Start angle of the fitted structure.
+ double endAngle; //End angle of the fitted structure.
+} PartialEllipse;
+
+typedef struct SetupMatchPatternData_struct {
+ unsigned char* matchSetupData; //String containing the match setup data.
+ int numMatchSetupData; //Number of match setup data.
+} SetupMatchPatternData;
+
+typedef struct RangeSettingDouble_struct {
+ SettingType settingType; //Match Constraints specifies the match option whose values you want to constrain by the given range.
+ double min; //Min is the minimum value of the range for a given Match Constraint.
+ double max; //Max is the maximum value of the range for a given Match Constraint.
+} RangeSettingDouble;
+
+typedef struct GeometricAdvancedSetupDataOption_struct {
+ GeometricSetupDataItem type; //It determines the option you want to use during the matching phase.
+ double value; //Value is the value for the option you want to use during the matching phase.
+} GeometricAdvancedSetupDataOption;
+
+typedef struct ContourInfoReport_struct {
+ PointDouble* pointsPixel; //Points (pixel) specifies the location of every point detected on the curve, in pixels.
+ unsigned int numPointsPixel; //Number of points pixel elements.
+ PointDouble* pointsReal; //Points (real) specifies the location of every point detected on the curve, in calibrated units.
+ unsigned int numPointsReal; //Number of points real elements.
+ double* curvaturePixel; //Curvature Pixel displays the curvature profile for the selected contour, in pixels.
+ unsigned int numCurvaturePixel; //Number of curvature pixels.
+ double* curvatureReal; //Curvature Real displays the curvature profile for the selected contour, in calibrated units.
+ unsigned int numCurvatureReal; //Number of curvature Real elements.
+ double length; //Length (pixel) specifies the length, in pixels, of the curves in the image.
+ double lengthReal; //Length (real) specifies the length, in calibrated units, of the curves within the curvature range.
+ unsigned int hasEquation; //Has Equation specifies whether the contour has a fitted equation.
+} ContourInfoReport;
+
+typedef struct ROILabel_struct {
+ char* className; //Specifies the classname you want to segment.
+ unsigned int label; //Label is the label number associated with the Class Name.
+} ROILabel;
+
+typedef struct SupervisedColorSegmentationReport_struct {
+ ROILabel* labelOut; //The Roi labels array.
+ unsigned int numLabelOut; //The number of elements in labelOut array.
+} SupervisedColorSegmentationReport;
+
+typedef struct LabelToROIReport_struct {
+ ROI** roiArray; //Array of ROIs.
+ unsigned int numOfROIs; //Number of ROIs in the roiArray.
+ unsigned int* labelsOutArray; //Array of labels.
+ unsigned int numOfLabels; //Number of labels.
+ int* isTooManyVectorsArray; //isTooManyVectorsArray array.
+ unsigned int isTooManyVectorsArraySize; //Number of elements in isTooManyVectorsArray.
+} LabelToROIReport;
+
+typedef struct ColorSegmenationOptions_struct {
+ unsigned int windowX; //X is the window size in x direction.
+ unsigned int windowY; //Y is the window size in y direction.
+ unsigned int stepSize; //Step Size is the distance between two windows.
+ unsigned int minParticleArea; //Min Particle Area is the minimum number of allowed pixels.
+ unsigned int maxParticleArea; //Max Particle Area is the maximum number of allowed pixels.
+ short isFineSegment; //When enabled, the step processes the boundary pixels of each segmentation cluster using a step size of 1.
+} ColorSegmenationOptions;
+
+typedef struct ClassifiedCurve_struct {
+ double length; //Specifies the length, in pixels, of the curves within the curvature range.
+ double lengthReal; //specifies the length, in calibrated units, of the curves within the curvature range.
+ double maxCurvature; //specifies the maximum curvature, in pixels, for the selected curvature range.
+ double maxCurvatureReal; //specifies the maximum curvature, in calibrated units, for the selected curvature range.
+ unsigned int label; //specifies the class to which the the sample belongs.
+ PointDouble* curvePoints; //Curve Points is a point-coordinate cluster that defines the points of the curve.
+ unsigned int numCurvePoints; //Number of curve points.
+} ClassifiedCurve;
+
+typedef struct RangeDouble_struct {
+ double minValue; //The minimum value of the range.
+ double maxValue; //The maximum value of the range.
+} RangeDouble;
+
+typedef struct RangeLabel_struct {
+ RangeDouble range; //Specifies the range of curvature values.
+ unsigned int label; //Class Label specifies the class to which the the sample belongs.
+} RangeLabel;
+
+typedef struct CurvatureAnalysisReport_struct {
+ ClassifiedCurve* curves;
+ unsigned int numCurves;
+} CurvatureAnalysisReport;
+
+typedef struct Disparity_struct {
+ PointDouble current; //Current is a array of points that defines the target contour.
+ PointDouble reference; //reference is a array of points that defines the template contour.
+ double distance; //Specifies the distance, in pixels, between the template contour point and the target contour point.
+} Disparity;
+
+typedef struct ComputeDistancesReport_struct {
+ Disparity* distances; //Distances is an array containing the computed distances.
+ unsigned int numDistances; //Number elements in the distances array.
+ Disparity* distancesReal; //Distances Real is an array containing the computed distances in calibrated units.
+ unsigned int numDistancesReal; //Number of elements in real distances array.
+} ComputeDistancesReport;
+
+typedef struct MatchMode_struct {
+ unsigned int rotation; //Rotation When enabled, the Function searches for occurrences of the template in the inspection image, allowing for template matches to be rotated.
+ unsigned int scale; //Rotation When enabled, the Function searches for occurrences of the template in the inspection image, allowing for template matches to be rotated.
+ unsigned int occlusion; //Occlusion specifies whether or not to search for occluded versions of the shape.
+} MatchMode;
+
+typedef struct ClassifiedDisparity_struct {
+ double length; //Length (pixel) specifies the length, in pixels, of the curves within the curvature range.
+ double lengthReal; //Length (real) specifies the length, in calibrated units, of the curves within the curvature range.
+ double maxDistance; //Maximum Distance (pixel) specifies the maximum distance, in pixels, between points along the selected contour and the template contour.
+ double maxDistanceReal; //Maximum Distance (real) specifies the maximum distance, in calibrated units, between points along the selected contour and the template contour.
+ unsigned int label; //Class Label specifies the class to which the the sample belongs.
+ PointDouble* templateSubsection; //Template subsection points is an array of points that defines the boundary of the template.
+ unsigned int numTemplateSubsection; //Number of reference points.
+ PointDouble* targetSubsection; //Current Points(Target subsection points) is an array of points that defines the boundary of the target.
+ unsigned int numTargetSubsection; //Number of current points.
+} ClassifiedDisparity;
+
+typedef struct ClassifyDistancesReport_struct {
+ ClassifiedDisparity* classifiedDistances; //Disparity array containing the classified distances.
+ unsigned int numClassifiedDistances; //Number of elements in the disparity array.
+} ClassifyDistancesReport;
+
+typedef struct ContourComputeCurvatureReport_struct {
+ double* curvaturePixel; //Curvature Pixel displays the curvature profile for the selected contour, in pixels.
+ unsigned int numCurvaturePixel; //Number of curvature pixels.
+ double* curvatureReal; //Curvature Real displays the curvature profile for the selected contour, in calibrated units.
+ unsigned int numCurvatureReal; //Number of curvature Real elements.
+} ContourComputeCurvatureReport;
+
+typedef struct ContourOverlaySettings_struct {
+ unsigned int overlay; //Overlay specifies whether to display the overlay on the image.
+ RGBValue color; //Color is the color of the overlay.
+ unsigned int width; //Width specifies the width of the overlay in pixels.
+ unsigned int maintainWidth; //Maintain Width? specifies whether you want the overlay measured in screen pixels or image pixels.
+} ContourOverlaySettings;
+
+typedef struct CurveParameters_struct {
+ ExtractionMode extractionMode; //Specifies the method the function uses to identify curves in the image.
+ int threshold; //Specifies the minimum contrast a seed point must have in order to begin a curve.
+ EdgeFilterSize filterSize; //Specifies the width of the edge filter the function uses to identify curves in the image.
+ int minLength; //Specifies the length, in pixels, of the smallest curve the function will extract.
+ int searchStep; //Search Step Size specifies the distance, in the y direction, between the image rows that the algorithm inspects for curve seed points.
+ int maxEndPointGap; //Specifies the maximum gap, in pixels, between the endpoints of a curve that the function identifies as a closed curve.
+ int subpixel; //Subpixel specifies whether to detect curve points with subpixel accuracy.
+} CurveParameters;
+
+typedef struct ExtractContourReport_struct {
+ PointDouble* contourPoints; //Contour Points specifies every point found on the contour.
+ int numContourPoints; //Number of contour points.
+ PointDouble* sourcePoints; //Source Image Points specifies every point found on the contour in the source image.
+ int numSourcePoints; //Number of source points.
+} ExtractContourReport;
+
+typedef struct ConnectionConstraint_struct {
+ ConnectionConstraintType constraintType; //Constraint Type specifies what criteria to use to consider two curves part of a contour.
+ RangeDouble range; //Specifies range for a given Match Constraint.
+} ConnectionConstraint;
+
+typedef struct ExtractTextureFeaturesReport_struct {
+ int* waveletBands; //The array having all the Wavelet Banks used for extraction.
+ int numWaveletBands; //Number of wavelet banks in the Array.
+ double** textureFeatures; //2-D array to store all the Texture features extracted.
+ int textureFeaturesRows; //Number of Rows in the Texture Features array.
+ int textureFeaturesCols; //Number of Cols in Texture Features array.
+} ExtractTextureFeaturesReport;
+
+typedef struct WaveletBandsReport_struct {
+ float** LLBand; //2-D array for LL Band.
+ float** LHBand; //2-D array for LH Band.
+ float** HLBand; //2-D array for HL Band.
+ float** HHBand; //2-D array for HH Band.
+ float** LLLBand; //2-D array for LLL Band.
+ float** LLHBand; //2-D array for LLH Band.
+ float LHLBand; //2-D array for LHL Band.
+ float** LHHBand; //2-D array for LHH Band.
+ int rows; //Number of Rows for each of the 2-D arrays.
+ int cols; //Number of Columns for each of the 2-D arrays.
+} WaveletBandsReport;
+
+typedef struct CircleFitOptions_struct {
+ int maxRadius; //Specifies the acceptable distance, in pixels, that a point determined to belong to the circle can be from the perimeter of the circle.
+ double stepSize; //Step Size is the angle, in degrees, between each radial line in the annular region.
+ RakeProcessType processType; //Method used to process the data extracted for edge detection.
+} CircleFitOptions;
+
+typedef struct EdgeOptions2_struct {
+ EdgePolaritySearchMode polarity; //Specifies the polarity of the edges to be found.
+ unsigned int kernelSize; //Specifies the size of the edge detection kernel.
+ unsigned int width; //Specifies the number of pixels averaged perpendicular to the search direction to compute the edge profile strength at each point along the search ROI.
+ float minThreshold; //Specifies the minimum edge strength (gradient magnitude) required for a detected edge.
+ InterpolationMethod interpolationType; //Specifies the interpolation method used to locate the edge position.
+ ColumnProcessingMode columnProcessingMode; //Specifies the method used to find the straight edge.
+} EdgeOptions2;
+
+typedef struct FindCircularEdgeOptions_struct {
+ SpokeDirection direction; //Specifies the Spoke direction to search in the ROI.
+ int showSearchArea; //If TRUE, the function overlays the search area on the image.
+ int showSearchLines; //If TRUE, the function overlays the search lines used to locate the edges on the image.
+ int showEdgesFound; //If TRUE, the function overlays the locations of the edges found on the image.
+ int showResult; //If TRUE, the function overlays the hit lines to the object and the edge used to generate the hit line on the result image.
+ RGBValue searchAreaColor; //Specifies the RGB color value to use to overlay the search area.
+ RGBValue searchLinesColor; //Specifies the RGB color value to use to overlay the search lines.
+ RGBValue searchEdgesColor; //Specifies the RGB color value to use to overlay the search edges.
+ RGBValue resultColor; //Specifies the RGB color value to use to overlay the results.
+ char* overlayGroupName; //Specifies the overlay group name to assign to the overlays.
+ EdgeOptions2 edgeOptions; //Specifies the edge detection options along a single search line.
+} FindCircularEdgeOptions;
+
+typedef struct FindConcentricEdgeOptions_struct {
+ ConcentricRakeDirection direction; //Specifies the Concentric Rake direction.
+ int showSearchArea; //If TRUE, the function overlays the search area on the image.
+ int showSearchLines; //If TRUE, the function overlays the search lines used to locate the edges on the image.
+ int showEdgesFound; //If TRUE, the function overlays the locations of the edges found on the image.
+ int showResult; //If TRUE, the function overlays the hit lines to the object and the edge used to generate the hit line on the result image.
+ RGBValue searchAreaColor; //Specifies the RGB color value to use to overlay the search area.
+ RGBValue searchLinesColor; //Specifies the RGB color value to use to overlay the search lines.
+ RGBValue searchEdgesColor; //Specifies the RGB color value to use to overlay the search edges.
+ RGBValue resultColor; //Specifies the RGB color value to use to overlay the results.
+ char* overlayGroupName; //Specifies the overlay group name to assign to the overlays.
+ EdgeOptions2 edgeOptions; //Specifies the edge detection options along a single search line.
+} FindConcentricEdgeOptions;
+
+typedef struct ConcentricEdgeFitOptions_struct {
+ int maxRadius; //Specifies the acceptable distance, in pixels, that a point determined to belong to the circle can be from the perimeter of the circle.
+ double stepSize; //The sampling factor that determines the gap between the rake lines.
+ RakeProcessType processType; //Method used to process the data extracted for edge detection.
+} ConcentricEdgeFitOptions;
+
+typedef struct FindConcentricEdgeReport_struct {
+ PointFloat startPt; //Pixel Coordinates for starting point of the edge.
+ PointFloat endPt; //Pixel Coordinates for end point of the edge.
+ PointFloat startPtCalibrated; //Real world Coordinates for starting point of the edge.
+ PointFloat endPtCalibrated; //Real world Coordinates for end point of the edge.
+ double angle; //Angle of the edge found.
+ double angleCalibrated; //Calibrated angle of the edge found.
+ double straightness; //The straightness value of the detected straight edge.
+ double avgStrength; //Average strength of the egde found.
+ double avgSNR; //Average SNR(Signal to Noise Ratio) for the edge found.
+ int lineFound; //If the edge is found or not.
+} FindConcentricEdgeReport;
+
+typedef struct FindCircularEdgeReport_struct {
+ PointFloat centerCalibrated; //Real world Coordinates of the Center.
+ double radiusCalibrated; //Real world radius of the Circular Edge found.
+ PointFloat center; //Pixel Coordinates of the Center.
+ double radius; //Radius in pixels of the Circular Edge found.
+ double roundness; //The roundness of the calculated circular edge.
+ double avgStrength; //Average strength of the egde found.
+ double avgSNR; //Average SNR(Signal to Noise Ratio) for the edge found.
+ int circleFound; //If the circlular edge is found or not.
+} FindCircularEdgeReport;
+
+typedef struct WindowSize_struct {
+ int x; //Window lenght on X direction.
+ int y; //Window lenght on Y direction.
+ int stepSize; //Distance between windows.
+} WindowSize;
+
+typedef struct DisplacementVector_struct {
+ int x; //length on X direction.
+ int y; //length on Y direction.
+} DisplacementVector;
+
+typedef struct WaveletOptions_struct {
+ WaveletType typeOfWavelet; //Type of wavelet(db, bior.
+ float minEnergy; //Minimum Energy in the bands to consider for texture defect detection.
+} WaveletOptions;
+
+typedef struct CooccurrenceOptions_struct {
+ int level; //Level/size of matrix.
+ DisplacementVector displacement; //Displacemnet between pixels to accumulate the matrix.
+} CooccurrenceOptions;
+
+typedef struct ParticleClassifierLocalThresholdOptions_struct {
+ LocalThresholdMethod method; //Specifies the local thresholding method the function uses.
+ ParticleType particleType; //Specifies what kind of particles to look for.
+ unsigned int windowWidth; //The width of the rectangular window around the pixel on which the function performs the local threshold.
+ unsigned int windowHeight; //The height of the rectangular window around the pixel on which the function performs the local threshold.
+ double deviationWeight; //Specifies the k constant used in the Niblack local thresholding algorithm, which determines the weight applied to the variance calculation.
+} ParticleClassifierLocalThresholdOptions;
+
+typedef struct RangeFloat_struct {
+ float minValue; //The minimum value of the range.
+ float maxValue; //The maximum value of the range.
+} RangeFloat;
+
+typedef struct ParticleClassifierAutoThresholdOptions_struct {
+ ThresholdMethod method; //The method for binary thresholding, which specifies how to calculate the classes.
+ ParticleType particleType; //Specifies what kind of particles to look for.
+ RangeFloat limits; //The limits on the automatic threshold range.
+} ParticleClassifierAutoThresholdOptions;
+
+typedef struct ParticleClassifierPreprocessingOptions2_struct {
+ ParticleClassifierThresholdType thresholdType; //The type of threshold to perform on the image.
+ RangeFloat manualThresholdRange; //The range of pixels to keep if manually thresholding the image.
+ ParticleClassifierAutoThresholdOptions autoThresholdOptions; //The options used to auto threshold the image.
+ ParticleClassifierLocalThresholdOptions localThresholdOptions; //The options used to local threshold the image.
+ int rejectBorder; //Set this element to TRUE to reject border particles.
+ int numErosions; //The number of erosions to perform.
+} ParticleClassifierPreprocessingOptions2;
+
+typedef struct MeasureParticlesReport_struct {
+ double** pixelMeasurements; //The measurements on the particles in the image, in pixel coordinates.
+ double** calibratedMeasurements; //The measurements on the particles in the image, in real-world coordinates.
+ size_t numParticles; //The number of particles on which measurements were taken.
+ size_t numMeasurements; //The number of measurements taken.
+} MeasureParticlesReport;
+
+typedef struct GeometricPatternMatch3_struct {
+ PointFloat position; //The location of the origin of the template in the match.
+ float rotation; //The rotation of the match relative to the template image, in degrees.
+ float scale; //The size of the match relative to the size of the template image, expressed as a percentage.
+ float score; //The accuracy of the match.
+ PointFloat corner[4]; //An array of four points describing the rectangle surrounding the template image.
+ int inverse; //This element is TRUE if the match is an inverse of the template image.
+ float occlusion; //The percentage of the match that is occluded.
+ float templateMatchCurveScore; //The accuracy of the match obtained by comparing the template curves to the curves in the match region.
+ float matchTemplateCurveScore; //The accuracy of the match obtained by comparing the curves in the match region to the template curves.
+ float correlationScore; //The accuracy of the match obtained by comparing the template image to the match region using a correlation metric that compares the two regions as a function of their pixel values.
+ PointFloat calibratedPosition; //The location of the origin of the template in the match.
+ float calibratedRotation; //The rotation of the match relative to the template image, in degrees.
+ PointFloat calibratedCorner[4]; //An array of four points describing the rectangle surrounding the template image.
+} GeometricPatternMatch3;
+
+typedef struct MatchGeometricPatternAdvancedOptions3_struct {
+ unsigned int subpixelIterations; //Specifies the maximum number of incremental improvements used to refine matches with subpixel information.
+ double subpixelTolerance; //Specifies the maximum amount of change, in pixels, between consecutive incremental improvements in the match position before the function stops refining the match position.
+ unsigned int initialMatchListLength; //Specifies the maximum size of the match list.
+ int targetTemplateCurveScore; //Set this element to TRUE to specify that the function should calculate the match curve to template curve score and return it for each match result.
+ int correlationScore; //Set this element to TRUE to specify that the function should calculate the correlation score and return it for each match result.
+ double minMatchSeparationDistance; //Specifies the minimum separation distance, in pixels, between the origins of two matches that have unique positions.
+ double minMatchSeparationAngle; //Specifies the minimum angular difference, in degrees, between two matches that have unique angles.
+ double minMatchSeparationScale; //Specifies the minimum difference in scale, expressed as a percentage, between two matches that have unique scales.
+ double maxMatchOverlap; //Specifies the maximum amount of overlap, expressed as a percentage, allowed between the bounding rectangles of two unique matches.
+ int coarseResult; //Specifies whether you want the function to spend less time accurately estimating the location of a match.
+ int enableCalibrationSupport; //Set this element to TRUE to specify the algorithm treat the inspection image as a calibrated image.
+ ContrastMode enableContrastReversal; //Use this element to specify the contrast of the matches to search for in the image.
+ GeometricMatchingSearchStrategy matchStrategy; //Specifies the aggressiveness of the search strategy.
+ unsigned int refineMatchFactor; //Specifies the factor that is applied to the number of matches requested by the user to determine the number of matches that are refined at the initial matching stage.
+ unsigned int subpixelMatchFactor; //Specifies the factor that is applied to the number of matches requested by the user to determine the number of matches that are evaluated at the final subpixel matching stage.
+} MatchGeometricPatternAdvancedOptions3;
+
+typedef struct ColorOptions_struct {
+ ColorClassificationResolution colorClassificationResolution; //Specifies the color resolution of the classifier.
+ unsigned int useLuminance; //Specifies if the luminance band is going to be used in the feature vector.
+ ColorMode colorMode; //Specifies the color mode of the classifier.
+} ColorOptions;
+
+typedef struct SampleScore_struct {
+ char* className; //The name of the class.
+ float distance; //The distance from the item to this class.
+ unsigned int index; //index of this sample.
+} SampleScore;
+
+typedef struct ClassifierReportAdvanced_struct {
+ char* bestClassName; //The name of the best class for the sample.
+ float classificationScore; //The similarity of the sample and the two closest classes in the classifier.
+ float identificationScore; //The similarity of the sample and the assigned class.
+ ClassScore* allScores; //All classes and their scores.
+ int allScoresSize; //The number of entries in allScores.
+ SampleScore* sampleScores; //All samples and their scores.
+ int sampleScoresSize; //The number of entries in sampleScores.
+} ClassifierReportAdvanced;
+
+typedef struct LearnGeometricPatternAdvancedOptions2_struct {
+ double minScaleFactor; //Specifies the minimum scale factor that the template is learned for.
+ double maxScaleFactor; //Specifies the maximum scale factor the template is learned for.
+ double minRotationAngleValue; //Specifies the minimum rotation angle the template is learned for.
+ double maxRotationAngleValue; //Specifies the maximum rotation angle the template is learned for.
+ unsigned int imageSamplingFactor; //Specifies the factor that is used to subsample the template and the image for the initial matching phase.
+} LearnGeometricPatternAdvancedOptions2;
+
+typedef struct ParticleFilterOptions2_struct {
+ int rejectMatches; //Set this parameter to TRUE to transfer only those particles that do not meet all the criteria.
+ int rejectBorder; //Set this element to TRUE to reject border particles.
+ int fillHoles; //Set this element to TRUE to fill holes in particles.
+ int connectivity8; //Set this parameter to TRUE to use connectivity-8 to determine whether particles are touching.
+} ParticleFilterOptions2;
+
+typedef struct FindEdgeOptions2_struct {
+ RakeDirection direction; //The direction to search in the ROI.
+ int showSearchArea; //If TRUE, the function overlays the search area on the image.
+ int showSearchLines; //If TRUE, the function overlays the search lines used to locate the edges on the image.
+ int showEdgesFound; //If TRUE, the function overlays the locations of the edges found on the image.
+ int showResult; //If TRUE, the function overlays the hit lines to the object and the edge used to generate the hit line on the result image.
+ RGBValue searchAreaColor; //Specifies the RGB color value to use to overlay the search area.
+ RGBValue searchLinesColor; //Specifies the RGB color value to use to overlay the search lines.
+ RGBValue searchEdgesColor; //Specifies the RGB color value to use to overlay the search edges.
+ RGBValue resultColor; //Specifies the RGB color value to use to overlay the results.
+ char* overlayGroupName; //Specifies the overlay group name to assign to the overlays.
+ EdgeOptions2 edgeOptions; //Specifies the edge detection options along a single search line.
+} FindEdgeOptions2;
+
+typedef struct FindEdgeReport_struct {
+ StraightEdge* straightEdges; //An array of straight edges detected.
+ unsigned int numStraightEdges; //Indicates the number of straight edges found.
+} FindEdgeReport;
+
+typedef struct FindTransformRectOptions2_struct {
+ FindReferenceDirection direction; //Specifies the direction and orientation in which the function searches for the primary axis.
+ int showSearchArea; //If TRUE, the function overlays the search area on the image.
+ int showSearchLines; //If TRUE, the function overlays the search lines used to locate the edges on the image.
+ int showEdgesFound; //If TRUE, the function overlays the locations of the edges found on the image.
+ int showResult; //If TRUE, the function overlays the hit lines to the object and the edge used to generate the hit line on the result image.
+ RGBValue searchAreaColor; //Specifies the RGB color value to use to overlay the search area.
+ RGBValue searchLinesColor; //Specifies the RGB color value to use to overlay the search lines.
+ RGBValue searchEdgesColor; //Specifies the RGB color value to use to overlay the search edges.
+ RGBValue resultColor; //Specifies the RGB color value to use to overlay the results.
+ char* overlayGroupName; //Specifies the overlay group name to assign to the overlays.
+ EdgeOptions2 edgeOptions; //Specifies the edge detection options along a single search line.
+} FindTransformRectOptions2;
+
+typedef struct FindTransformRectsOptions2_struct {
+ FindReferenceDirection direction; //Specifies the direction and orientation in which the function searches for the primary axis.
+ int showSearchArea; //If TRUE, the function overlays the search area on the image.
+ int showSearchLines; //If TRUE, the function overlays the search lines used to locate the edges on the image.
+ int showEdgesFound; //If TRUE, the function overlays the locations of the edges found on the image.
+ int showResult; //If TRUE, the function overlays the hit lines to the object and the edge used to generate the hit line on the result image.
+ RGBValue searchAreaColor; //Specifies the RGB color value to use to overlay the search area.
+ RGBValue searchLinesColor; //Specifies the RGB color value to use to overlay the search lines.
+ RGBValue searchEdgesColor; //Specifies the RGB color value to use to overlay the search edges.
+ RGBValue resultColor; //Specifies the RGB color value to use to overlay the results.
+ char* overlayGroupName; //Specifies the overlay group name to assign to the overlays.
+ EdgeOptions2 primaryEdgeOptions; //Specifies the parameters used to compute the edge gradient information and detect the edges for the primary ROI.
+ EdgeOptions2 secondaryEdgeOptions; //Specifies the parameters used to compute the edge gradient information and detect the edges for the secondary ROI.
+} FindTransformRectsOptions2;
+
+typedef struct ReadTextReport3_struct {
+ const char* readString; //The read string.
+ CharReport3* characterReport; //An array of reports describing the properties of each identified character.
+ int numCharacterReports; //The number of identified characters.
+ ROI* roiBoundingCharacters; //An array specifying the coordinates of the character bounding ROI.
+} ReadTextReport3;
+
+typedef struct CharacterStatistics_struct {
+ int left; //The left offset of the character bounding rectangles in the current ROI.
+ int top; //The top offset of the character bounding rectangles in the current ROI.
+ int width; //The width of each of the characters you trained in the current ROI.
+ int height; //The height of each trained character in the current ROI.
+ int characterSize; //The size of the character in pixels.
+} CharacterStatistics;
+
+typedef struct CharReport3_struct {
+ const char* character; //The character value.
+ int classificationScore; //The degree to which the assigned character class represents the object better than the other character classes in the character set.
+ int verificationScore; //The similarity of the character and the reference character for the character class.
+ int verified; //This element is TRUE if a reference character was found for the character class and FALSE if a reference character was not found.
+ int lowThreshold; //The minimum value of the threshold range used for this character.
+ int highThreshold; //The maximum value of the threshold range used for this character.
+ CharacterStatistics characterStats; //Describes the characters segmented in the ROI.
+} CharReport3;
+
+typedef struct ArcInfo2_struct {
+ PointFloat center; //The center point of the arc.
+ double radius; //The radius of the arc.
+ double startAngle; //The starting angle of the arc, specified counter-clockwise from the x-axis.
+ double endAngle; //The ending angle of the arc, specified counter-clockwise from the x-axis.
+} ArcInfo2;
+
+typedef struct EdgeReport2_struct {
+ EdgeInfo* edges; //An array of edges detected.
+ unsigned int numEdges; //Indicates the number of edges detected.
+ double* gradientInfo; //An array that contains the calculated edge strengths along the user-defined search area.
+ unsigned int numGradientInfo; //Indicates the number of elements contained in gradientInfo.
+ int calibrationValid; //Indicates if the calibration data corresponding to the location of the edges is correct.
+} EdgeReport2;
+
+typedef struct SearchArcInfo_struct {
+ ArcInfo2 arcCoordinates; //Describes the arc used for edge detection.
+ EdgeReport2 edgeReport; //Describes the edges found in this search line.
+} SearchArcInfo;
+
+typedef struct ConcentricRakeReport2_struct {
+ EdgeInfo* firstEdges; //The first edge point detected along each search line in the ROI.
+ unsigned int numFirstEdges; //The number of points in the firstEdges array.
+ EdgeInfo* lastEdges; //The last edge point detected along each search line in the ROI.
+ unsigned int numLastEdges; //The number of points in the lastEdges array.
+ SearchArcInfo* searchArcs; //Contains the arcs used for edge detection and the edge information for each arc.
+ unsigned int numSearchArcs; //The number of arcs in the searchArcs array.
+} ConcentricRakeReport2;
+
+typedef struct SpokeReport2_struct {
+ EdgeInfo* firstEdges; //The first edge point detected along each search line in the ROI.
+ unsigned int numFirstEdges; //The number of points in the firstEdges array.
+ EdgeInfo* lastEdges; //The last edge point detected along each search line in the ROI.
+ unsigned int numLastEdges; //The number of points in the lastEdges array.
+ SearchLineInfo* searchLines; //The search lines used for edge detection.
+ unsigned int numSearchLines; //The number of search lines used in the edge detection.
+} SpokeReport2;
+
+typedef struct EdgeInfo_struct {
+ PointFloat position; //The location of the edge in the image.
+ PointFloat calibratedPosition; //The position of the edge in the image in real-world coordinates.
+ double distance; //The location of the edge from the first point along the boundary of the input ROI.
+ double calibratedDistance; //The location of the edge from the first point along the boundary of the input ROI in real-world coordinates.
+ double magnitude; //The intensity contrast at the edge.
+ double noisePeak; //The strength of the noise associated with the current edge.
+ int rising; //Indicates the polarity of the edge.
+} EdgeInfo;
+
+typedef struct SearchLineInfo_struct {
+ LineFloat lineCoordinates; //The endpoints of the search line.
+ EdgeReport2 edgeReport; //Describes the edges found in this search line.
+} SearchLineInfo;
+
+typedef struct RakeReport2_struct {
+ EdgeInfo* firstEdges; //The first edge point detected along each search line in the ROI.
+ unsigned int numFirstEdges; //The number of points in the firstEdges array.
+ EdgeInfo* lastEdges; //The last edge point detected along each search line in the ROI.
+ unsigned int numLastEdges; //The number of points in the lastEdges array.
+ SearchLineInfo* searchLines; //The search lines used for edge detection.
+ unsigned int numSearchLines; //The number of search lines used in the edge detection.
+} RakeReport2;
+
+typedef struct TransformBehaviors_struct {
+ GroupBehavior ShiftBehavior; //Specifies the behavior of an overlay group when a shift operation is applied to an image.
+ GroupBehavior ScaleBehavior; //Specifies the behavior of an overlay group when a scale operation is applied to an image.
+ GroupBehavior RotateBehavior; //Specifies the behavior of an overlay group when a rotate operation is applied to an image.
+ GroupBehavior SymmetryBehavior; //Specifies the behavior of an overlay group when a symmetry operation is applied to an image.
+} TransformBehaviors;
+
+typedef struct QRCodeDataToken_struct {
+ QRStreamMode mode; //Specifies the stream mode or the format of the data that is encoded in the QR code.
+ unsigned int modeData; //Indicates specifiers used by the user to postprocess the data if it requires it.
+ unsigned char* data; //Shows the encoded data in the QR code.
+ unsigned int dataLength; //Specifies the length of the data found in the QR code.
+} QRCodeDataToken;
+
+typedef struct ParticleFilterOptions_struct {
+ int rejectMatches; //Set this parameter to TRUE to transfer only those particles that do not meet all the criteria.
+ int rejectBorder; //Set this element to TRUE to reject border particles.
+ int connectivity8; //Set this parameter to TRUE to use connectivity-8 to determine whether particles are touching.
+} ParticleFilterOptions;
+
+typedef struct StraightEdgeReport2_struct {
+ StraightEdge* straightEdges; //Contains an array of found straight edges.
+ unsigned int numStraightEdges; //Indicates the number of straight edges found.
+ SearchLineInfo* searchLines; //Contains an array of all search lines used in the detection.
+ unsigned int numSearchLines; //The number of search lines used in the edge detection.
+} StraightEdgeReport2;
+
+typedef struct StraightEdgeOptions_struct {
+ unsigned int numLines; //Specifies the number of straight edges to find.
+ StraightEdgeSearchMode searchMode; //Specifies the method used to find the straight edge.
+ double minScore; //Specifies the minimum score of a detected straight edge.
+ double maxScore; //Specifies the maximum score of a detected edge.
+ double orientation; //Specifies the angle at which the straight edge is expected to be found.
+ double angleRange; //Specifies the +/- range around the orientation within which the straight edge is expected to be found.
+ double angleTolerance; //Specifies the expected angular accuracy of the straight edge.
+ unsigned int stepSize; //Specifies the gap in pixels between the search lines used with the rake-based methods.
+ double minSignalToNoiseRatio; //Specifies the minimum signal to noise ratio (SNR) of the edge points used to fit the straight edge.
+ double minCoverage; //Specifies the minimum number of points as a percentage of the number of search lines that need to be included in the detected straight edge.
+ unsigned int houghIterations; //Specifies the number of iterations used in the Hough-based method.
+} StraightEdgeOptions;
+
+typedef struct StraightEdge_struct {
+ LineFloat straightEdgeCoordinates; //End points of the detected straight edge in pixel coordinates.
+ LineFloat calibratedStraightEdgeCoordinates; //End points of the detected straight edge in real-world coordinates.
+ double angle; //Angle of the found edge using the pixel coordinates.
+ double calibratedAngle; //Angle of the found edge using the real-world coordinates.
+ double score; //Describes the score of the detected edge.
+ double straightness; //The straightness value of the detected straight edge.
+ double averageSignalToNoiseRatio; //Describes the average signal to noise ratio (SNR) of the detected edge.
+ int calibrationValid; //Indicates if the calibration data for the straight edge is valid.
+ EdgeInfo* usedEdges; //An array of edges that were used to determine this straight line.
+ unsigned int numUsedEdges; //Indicates the number of edges in the usedEdges array.
+} StraightEdge;
+
+typedef struct QRCodeSearchOptions_struct {
+ QRRotationMode rotationMode; //Specifies the amount of QR code rotation the function should allow for.
+ unsigned int skipLocation; //If set to TRUE, specifies that the function should assume that the QR code occupies the entire image (or the entire search region).
+ unsigned int edgeThreshold; //The strength of the weakest edge the function uses to find the coarse location of the QR code in the image.
+ QRDemodulationMode demodulationMode; //The demodulation mode the function uses to locate the QR code.
+ QRCellSampleSize cellSampleSize; //The cell sample size the function uses to locate the QR code.
+ QRCellFilterMode cellFilterMode; //The cell filter mode the function uses to locate the QR code.
+ unsigned int skewDegreesAllowed; //Specifies the amount of skew in the QR code the function should allow for.
+} QRCodeSearchOptions;
+
+typedef struct QRCodeSizeOptions_struct {
+ unsigned int minSize; //Specifies the minimum size (in pixels) of the QR code in the image.
+ unsigned int maxSize; //Specifies the maximum size (in pixels) of the QR code in the image.
+} QRCodeSizeOptions;
+
+typedef struct QRCodeDescriptionOptions_struct {
+ QRDimensions dimensions; //The number of rows and columns that are populated for the QR code, measured in cells.
+ QRPolarities polarity; //The polarity of the QR code.
+ QRMirrorMode mirror; //This element is TRUE if the QR code appears mirrored in the image and FALSE if the QR code appears normally in the image.
+ QRModelType modelType; //This option allows you to specify the type of QR code.
+} QRCodeDescriptionOptions;
+
+typedef struct QRCodeReport_struct {
+ unsigned int found; //This element is TRUE if the function located and decoded a QR code and FALSE if the function failed to locate and decode a QR code.
+ unsigned char* data; //The data encoded in the QR code.
+ unsigned int dataLength; //The length of the data array.
+ PointFloat boundingBox[4]; //An array of four points describing the rectangle surrounding the QR code.
+ QRCodeDataToken* tokenizedData; //Contains the data tokenized in exactly the way it was encoded in the code.
+ unsigned int sizeOfTokenizedData; //Size of the tokenized data.
+ unsigned int numErrorsCorrected; //The number of errors the function corrected when decoding the QR code.
+ unsigned int dimensions; //The number of rows and columns that are populated for the QR code, measured in cells.
+ unsigned int version; //The version of the QR code.
+ QRModelType modelType; //This option allows you to specify what type of QR code this is.
+ QRStreamMode streamMode; //The format of the data encoded in the stream.
+ QRPolarities matrixPolarity; //The polarity of the QR code.
+ unsigned int mirrored; //This element is TRUE if the QR code appears mirrored in the image and FALSE if the QR code appears normally in the image.
+ unsigned int positionInAppendStream; //Indicates what position the QR code is in with respect to the stream of data in all codes.
+ unsigned int sizeOfAppendStream; //Specifies how many QR codes are part of a larger array of codes.
+ int firstEAN128ApplicationID; //The first EAN-128 Application ID encountered in the stream.
+ int firstECIDesignator; //The first Regional Language Designator encountered in the stream.
+ unsigned int appendStreamIdentifier; //Specifies what stream the QR code is in relation to when the code is part of a larger array of codes.
+ unsigned int minimumEdgeStrength; //The strength of the weakest edge the function used to find the coarse location of the QR code in the image.
+ QRDemodulationMode demodulationMode; //The demodulation mode the function used to locate the QR code.
+ QRCellSampleSize cellSampleSize; //The cell sample size the function used to locate the QR code.
+ QRCellFilterMode cellFilterMode; //The cell filter mode the function used to locate the QR code.
+} QRCodeReport;
+
+typedef struct AIMGradeReport_struct {
+ AIMGrade overallGrade; //The overall letter grade, which is equal to the lowest of the other five letter grades.
+ AIMGrade decodingGrade; //The letter grade assigned to a Data Matrix barcode based on the success of the function in decoding the Data Matrix barcode.
+ AIMGrade symbolContrastGrade; //The letter grade assigned to a Data Matrix barcode based on the symbol contrast raw score.
+ float symbolContrast; //The symbol contrast raw score representing the percentage difference between the mean of the reflectance of the darkest 10 percent and lightest 10 percent of the Data Matrix barcode.
+ AIMGrade printGrowthGrade; //The print growth letter grade for the Data Matrix barcode.
+ float printGrowth; //The print growth raw score for the barcode, which is based on the extent to which dark or light markings appropriately fill their module boundaries.
+ AIMGrade axialNonuniformityGrade; //The axial nonuniformity grade for the Data Matrix barcode.
+ float axialNonuniformity; //The axial nonuniformity raw score for the barcode, which is based on how much the sampling point spacing differs from one axis to another.
+ AIMGrade unusedErrorCorrectionGrade; //The unused error correction letter grade for the Data Matrix barcode.
+ float unusedErrorCorrection; //The unused error correction raw score for the Data Matrix barcode, which is based on the extent to which regional or spot damage in the Data Matrix barcode has eroded the reading safety margin provided by the error correction.
+} AIMGradeReport;
+
+typedef struct DataMatrixSizeOptions_struct {
+ unsigned int minSize; //Specifies the minimum size (in pixels) of the Data Matrix barcode in the image.
+ unsigned int maxSize; //Specifies the maximum size (in pixels) of the Data Matrix barcode in the image.
+ unsigned int quietZoneWidth; //Specifies the expected minimum size of the quiet zone, in pixels.
+} DataMatrixSizeOptions;
+
+typedef struct DataMatrixDescriptionOptions_struct {
+ float aspectRatio; //Specifies the ratio of the width of each Data Matrix barcode cell (in pixels) to the height of the Data Matrix barcode (in pixels).
+ unsigned int rows; //Specifies the number of rows in the Data Matrix barcode.
+ unsigned int columns; //Specifies the number of columns in the Data Matrix barcode.
+ int rectangle; //Set this element to TRUE to specify that the Data Matrix barcode is rectangular.
+ DataMatrixECC ecc; //Specifies the ECC used for this Data Matrix barcode.
+ DataMatrixPolarity polarity; //Specifies the data-to-background contrast for the Data Matrix barcode.
+ DataMatrixCellFillMode cellFill; //Specifies the fill percentage for a cell of the Data Matrix barcode that is in the "ON" state.
+ float minBorderIntegrity; //Specifies the minimum percentage of the border (locator pattern and timing pattern) the function should expect in the Data Matrix barcode.
+ DataMatrixMirrorMode mirrorMode; //Specifies if the Data Matrix barcode appears normally in the image or if the barcode appears mirrored in the image.
+} DataMatrixDescriptionOptions;
+
+typedef struct DataMatrixSearchOptions_struct {
+ DataMatrixRotationMode rotationMode; //Specifies the amount of Data Matrix barcode rotation the function should allow for.
+ int skipLocation; //If set to TRUE, specifies that the function should assume that the Data Matrix barcode occupies the entire image (or the entire search region).
+ unsigned int edgeThreshold; //Specifies the minimum contrast a pixel must have in order to be considered part of a matrix cell edge.
+ DataMatrixDemodulationMode demodulationMode; //Specifies the mode the function should use to demodulate (determine which cells are on and which cells are off) the Data Matrix barcode.
+ DataMatrixCellSampleSize cellSampleSize; //Specifies the sample size, in pixels, the function should take to determine if each cell is on or off.
+ DataMatrixCellFilterMode cellFilterMode; //Specifies the mode the function uses to determine the pixel value for each cell.
+ unsigned int skewDegreesAllowed; //Specifies the amount of skew in the Data Matrix barcode the function should allow for.
+ unsigned int maxIterations; //Specifies the maximum number of iterations before the function stops looking for the Data Matrix barcode.
+ unsigned int initialSearchVectorWidth; //Specifies the number of pixels the function should average together to determine the location of an edge.
+} DataMatrixSearchOptions;
+
+typedef struct DataMatrixReport_struct {
+ int found; //This element is TRUE if the function located and decoded a Data Matrix barcode and FALSE if the function failed to locate and decode a Data Matrix barcode.
+ int binary; //This element is TRUE if the Data Matrix barcode contains binary data and FALSE if the Data Matrix barcode contains text data.
+ unsigned char* data; //The data encoded in the Data Matrix barcode.
+ unsigned int dataLength; //The length of the data array.
+ PointFloat boundingBox[4]; //An array of four points describing the rectangle surrounding the Data Matrix barcode.
+ unsigned int numErrorsCorrected; //The number of errors the function corrected when decoding the Data Matrix barcode.
+ unsigned int numErasuresCorrected; //The number of erasures the function corrected when decoding the Data Matrix barcode.
+ float aspectRatio; //Specifies the aspect ratio of the Data Matrix barcode in the image, which equals the ratio of the width of a Data Matrix barcode cell (in pixels) to the height of a Data Matrix barcode cell (in pixels).
+ unsigned int rows; //The number of rows in the Data Matrix barcode.
+ unsigned int columns; //The number of columns in the Data Matrix barcode.
+ DataMatrixECC ecc; //The Error Correction Code (ECC) used by the Data Matrix barcode.
+ DataMatrixPolarity polarity; //The polarity of the Data Matrix barcode.
+ DataMatrixCellFillMode cellFill; //The cell fill percentage of the Data Matrix barcode.
+ float borderIntegrity; //The percentage of the Data Matrix barcode border that appears correctly in the image.
+ int mirrored; //This element is TRUE if the Data Matrix barcode appears mirrored in the image and FALSE if the Data Matrix barcode appears normally in the image.
+ unsigned int minimumEdgeStrength; //The strength of the weakest edge the function used to find the coarse location of the Data Matrix barcode in the image.
+ DataMatrixDemodulationMode demodulationMode; //The demodulation mode the function used to locate the Data Matrix barcode.
+ DataMatrixCellSampleSize cellSampleSize; //The cell sample size the function used to locate the Data Matrix barcode.
+ DataMatrixCellFilterMode cellFilterMode; //The cell filter mode the function used to locate the Data Matrix barcode.
+ unsigned int iterations; //The number of iterations the function took in attempting to locate the Data Matrix barcode.
+} DataMatrixReport;
+
+typedef struct JPEG2000FileAdvancedOptions_struct {
+ WaveletTransformMode waveletMode; //Determines which wavelet transform to use when writing the file.
+ int useMultiComponentTransform; //Set this parameter to TRUE to use an additional transform on RGB images.
+ unsigned int maxWaveletTransformLevel; //Specifies the maximum allowed level of wavelet transform.
+ float quantizationStepSize; //Specifies the absolute base quantization step size for derived quantization mode.
+} JPEG2000FileAdvancedOptions;
+
+typedef struct MatchGeometricPatternAdvancedOptions2_struct {
+ int minFeaturesUsed; //Specifies the minimum number of features the function uses when matching.
+ int maxFeaturesUsed; //Specifies the maximum number of features the function uses when matching.
+ int subpixelIterations; //Specifies the maximum number of incremental improvements used to refine matches with subpixel information.
+ double subpixelTolerance; //Specifies the maximum amount of change, in pixels, between consecutive incremental improvements in the match position before the function stops refining the match position.
+ int initialMatchListLength; //Specifies the maximum size of the match list.
+ float matchTemplateCurveScore; //Set this element to TRUE to specify that the function should calculate the match curve to template curve score and return it for each match result.
+ int correlationScore; //Set this element to TRUE to specify that the function should calculate the correlation score and return it for each match result.
+ double minMatchSeparationDistance; //Specifies the minimum separation distance, in pixels, between the origins of two matches that have unique positions.
+ double minMatchSeparationAngle; //Specifies the minimum angular difference, in degrees, between two matches that have unique angles.
+ double minMatchSeparationScale; //Specifies the minimum difference in scale, expressed as a percentage, between two matches that have unique scales.
+ double maxMatchOverlap; //Specifies the maximum amount of overlap, expressed as a percentage, allowed between the bounding rectangles of two unique matches.
+ int coarseResult; //Specifies whether you want the function to spend less time accurately estimating the location of a match.
+ int smoothContours; //Set this element to TRUE to specify smoothing be done on the contours of the inspection image before feature extraction.
+ int enableCalibrationSupport; //Set this element to TRUE to specify the algorithm treat the inspection image as a calibrated image.
+} MatchGeometricPatternAdvancedOptions2;
+
+typedef struct InspectionAlignment_struct {
+ PointFloat position; //The location of the center of the golden template in the image under inspection.
+ float rotation; //The rotation of the golden template in the image under inspection, in degrees.
+ float scale; //The percentage of the size of the area under inspection compared to the size of the golden template.
+} InspectionAlignment;
+
+typedef struct InspectionOptions_struct {
+ RegistrationMethod registrationMethod; //Specifies how the function registers the golden template and the target image.
+ NormalizationMethod normalizationMethod; //Specifies how the function normalizes the golden template to the target image.
+ int edgeThicknessToIgnore; //Specifies desired thickness of edges to be ignored.
+ float brightThreshold; //Specifies the threshold for areas where the target image is brighter than the golden template.
+ float darkThreshold; //Specifies the threshold for areas where the target image is darker than the golden template.
+ int binary; //Specifies whether the function should return a binary image giving the location of defects, or a grayscale image giving the intensity of defects.
+} InspectionOptions;
+
+typedef struct CharReport2_struct {
+ const char* character; //The character value.
+ PointFloat corner[4]; //An array of four points that describes the rectangle that surrounds the character.
+ int lowThreshold; //The minimum value of the threshold range used for this character.
+ int highThreshold; //The maximum value of the threshold range used for this character.
+ int classificationScore; //The degree to which the assigned character class represents the object better than the other character classes in the character set.
+ int verificationScore; //The similarity of the character and the reference character for the character class.
+ int verified; //This element is TRUE if a reference character was found for the character class and FALSE if a reference character was not found.
+} CharReport2;
+
+typedef struct CharInfo2_struct {
+ const char* charValue; //Retrieves the character value of the corresponding character in the character set.
+ const Image* charImage; //The image you used to train this character.
+ const Image* internalImage; //The internal representation that NI Vision uses to match objects to this character.
+ int isReferenceChar; //This element is TRUE if the character is the reference character for the character class.
+} CharInfo2;
+
+typedef struct ReadTextReport2_struct {
+ const char* readString; //The read string.
+ CharReport2* characterReport; //An array of reports describing the properties of each identified character.
+ int numCharacterReports; //The number of identified characters.
+} ReadTextReport2;
+
+typedef struct EllipseFeature_struct {
+ PointFloat position; //The location of the center of the ellipse.
+ double rotation; //The orientation of the semi-major axis of the ellipse with respect to the horizontal.
+ double minorRadius; //The length of the semi-minor axis of the ellipse.
+ double majorRadius; //The length of the semi-major axis of the ellipse.
+} EllipseFeature;
+
+typedef struct CircleFeature_struct {
+ PointFloat position; //The location of the center of the circle.
+ double radius; //The radius of the circle.
+} CircleFeature;
+
+typedef struct ConstCurveFeature_struct {
+ PointFloat position; //The center of the circle that this constant curve lies upon.
+ double radius; //The radius of the circle that this constant curve lies upon.
+ double startAngle; //When traveling along the constant curve from one endpoint to the next in a counterclockwise manner, this is the angular component of the vector originating at the center of the constant curve and pointing towards the first endpoint of the constant curve.
+ double endAngle; //When traveling along the constant curve from one endpoint to the next in a counterclockwise manner, this is the angular component of the vector originating at the center of the constant curve and pointing towards the second endpoint of the constant curve.
+} ConstCurveFeature;
+
+typedef struct RectangleFeature_struct {
+ PointFloat position; //The center of the rectangle.
+ PointFloat corner[4]; //The four corners of the rectangle.
+ double rotation; //The orientation of the rectangle with respect to the horizontal.
+ double width; //The width of the rectangle.
+ double height; //The height of the rectangle.
+} RectangleFeature;
+
+typedef struct LegFeature_struct {
+ PointFloat position; //The location of the leg feature.
+ PointFloat corner[4]; //The four corners of the leg feature.
+ double rotation; //The orientation of the leg with respect to the horizontal.
+ double width; //The width of the leg.
+ double height; //The height of the leg.
+} LegFeature;
+
+typedef struct CornerFeature_struct {
+ PointFloat position; //The location of the corner feature.
+ double rotation; //The angular component of the vector bisecting the corner from position.
+ double enclosedAngle; //The measure of the enclosed angle of the corner.
+ int isVirtual;
+} CornerFeature;
+
+typedef struct LineFeature_struct {
+ PointFloat startPoint; //The starting point of the line.
+ PointFloat endPoint; //The ending point of the line.
+ double length; //The length of the line measured in pixels from the start point to the end point.
+ double rotation; //The orientation of the line with respect to the horizontal.
+} LineFeature;
+
+typedef struct ParallelLinePairFeature_struct {
+ PointFloat firstStartPoint; //The starting point of the first line of the pair.
+ PointFloat firstEndPoint; //The ending point of the first line of the pair.
+ PointFloat secondStartPoint; //The starting point of the second line of the pair.
+ PointFloat secondEndPoint; //The ending point of the second line of the pair.
+ double rotation; //The orientation of the feature with respect to the horizontal.
+ double distance; //The distance from the first line to the second line.
+} ParallelLinePairFeature;
+
+typedef struct PairOfParallelLinePairsFeature_struct {
+ ParallelLinePairFeature firstParallelLinePair; //The first parallel line pair.
+ ParallelLinePairFeature secondParallelLinePair; //The second parallel line pair.
+ double rotation; //The orientation of the feature with respect to the horizontal.
+ double distance; //The distance from the midline of the first parallel line pair to the midline of the second parallel line pair.
+} PairOfParallelLinePairsFeature;
+
+typedef union GeometricFeature_union {
+ CircleFeature* circle; //A pointer to a CircleFeature.
+ EllipseFeature* ellipse; //A pointer to an EllipseFeature.
+ ConstCurveFeature* constCurve; //A pointer to a ConstCurveFeature.
+ RectangleFeature* rectangle; //A pointer to a RectangleFeature.
+ LegFeature* leg; //A pointer to a LegFeature.
+ CornerFeature* corner; //A pointer to a CornerFeature.
+ ParallelLinePairFeature* parallelLinePair; //A pointer to a ParallelLinePairFeature.
+ PairOfParallelLinePairsFeature* pairOfParallelLinePairs; //A pointer to a PairOfParallelLinePairsFeature.
+ LineFeature* line; //A pointer to a LineFeature.
+ ClosedCurveFeature* closedCurve; //A pointer to a ClosedCurveFeature.
+} GeometricFeature;
+
+typedef struct FeatureData_struct {
+ FeatureType type; //An enumeration representing the type of the feature.
+ PointFloat* contourPoints; //A set of points describing the contour of the feature.
+ int numContourPoints; //The number of points in the contourPoints array.
+ GeometricFeature feature; //The feature data specific to this type of feature.
+} FeatureData;
+
+typedef struct GeometricPatternMatch2_struct {
+ PointFloat position; //The location of the origin of the template in the match.
+ float rotation; //The rotation of the match relative to the template image, in degrees.
+ float scale; //The size of the match relative to the size of the template image, expressed as a percentage.
+ float score; //The accuracy of the match.
+ PointFloat corner[4]; //An array of four points describing the rectangle surrounding the template image.
+ int inverse; //This element is TRUE if the match is an inverse of the template image.
+ float occlusion; //The percentage of the match that is occluded.
+ float templateMatchCurveScore; //The accuracy of the match obtained by comparing the template curves to the curves in the match region.
+ float matchTemplateCurveScore; //The accuracy of the match obtained by comparing the curves in the match region to the template curves.
+ float correlationScore; //The accuracy of the match obtained by comparing the template image to the match region using a correlation metric that compares the two regions as a function of their pixel values.
+ String255 label; //The label corresponding to this match when the match is returned by imaqMatchMultipleGeometricPatterns().
+ FeatureData* featureData; //The features used in this match.
+ int numFeatureData; //The size of the featureData array.
+ PointFloat calibratedPosition; //The location of the origin of the template in the match.
+ float calibratedRotation; //The rotation of the match relative to the template image, in degrees.
+ PointFloat calibratedCorner[4]; //An array of four points describing the rectangle surrounding the template image.
+} GeometricPatternMatch2;
+
+typedef struct ClosedCurveFeature_struct {
+ PointFloat position; //The center of the closed curve feature.
+ double arcLength; //The arc length of the closed curve feature.
+} ClosedCurveFeature;
+
+typedef struct LineMatch_struct {
+ PointFloat startPoint; //The starting point of the matched line.
+ PointFloat endPoint; //The ending point of the matched line.
+ double length; //The length of the line measured in pixels from the start point to the end point.
+ double rotation; //The orientation of the matched line.
+ double score; //The score of the matched line.
+} LineMatch;
+
+typedef struct LineDescriptor_struct {
+ double minLength; //Specifies the minimum length of a line the function will return.
+ double maxLength; //Specifies the maximum length of a line the function will return.
+} LineDescriptor;
+
+typedef struct RectangleDescriptor_struct {
+ double minWidth; //Specifies the minimum width of a rectangle the algorithm will return.
+ double maxWidth; //Specifies the maximum width of a rectangle the algorithm will return.
+ double minHeight; //Specifies the minimum height of a rectangle the algorithm will return.
+ double maxHeight; //Specifies the maximum height of a rectangle the algorithm will return.
+} RectangleDescriptor;
+
+typedef struct RectangleMatch_struct {
+ PointFloat corner[4]; //The corners of the matched rectangle.
+ double rotation; //The orientation of the matched rectangle.
+ double width; //The width of the matched rectangle.
+ double height; //The height of the matched rectangle.
+ double score; //The score of the matched rectangle.
+} RectangleMatch;
+
+typedef struct EllipseDescriptor_struct {
+ double minMajorRadius; //Specifies the minimum length of the semi-major axis of an ellipse the function will return.
+ double maxMajorRadius; //Specifies the maximum length of the semi-major axis of an ellipse the function will return.
+ double minMinorRadius; //Specifies the minimum length of the semi-minor axis of an ellipse the function will return.
+ double maxMinorRadius; //Specifies the maximum length of the semi-minor axis of an ellipse the function will return.
+} EllipseDescriptor;
+
+typedef struct EllipseMatch_struct {
+ PointFloat position; //The location of the center of the matched ellipse.
+ double rotation; //The orientation of the matched ellipse.
+ double majorRadius; //The length of the semi-major axis of the matched ellipse.
+ double minorRadius; //The length of the semi-minor axis of the matched ellipse.
+ double score; //The score of the matched ellipse.
+} EllipseMatch;
+
+typedef struct CircleMatch_struct {
+ PointFloat position; //The location of the center of the matched circle.
+ double radius; //The radius of the matched circle.
+ double score; //The score of the matched circle.
+} CircleMatch;
+
+typedef struct CircleDescriptor_struct {
+ double minRadius; //Specifies the minimum radius of a circle the function will return.
+ double maxRadius; //Specifies the maximum radius of a circle the function will return.
+} CircleDescriptor;
+
+typedef struct ShapeDetectionOptions_struct {
+ unsigned int mode; //Specifies the method used when looking for the shape in the image.
+ RangeFloat* angleRanges; //An array of angle ranges, in degrees, where each range specifies how much you expect the shape to be rotated in the image.
+ int numAngleRanges; //The size of the orientationRanges array.
+ RangeFloat scaleRange; //A range that specifies the sizes of the shapes you expect to be in the image, expressed as a ratio percentage representing the size of the pattern in the image divided by size of the original pattern multiplied by 100.
+ double minMatchScore;
+} ShapeDetectionOptions;
+
+typedef struct Curve_struct {
+ PointFloat* points; //The points on the curve.
+ unsigned int numPoints; //The number of points in the curve.
+ int closed; //This element is TRUE if the curve is closed and FALSE if the curve is open.
+ double curveLength; //The length of the curve.
+ double minEdgeStrength; //The lowest edge strength detected on the curve.
+ double maxEdgeStrength; //The highest edge strength detected on the curve.
+ double averageEdgeStrength; //The average of all edge strengths detected on the curve.
+} Curve;
+
+typedef struct CurveOptions_struct {
+ ExtractionMode extractionMode; //Specifies the method the function uses to identify curves in the image.
+ int threshold; //Specifies the minimum contrast a seed point must have in order to begin a curve.
+ EdgeFilterSize filterSize; //Specifies the width of the edge filter the function uses to identify curves in the image.
+ int minLength; //Specifies the length, in pixels, of the smallest curve the function will extract.
+ int rowStepSize; //Specifies the distance, in the y direction, between lines the function inspects for curve seed points.
+ int columnStepSize; //Specifies the distance, in the x direction, between columns the function inspects for curve seed points.
+ int maxEndPointGap; //Specifies the maximum gap, in pixels, between the endpoints of a curve that the function identifies as a closed curve.
+ int onlyClosed; //Set this element to TRUE to specify that the function should only identify closed curves in the image.
+ int subpixelAccuracy; //Set this element to TRUE to specify that the function identifies the location of curves with subpixel accuracy by interpolating between points to find the crossing of threshold.
+} CurveOptions;
+
+typedef struct Barcode2DInfo_struct {
+ Barcode2DType type; //The type of the 2D barcode.
+ int binary; //This element is TRUE if the 2D barcode contains binary data and FALSE if the 2D barcode contains text data.
+ unsigned char* data; //The data encoded in the 2D barcode.
+ unsigned int dataLength; //The length of the data array.
+ PointFloat boundingBox[4]; //An array of four points describing the rectangle surrounding the 2D barcode.
+ unsigned int numErrorsCorrected; //The number of errors the function corrected when decoding the 2D barcode.
+ unsigned int numErasuresCorrected; //The number of erasures the function corrected when decoding the 2D barcode.
+ unsigned int rows; //The number of rows in the 2D barcode.
+ unsigned int columns; //The number of columns in the 2D barcode.
+} Barcode2DInfo;
+
+typedef struct DataMatrixOptions_struct {
+ Barcode2DSearchMode searchMode; //Specifies the mode the function uses to search for barcodes.
+ Barcode2DContrast contrast; //Specifies the contrast of the barcodes that the function searches for.
+ Barcode2DCellShape cellShape; //Specifies the shape of the barcode data cells, which affects how the function decodes the barcode.
+ Barcode2DShape barcodeShape; //Specifies the shape of the barcodes that the function searches for.
+ DataMatrixSubtype subtype; //Specifies the Data Matrix subtypes of the barcodes that the function searches for.
+} DataMatrixOptions;
+
+typedef struct ClassifierAccuracyReport_struct {
+ int size; //The size of the arrays in this structure.
+ float accuracy; //The overall accuracy of the classifier, from 0 to 1000.
+ char** classNames; //The names of the classes of this classifier.
+ double* classAccuracy; //An array of size elements that contains accuracy information for each class.
+ double* classPredictiveValue; //An array containing size elements that contains the predictive values of each class.
+ int** classificationDistribution; //A two-dimensional array containing information about how the classifier classifies its samples.
+} ClassifierAccuracyReport;
+
+typedef struct NearestNeighborClassResult_struct {
+ char* className; //The name of the class.
+ float standardDeviation; //The standard deviation of the members of this class.
+ int count; //The number of samples in this class.
+} NearestNeighborClassResult;
+
+typedef struct NearestNeighborTrainingReport_struct {
+ float** classDistancesTable; //The confidence in the training.
+ NearestNeighborClassResult* allScores; //All classes and their scores.
+ int allScoresSize; //The number of entries in allScores.
+} NearestNeighborTrainingReport;
+
+typedef struct ParticleClassifierPreprocessingOptions_struct {
+ int manualThreshold; //Set this element to TRUE to specify the threshold range manually.
+ RangeFloat manualThresholdRange; //If a manual threshold is being done, the range of pixels to keep.
+ ThresholdMethod autoThresholdMethod; //If an automatic threshold is being done, the method used to calculate the threshold range.
+ RangeFloat limits; //The limits on the automatic threshold range.
+ ParticleType particleType; //Specifies what kind of particles to look for.
+ int rejectBorder; //Set this element to TRUE to reject border particles.
+ int numErosions; //The number of erosions to perform.
+} ParticleClassifierPreprocessingOptions;
+
+typedef struct ClassifierSampleInfo_struct {
+ char* className; //The name of the class this sample is in.
+ double* featureVector; //The feature vector of this sample, or NULL if this is not a custom classifier session.
+ int featureVectorSize; //The number of elements in the feature vector.
+ Image* thumbnail; //A thumbnail image of this sample, or NULL if no image was specified.
+} ClassifierSampleInfo;
+
+typedef struct ClassScore_struct {
+ char* className; //The name of the class.
+ float distance; //The distance from the item to this class.
+} ClassScore;
+
+typedef struct ClassifierReport_struct {
+ char* bestClassName; //The name of the best class for the sample.
+ float classificationScore; //The similarity of the sample and the two closest classes in the classifier.
+ float identificationScore; //The similarity of the sample and the assigned class.
+ ClassScore* allScores; //All classes and their scores.
+ int allScoresSize; //The number of entries in allScores.
+} ClassifierReport;
+
+typedef struct NearestNeighborOptions_struct {
+ NearestNeighborMethod method; //The method to use.
+ NearestNeighborMetric metric; //The metric to use.
+ int k; //The value of k, if the IMAQ_K_NEAREST_NEIGHBOR method is used.
+} NearestNeighborOptions;
+
+typedef struct ParticleClassifierOptions_struct {
+ float scaleDependence; //The relative importance of scale when classifying particles.
+ float mirrorDependence; //The relative importance of mirror symmetry when classifying particles.
+} ParticleClassifierOptions;
+
+typedef struct RGBU64Value_struct {
+ unsigned short B; //The blue value of the color.
+ unsigned short G; //The green value of the color.
+ unsigned short R; //The red value of the color.
+ unsigned short alpha; //The alpha value of the color, which represents extra information about a color image, such as gamma correction.
+} RGBU64Value;
+
+typedef struct GeometricPatternMatch_struct {
+ PointFloat position; //The location of the origin of the template in the match.
+ float rotation; //The rotation of the match relative to the template image, in degrees.
+ float scale; //The size of the match relative to the size of the template image, expressed as a percentage.
+ float score; //The accuracy of the match.
+ PointFloat corner[4]; //An array of four points describing the rectangle surrounding the template image.
+ int inverse; //This element is TRUE if the match is an inverse of the template image.
+ float occlusion; //The percentage of the match that is occluded.
+ float templateMatchCurveScore; //The accuracy of the match obtained by comparing the template curves to the curves in the match region.
+ float matchTemplateCurveScore; //The accuracy of the match obtained by comparing the curves in the match region to the template curves.
+ float correlationScore; //The accuracy of the match obtained by comparing the template image to the match region using a correlation metric that compares the two regions as a function of their pixel values.
+} GeometricPatternMatch;
+
+typedef struct MatchGeometricPatternAdvancedOptions_struct {
+ int minFeaturesUsed; //Specifies the minimum number of features the function uses when matching.
+ int maxFeaturesUsed; //Specifies the maximum number of features the function uses when matching.
+ int subpixelIterations; //Specifies the maximum number of incremental improvements used to refine matches with subpixel information.
+ double subpixelTolerance; //Specifies the maximum amount of change, in pixels, between consecutive incremental improvements in the match position before the function stops refining the match position.
+ int initialMatchListLength; //Specifies the maximum size of the match list.
+ int matchTemplateCurveScore; //Set this element to TRUE to specify that the function should calculate the match curve to template curve score and return it for each match result.
+ int correlationScore; //Set this element to TRUE to specify that the function should calculate the correlation score and return it for each match result.
+ double minMatchSeparationDistance; //Specifies the minimum separation distance, in pixels, between the origins of two matches that have unique positions.
+ double minMatchSeparationAngle; //Specifies the minimum angular difference, in degrees, between two matches that have unique angles.
+ double minMatchSeparationScale; //Specifies the minimum difference in scale, expressed as a percentage, between two matches that have unique scales.
+ double maxMatchOverlap; //Specifies the maximum amount of overlap, expressed as a percentage, allowed between the bounding rectangles of two unique matches.
+ int coarseResult; //Specifies whether you want the function to spend less time accurately estimating the location of a match.
+} MatchGeometricPatternAdvancedOptions;
+
+typedef struct MatchGeometricPatternOptions_struct {
+ unsigned int mode; //Specifies the method imaqMatchGeometricPattern() uses when looking for the pattern in the image.
+ int subpixelAccuracy; //Set this element to TRUE to specify that the function should calculate match locations with subpixel accuracy.
+ RangeFloat* angleRanges; //An array of angle ranges, in degrees, where each range specifies how much you expect the template to be rotated in the image.
+ int numAngleRanges; //Number of angle ranges in the angleRanges array.
+ RangeFloat scaleRange; //A range that specifies the sizes of the pattern you expect to be in the image, expressed as a ratio percentage representing the size of the pattern in the image divided by size of the original pattern multiplied by 100.
+ RangeFloat occlusionRange; //A range that specifies the percentage of the pattern you expect to be occluded in the image.
+ int numMatchesRequested; //Number of valid matches expected.
+ float minMatchScore; //The minimum score a match can have for the function to consider the match valid.
+} MatchGeometricPatternOptions;
+
+typedef struct LearnGeometricPatternAdvancedOptions_struct {
+ int minRectLength; //Specifies the minimum length for each side of a rectangular feature.
+ double minRectAspectRatio; //Specifies the minimum aspect ratio of a rectangular feature.
+ int minRadius; //Specifies the minimum radius for a circular feature.
+ int minLineLength; //Specifies the minimum length for a linear feature.
+ double minFeatureStrength; //Specifies the minimum strength for a feature.
+ int maxFeaturesUsed; //Specifies the maximum number of features the function uses when learning.
+ int maxPixelDistanceFromLine; //Specifies the maximum number of pixels between an edge pixel and a linear feature for the function to consider that edge pixel as part of the linear feature.
+} LearnGeometricPatternAdvancedOptions;
+
+typedef struct FitEllipseOptions_struct {
+ int rejectOutliers; //Whether to use every given point or only a subset of the points to fit the ellipse.
+ double minScore; //Specifies the required quality of the fitted ellipse.
+ double pixelRadius; //The acceptable distance, in pixels, that a point determined to belong to the ellipse can be from the circumference of the ellipse.
+ int maxIterations; //Specifies the number of refinement iterations you allow the function to perform on the initial subset of points.
+} FitEllipseOptions;
+
+typedef struct FitCircleOptions_struct {
+ int rejectOutliers; //Whether to use every given point or only a subset of the points to fit the circle.
+ double minScore; //Specifies the required quality of the fitted circle.
+ double pixelRadius; //The acceptable distance, in pixels, that a point determined to belong to the circle can be from the circumference of the circle.
+ int maxIterations; //Specifies the number of refinement iterations you allow the function to perform on the initial subset of points.
+} FitCircleOptions;
+
+typedef struct ConstructROIOptions2_struct {
+ int windowNumber; //The window number of the image window.
+ const char* windowTitle; //Specifies the message string that the function displays in the title bar of the window.
+ PaletteType type; //The palette type to use.
+ RGBValue* palette; //If type is IMAQ_PALETTE_USER, this array is the palette of colors to use with the window.
+ int numColors; //If type is IMAQ_PALETTE_USER, this element is the number of colors in the palette array.
+ unsigned int maxContours; //The maximum number of contours the user will be able to select.
+} ConstructROIOptions2;
+
+typedef struct HSLValue_struct {
+ unsigned char L; //The color luminance.
+ unsigned char S; //The color saturation.
+ unsigned char H; //The color hue.
+ unsigned char alpha; //The alpha value of the color, which represents extra information about a color image, such as gamma correction.
+} HSLValue;
+
+typedef struct HSVValue_struct {
+ unsigned char V; //The color value.
+ unsigned char S; //The color saturation.
+ unsigned char H; //The color hue.
+ unsigned char alpha; //The alpha value of the color, which represents extra information about a color image, such as gamma correction.
+} HSVValue;
+
+typedef struct HSIValue_struct {
+ unsigned char I; //The color intensity.
+ unsigned char S; //The color saturation.
+ unsigned char H; //The color hue.
+ unsigned char alpha; //The alpha value of the color, which represents extra information about a color image, such as gamma correction.
+} HSIValue;
+
+typedef struct CIELabValue_struct {
+ double b; //The yellow/blue information of the color.
+ double a; //The red/green information of the color.
+ double L; //The color lightness.
+ unsigned char alpha; //The alpha value of the color, which represents extra information about a color image, such as gamma correction.
+} CIELabValue;
+
+typedef struct CIEXYZValue_struct {
+ double Z; //The Z color information.
+ double Y; //The color luminance.
+ double X; //The X color information.
+ unsigned char alpha; //The alpha value of the color, which represents extra information about a color image, such as gamma correction.
+} CIEXYZValue;
+
+typedef union Color2_union {
+ RGBValue rgb; //The information needed to describe a color in the RGB (Red, Green, and Blue) color space.
+ HSLValue hsl; //The information needed to describe a color in the HSL (Hue, Saturation, and Luminance) color space.
+ HSVValue hsv; //The information needed to describe a color in the HSI (Hue, Saturation, and Value) color space.
+ HSIValue hsi; //The information needed to describe a color in the HSI (Hue, Saturation, and Intensity) color space.
+ CIELabValue cieLab; //The information needed to describe a color in the CIE L*a*b* (L, a, b) color space.
+ CIEXYZValue cieXYZ; //The information needed to describe a color in the CIE XYZ (X, Y, Z) color space.
+ int rawValue; //The integer value for the data in the color union.
+} Color2;
+
+typedef struct BestEllipse2_struct {
+ PointFloat center; //The coordinate location of the center of the ellipse.
+ PointFloat majorAxisStart; //The coordinate location of the start of the major axis of the ellipse.
+ PointFloat majorAxisEnd; //The coordinate location of the end of the major axis of the ellipse.
+ PointFloat minorAxisStart; //The coordinate location of the start of the minor axis of the ellipse.
+ PointFloat minorAxisEnd; //The coordinate location of the end of the minor axis of the ellipse.
+ double area; //The area of the ellipse.
+ double perimeter; //The length of the perimeter of the ellipse.
+ double error; //Represents the least square error of the fitted ellipse to the entire set of points.
+ int valid; //This element is TRUE if the function achieved the minimum score within the number of allowed refinement iterations and FALSE if the function did not achieve the minimum score.
+ int* pointsUsed; //An array of the indexes for the points array indicating which points the function used to fit the ellipse.
+ int numPointsUsed; //The number of points the function used to fit the ellipse.
+} BestEllipse2;
+
+typedef struct LearnPatternAdvancedOptions_struct {
+ LearnPatternAdvancedShiftOptions* shiftOptions; //Use this element to control the behavior of imaqLearnPattern2() during the shift-invariant learning phase.
+ LearnPatternAdvancedRotationOptions* rotationOptions; //Use this element to control the behavior of imaqLearnPattern2()during the rotation-invariant learning phase.
+} LearnPatternAdvancedOptions;
+
+typedef struct AVIInfo_struct {
+ unsigned int width; //The width of each frame.
+ unsigned int height; //The height of each frame.
+ ImageType imageType; //The type of images this AVI contains.
+ unsigned int numFrames; //The number of frames in the AVI.
+ unsigned int framesPerSecond; //The number of frames per second this AVI should be shown at.
+ char* filterName; //The name of the compression filter used to create this AVI.
+ int hasData; //Specifies whether this AVI has data attached to each frame or not.
+ unsigned int maxDataSize; //If this AVI has data, the maximum size of the data in each frame.
+} AVIInfo;
+
+typedef struct LearnPatternAdvancedShiftOptions_struct {
+ int initialStepSize; //The largest number of image pixels to shift the sample across the inspection image during the initial phase of shift-invariant matching.
+ int initialSampleSize; //Specifies the number of template pixels that you want to include in a sample for the initial phase of shift-invariant matching.
+ double initialSampleSizeFactor; //Specifies the size of the sample for the initial phase of shift-invariant matching as a percent of the template size, in pixels.
+ int finalSampleSize; //Specifies the number of template pixels you want to add to initialSampleSize for the final phase of shift-invariant matching.
+ double finalSampleSizeFactor; //Specifies the size of the sample for the final phase of shift-invariant matching as a percent of the edge points in the template, in pixels.
+ int subpixelSampleSize; //Specifies the number of template pixels that you want to include in a sample for the subpixel phase of shift-invariant matching.
+ double subpixelSampleSizeFactor; //Specifies the size of the sample for the subpixel phase of shift-invariant matching as a percent of the template size, in pixels.
+} LearnPatternAdvancedShiftOptions;
+
+typedef struct LearnPatternAdvancedRotationOptions_struct {
+ SearchStrategy searchStrategySupport; //Specifies the aggressiveness of the rotation search strategy available during the matching phase.
+ int initialStepSize; //The largest number of image pixels to shift the sample across the inspection image during the initial phase of matching.
+ int initialSampleSize; //Specifies the number of template pixels that you want to include in a sample for the initial phase of rotation-invariant matching.
+ double initialSampleSizeFactor; //Specifies the size of the sample for the initial phase of rotation-invariant matching as a percent of the template size, in pixels.
+ int initialAngularAccuracy; //Sets the angle accuracy, in degrees, to use during the initial phase of rotation-invariant matching.
+ int finalSampleSize; //Specifies the number of template pixels you want to add to initialSampleSize for the final phase of rotation-invariant matching.
+ double finalSampleSizeFactor; //Specifies the size of the sample for the final phase of rotation-invariant matching as a percent of the edge points in the template, in pixels.
+ int finalAngularAccuracy; //Sets the angle accuracy, in degrees, to use during the final phase of the rotation-invariant matching.
+ int subpixelSampleSize; //Specifies the number of template pixels that you want to include in a sample for the subpixel phase of rotation-invariant matching.
+ double subpixelSampleSizeFactor; //Specifies the size of the sample for the subpixel phase of rotation-invariant matching as a percent of the template size, in pixels.
+} LearnPatternAdvancedRotationOptions;
+
+typedef struct MatchPatternAdvancedOptions_struct {
+ int subpixelIterations; //Defines the maximum number of incremental improvements used to refine matching using subpixel information.
+ double subpixelTolerance; //Defines the maximum amount of change, in pixels, between consecutive incremental improvements in the match position that you want to trigger the end of the refinement process.
+ int initialMatchListLength; //Specifies the maximum size of the match list.
+ int matchListReductionFactor; //Specifies the reduction of the match list as matches are refined.
+ int initialStepSize; //Specifies the number of pixels to shift the sample across the inspection image during the initial phase of shift-invariant matching.
+ SearchStrategy searchStrategy; //Specifies the aggressiveness of the rotation search strategy.
+ int intermediateAngularAccuracy; //Specifies the accuracy to use during the intermediate phase of rotation-invariant matching.
+} MatchPatternAdvancedOptions;
+
+typedef struct ParticleFilterCriteria2_struct {
+ MeasurementType parameter; //The morphological measurement that the function uses for filtering.
+ float lower; //The lower bound of the criteria range.
+ float upper; //The upper bound of the criteria range.
+ int calibrated; //Set this element to TRUE to take calibrated measurements.
+ int exclude; //Set this element to TRUE to indicate that a match occurs when the measurement is outside the criteria range.
+} ParticleFilterCriteria2;
+
+typedef struct BestCircle2_struct {
+ PointFloat center; //The coordinate location of the center of the circle.
+ double radius; //The radius of the circle.
+ double area; //The area of the circle.
+ double perimeter; //The length of the perimeter of the circle.
+ double error; //Represents the least square error of the fitted circle to the entire set of points.
+ int valid; //This element is TRUE if the function achieved the minimum score within the number of allowed refinement iterations and FALSE if the function did not achieve the minimum score.
+ int* pointsUsed; //An array of the indexes for the points array indicating which points the function used to fit the circle.
+ int numPointsUsed; //The number of points the function used to fit the circle.
+} BestCircle2;
+
+typedef struct OCRSpacingOptions_struct {
+ int minCharSpacing; //The minimum number of pixels that must be between two characters for NI Vision to train or read the characters separately.
+ int minCharSize; //The minimum number of pixels required for an object to be a potentially identifiable character.
+ int maxCharSize; //The maximum number of pixels required for an object to be a potentially identifiable character.
+ int maxHorizontalElementSpacing; //The maximum horizontal spacing, in pixels, allowed between character elements to train or read the character elements as a single character.
+ int maxVerticalElementSpacing; //The maximum vertical element spacing in pixels.
+ int minBoundingRectWidth; //The minimum possible width, in pixels, for a character bounding rectangle.
+ int maxBoundingRectWidth; //The maximum possible width, in pixels, for a character bounding rectangle.
+ int minBoundingRectHeight; //The minimum possible height, in pixels, for a character bounding rectangle.
+ int maxBoundingRectHeight; //The maximum possible height, in pixels, for a character bounding rectangle.
+ int autoSplit; //Set this element to TRUE to automatically adjust the location of the character bounding rectangle when characters overlap vertically.
+} OCRSpacingOptions;
+
+typedef struct OCRProcessingOptions_struct {
+ ThresholdMode mode; //The thresholding mode.
+ int lowThreshold; //The low threshold value when you set mode to IMAQ_FIXED_RANGE.
+ int highThreshold; //The high threshold value when you set mode to IMAQ_FIXED_RANGE.
+ int blockCount; //The number of blocks for threshold calculation algorithms that require blocks.
+ int fastThreshold; //Set this element to TRUE to use a faster, less accurate threshold calculation algorithm.
+ int biModalCalculation; //Set this element to TRUE to calculate both the low and high threshold values when using the fast thresholding method.
+ int darkCharacters; //Set this element to TRUE to read or train dark characters on a light background.
+ int removeParticlesTouchingROI; //Set this element to TRUE to remove the particles touching the ROI.
+ int erosionCount; //The number of erosions to perform.
+} OCRProcessingOptions;
+
+typedef struct ReadTextOptions_struct {
+ String255 validChars[255]; //An array of strings that specifies the valid characters.
+ int numValidChars; //The number of strings in the validChars array that you have initialized.
+ char substitutionChar; //The character to substitute for objects that the function cannot match with any of the trained characters.
+ ReadStrategy readStrategy; //The read strategy, which determines how closely the function analyzes images in the reading process to match objects with trained characters.
+ int acceptanceLevel; //The minimum acceptance level at which an object is considered a trained character.
+ int aspectRatio; //The maximum aspect ratio variance percentage for valid characters.
+ ReadResolution readResolution; //The read resolution, which determines how much of the trained character data the function uses to match objects to trained characters.
+} ReadTextOptions;
+
+typedef struct CharInfo_struct {
+ const char* charValue; //Retrieves the character value of the corresponding character in the character set.
+ const Image* charImage; //The image you used to train this character.
+ const Image* internalImage; //The internal representation that NI Vision uses to match objects to this character.
+} CharInfo;
+
+#if !defined(USERINT_HEADER) && !defined(_CVI_RECT_DEFINED)
+typedef struct Rect_struct {
+ int top; //Location of the top edge of the rectangle.
+ int left; //Location of the left edge of the rectangle.
+ int height; //Height of the rectangle.
+ int width; //Width of the rectangle.
+} Rect;
+#define _CVI_RECT_DEFINED
+#endif
+
+typedef struct CharReport_struct {
+ const char* character; //The character value.
+ PointFloat corner[4]; //An array of four points that describes the rectangle that surrounds the character.
+ int reserved; //This element is reserved.
+ int lowThreshold; //The minimum value of the threshold range used for this character.
+ int highThreshold; //The maximum value of the threshold range used for this character.
+} CharReport;
+
+typedef struct ReadTextReport_struct {
+ const char* readString; //The read string.
+ const CharReport* characterReport; //An array of reports describing the properties of each identified character.
+ int numCharacterReports; //The number of identified characters.
+} ReadTextReport;
+
+#if !defined(USERINT_HEADER) && !defined(_CVI_POINT_DEFINED)
+typedef struct Point_struct {
+ int x; //The x-coordinate of the point.
+ int y; //The y-coordinate of the point.
+} Point;
+#define _CVI_POINT_DEFINED
+#endif
+
+typedef struct Annulus_struct {
+ Point center; //The coordinate location of the center of the annulus.
+ int innerRadius; //The internal radius of the annulus.
+ int outerRadius; //The external radius of the annulus.
+ double startAngle; //The start angle, in degrees, of the annulus.
+ double endAngle; //The end angle, in degrees, of the annulus.
+} Annulus;
+
+typedef struct EdgeLocationReport_struct {
+ PointFloat* edges; //The coordinate location of all edges detected by the search line.
+ int numEdges; //The number of points in the edges array.
+} EdgeLocationReport;
+
+typedef struct EdgeOptions_struct {
+ unsigned threshold; //Specifies the threshold value for the contrast of the edge.
+ unsigned width; //The number of pixels that the function averages to find the contrast at either side of the edge.
+ unsigned steepness; //The span, in pixels, of the slope of the edge projected along the path specified by the input points.
+ InterpolationMethod subpixelType; //The method for interpolating.
+ unsigned subpixelDivisions; //The number of samples the function obtains from a pixel.
+} EdgeOptions;
+
+typedef struct EdgeReport_struct {
+ float location; //The location of the edge from the first point in the points array.
+ float contrast; //The contrast at the edge.
+ PolarityType polarity; //The polarity of the edge.
+ float reserved; //This element is reserved.
+ PointFloat coordinate; //The coordinates of the edge.
+} EdgeReport;
+
+typedef struct ExtremeReport_struct {
+ double location; //The locations of the extreme.
+ double amplitude; //The amplitude of the extreme.
+ double secondDerivative; //The second derivative of the extreme.
+} ExtremeReport;
+
+typedef struct FitLineOptions_struct {
+ float minScore; //Specifies the required quality of the fitted line.
+ float pixelRadius; //Specifies the neighborhood pixel relationship for the initial subset of points being used.
+ int numRefinements; //Specifies the number of refinement iterations you allow the function to perform on the initial subset of points.
+} FitLineOptions;
+
+typedef struct DisplayMapping_struct {
+ MappingMethod method; //Describes the method for converting 16-bit pixels to 8-bit pixels.
+ int minimumValue; //When method is IMAQ_RANGE, minimumValue represents the value that is mapped to 0.
+ int maximumValue; //When method is IMAQ_RANGE, maximumValue represents the value that is mapped to 255.
+ int shiftCount; //When method is IMAQ_DOWNSHIFT, shiftCount represents the number of bits the function right-shifts the 16-bit pixel values.
+} DisplayMapping;
+
+typedef struct DetectExtremesOptions_struct {
+ double threshold; //Defines which extremes are too small.
+ int width; //Specifies the number of consecutive data points the function uses in the quadratic least-squares fit.
+} DetectExtremesOptions;
+
+typedef struct ImageInfo_struct {
+ CalibrationUnit imageUnit; //If you set calibration information with imaqSetSimpleCalibrationInfo(), imageUnit is the calibration unit.
+ float stepX; //If you set calibration information with imaqSetCalibrationInfo(), stepX is the distance in the calibration unit between two pixels in the x direction.
+ float stepY; //If you set calibration information with imaqSetCalibrationInfo(), stepY is the distance in the calibration unit between two pixels in the y direction.
+ ImageType imageType; //The type of the image.
+ int xRes; //The number of columns in the image.
+ int yRes; //The number of rows in the image.
+ int xOffset; //If you set mask offset information with imaqSetMaskOffset(), xOffset is the offset of the mask origin in the x direction.
+ int yOffset; //If you set mask offset information with imaqSetMaskOffset(), yOffset is the offset of the mask origin in the y direction.
+ int border; //The number of border pixels around the image.
+ int pixelsPerLine; //The number of pixels stored for each line of the image.
+ void* reserved0; //This element is reserved.
+ void* reserved1; //This element is reserved.
+ void* imageStart; //A pointer to pixel (0,0).
+} ImageInfo;
+
+typedef struct LCDOptions_struct {
+ int litSegments; //Set this parameter to TRUE if the segments are brighter than the background.
+ float threshold; //Determines whether a segment is ON or OFF.
+ int sign; //Indicates whether the function must read the sign of the indicator.
+ int decimalPoint; //Determines whether to look for a decimal separator after each digit.
+} LCDOptions;
+
+typedef struct LCDReport_struct {
+ const char* text; //A string of the characters of the LCD.
+ LCDSegments* segmentInfo; //An array of LCDSegment structures describing which segments of each digit are on.
+ int numCharacters; //The number of characters that the function reads.
+ int reserved; //This element is reserved.
+} LCDReport;
+
+typedef struct LCDSegments_struct {
+ unsigned a:1; //True if the a segment is on.
+ unsigned b:1; //True if the b segment is on.
+ unsigned c:1; //True if the c segment is on.
+ unsigned d:1; //True if the d segment is on.
+ unsigned e:1; //True if the e segment is on.
+ unsigned f:1; //True if the f segment is on.
+ unsigned g:1; //True if the g segment is on.
+ unsigned reserved:25; //This element is reserved.
+} LCDSegments;
+
+typedef struct LearnCalibrationOptions_struct {
+ CalibrationMode mode; //Specifies the type of algorithm you want to use to reduce distortion in your image.
+ ScalingMethod method; //Defines the scaling method correction functions use to correct the image.
+ CalibrationROI roi; //Specifies the ROI correction functions use when correcting an image.
+ int learnMap; //Set this element to TRUE if you want the function to calculate and store an error map during the learning process.
+ int learnTable; //Set this element to TRUE if you want the function to calculate and store the correction table.
+} LearnCalibrationOptions;
+
+typedef struct LearnColorPatternOptions_struct {
+ LearningMode learnMode; //Specifies the invariance mode the function uses when learning the pattern.
+ ImageFeatureMode featureMode; //Specifies the features the function uses when learning the color pattern.
+ int threshold; //Specifies the saturation threshold the function uses to distinguish between two colors that have the same hue values.
+ ColorIgnoreMode ignoreMode; //Specifies whether the function excludes certain colors from the color features of the template image.
+ ColorInformation* colorsToIgnore; //An array of ColorInformation structures providing a set of colors to exclude from the color features of the template image.
+ int numColorsToIgnore; //The number of ColorInformation structures in the colorsToIgnore array.
+} LearnColorPatternOptions;
+
+typedef struct Line_struct {
+ Point start; //The coordinate location of the start of the line.
+ Point end; //The coordinate location of the end of the line.
+} Line;
+
+typedef struct LinearAverages_struct {
+ float* columnAverages; //An array containing the mean pixel value of each column.
+ int columnCount; //The number of elements in the columnAverages array.
+ float* rowAverages; //An array containing the mean pixel value of each row.
+ int rowCount; //The number of elements in the rowAverages array.
+ float* risingDiagAverages; //An array containing the mean pixel value of each diagonal running from the lower left to the upper right of the inspected area of the image.
+ int risingDiagCount; //The number of elements in the risingDiagAverages array.
+ float* fallingDiagAverages; //An array containing the mean pixel value of each diagonal running from the upper left to the lower right of the inspected area of the image.
+ int fallingDiagCount; //The number of elements in the fallingDiagAverages array.
+} LinearAverages;
+
+typedef struct LineProfile_struct {
+ float* profileData; //An array containing the value of each pixel in the line.
+ Rect boundingBox; //The bounding rectangle of the line.
+ float min; //The smallest pixel value in the line profile.
+ float max; //The largest pixel value in the line profile.
+ float mean; //The mean value of the pixels in the line profile.
+ float stdDev; //The standard deviation of the line profile.
+ int dataCount; //The size of the profileData array.
+} LineProfile;
+
+typedef struct MatchColorPatternOptions_struct {
+ MatchingMode matchMode; //Specifies the method to use when looking for the color pattern in the image.
+ ImageFeatureMode featureMode; //Specifies the features to use when looking for the color pattern in the image.
+ int minContrast; //Specifies the minimum contrast expected in the image.
+ int subpixelAccuracy; //Set this parameter to TRUE to return areas in the image that match the pattern area with subpixel accuracy.
+ RotationAngleRange* angleRanges; //An array of angle ranges, in degrees, where each range specifies how much you expect the pattern to be rotated in the image.
+ int numRanges; //Number of angle ranges in the angleRanges array.
+ double colorWeight; //Determines the percent contribution of the color score to the final color pattern matching score.
+ ColorSensitivity sensitivity; //Specifies the sensitivity of the color information in the image.
+ SearchStrategy strategy; //Specifies how the color features of the image are used during the search phase.
+ int numMatchesRequested; //Number of valid matches expected.
+ float minMatchScore; //The minimum score a match can have for the function to consider the match valid.
+} MatchColorPatternOptions;
+
+typedef struct HistogramReport_struct {
+ int* histogram; //An array describing the number of pixels that fell into each class.
+ int histogramCount; //The number of elements in the histogram array.
+ float min; //The smallest pixel value that the function classified.
+ float max; //The largest pixel value that the function classified.
+ float start; //The smallest pixel value that fell into the first class.
+ float width; //The size of each class.
+ float mean; //The mean value of the pixels that the function classified.
+ float stdDev; //The standard deviation of the pixels that the function classified.
+ int numPixels; //The number of pixels that the function classified.
+} HistogramReport;
+
+typedef struct ArcInfo_struct {
+ Rect boundingBox; //The coordinate location of the bounding box of the arc.
+ double startAngle; //The counterclockwise angle from the x-axis in degrees to the start of the arc.
+ double endAngle; //The counterclockwise angle from the x-axis in degrees to the end of the arc.
+} ArcInfo;
+
+typedef struct AxisReport_struct {
+ PointFloat origin; //The origin of the coordinate system, which is the intersection of the two axes of the coordinate system.
+ PointFloat mainAxisEnd; //The end of the main axis, which is the result of the computation of the intersection of the main axis with the rectangular search area.
+ PointFloat secondaryAxisEnd; //The end of the secondary axis, which is the result of the computation of the intersection of the secondary axis with the rectangular search area.
+} AxisReport;
+
+typedef struct BarcodeInfo_struct {
+ const char* outputString; //A string containing the decoded barcode data.
+ int size; //The size of the output string.
+ char outputChar1; //The contents of this character depend on the barcode type.
+ char outputChar2; //The contents of this character depend on the barcode type.
+ double confidenceLevel; //A quality measure of the decoded barcode ranging from 0 to 100, with 100 being the best.
+ BarcodeType type; //The type of barcode.
+} BarcodeInfo;
+
+typedef struct BCGOptions_struct {
+ float brightness; //Adjusts the brightness of the image.
+ float contrast; //Adjusts the contrast of the image.
+ float gamma; //Performs gamma correction.
+} BCGOptions;
+
+typedef struct BestCircle_struct {
+ PointFloat center; //The coordinate location of the center of the circle.
+ double radius; //The radius of the circle.
+ double area; //The area of the circle.
+ double perimeter; //The length of the perimeter of the circle.
+ double error; //Represents the least square error of the fitted circle to the entire set of points.
+} BestCircle;
+
+typedef struct BestEllipse_struct {
+ PointFloat center; //The coordinate location of the center of the ellipse.
+ PointFloat majorAxisStart; //The coordinate location of the start of the major axis of the ellipse.
+ PointFloat majorAxisEnd; //The coordinate location of the end of the major axis of the ellipse.
+ PointFloat minorAxisStart; //The coordinate location of the start of the minor axis of the ellipse.
+ PointFloat minorAxisEnd; //The coordinate location of the end of the minor axis of the ellipse.
+ double area; //The area of the ellipse.
+ double perimeter; //The length of the perimeter of the ellipse.
+} BestEllipse;
+
+typedef struct BestLine_struct {
+ PointFloat start; //The coordinate location of the start of the line.
+ PointFloat end; //The coordinate location of the end of the line.
+ LineEquation equation; //Defines the three coefficients of the equation of the best fit line.
+ int valid; //This element is TRUE if the function achieved the minimum score within the number of allowed refinement iterations and FALSE if the function did not achieve the minimum score.
+ double error; //Represents the least square error of the fitted line to the entire set of points.
+ int* pointsUsed; //An array of the indexes for the points array indicating which points the function used to fit the line.
+ int numPointsUsed; //The number of points the function used to fit the line.
+} BestLine;
+
+typedef struct BrowserOptions_struct {
+ int width; //The width to make the browser.
+ int height; //The height to make the browser image.
+ int imagesPerLine; //The number of images to place on a single line.
+ RGBValue backgroundColor; //The background color of the browser.
+ int frameSize; //Specifies the number of pixels with which to border each thumbnail.
+ BrowserFrameStyle style; //The style for the frame around each thumbnail.
+ float ratio; //Specifies the width to height ratio of each thumbnail.
+ RGBValue focusColor; //The color to use to display focused cells.
+} BrowserOptions;
+
+typedef struct CoordinateSystem_struct {
+ PointFloat origin; //The origin of the coordinate system.
+ float angle; //The angle, in degrees, of the x-axis of the coordinate system relative to the image x-axis.
+ AxisOrientation axisOrientation; //The direction of the y-axis of the coordinate reference system.
+} CoordinateSystem;
+
+typedef struct CalibrationInfo_struct {
+ float* errorMap; //The error map for the calibration.
+ int mapColumns; //The number of columns in the error map.
+ int mapRows; //The number of rows in the error map.
+ ROI* userRoi; //Specifies the ROI the user provided when learning the calibration.
+ ROI* calibrationRoi; //Specifies the ROI that corresponds to the region of the image where the calibration information is accurate.
+ LearnCalibrationOptions options; //Specifies the calibration options the user provided when learning the calibration.
+ GridDescriptor grid; //Specifies the scaling constants for the image.
+ CoordinateSystem system; //Specifies the coordinate system for the real world coordinates.
+ RangeFloat range; //The range of the grayscale the function used to represent the circles in the grid image.
+ float quality; //The quality score of the learning process, which is a value between 0-1000.
+} CalibrationInfo;
+
+typedef struct CalibrationPoints_struct {
+ PointFloat* pixelCoordinates; //The array of pixel coordinates.
+ PointFloat* realWorldCoordinates; //The array of corresponding real-world coordinates.
+ int numCoordinates; //The number of coordinates in both of the arrays.
+} CalibrationPoints;
+
+typedef struct CaliperOptions_struct {
+ TwoEdgePolarityType polarity; //Specifies the edge polarity of the edge pairs.
+ float separation; //The distance between edge pairs.
+ float separationDeviation; //Sets the range around the separation value.
+} CaliperOptions;
+
+typedef struct CaliperReport_struct {
+ float edge1Contrast; //The contrast of the first edge.
+ PointFloat edge1Coord; //The coordinates of the first edge.
+ float edge2Contrast; //The contrast of the second edge.
+ PointFloat edge2Coord; //The coordinates of the second edge.
+ float separation; //The distance between the two edges.
+ float reserved; //This element is reserved.
+} CaliperReport;
+
+typedef struct DrawTextOptions_struct {
+ char fontName[32]; //The font name to use.
+ int fontSize; //The size of the font.
+ int bold; //Set this parameter to TRUE to bold text.
+ int italic; //Set this parameter to TRUE to italicize text.
+ int underline; //Set this parameter to TRUE to underline text.
+ int strikeout; //Set this parameter to TRUE to strikeout text.
+ TextAlignment textAlignment; //Sets the alignment of text.
+ FontColor fontColor; //Sets the font color.
+} DrawTextOptions;
+
+typedef struct CircleReport_struct {
+ Point center; //The coordinate point of the center of the circle.
+ int radius; //The radius of the circle, in pixels.
+ int area; //The area of the circle, in pixels.
+} CircleReport;
+
+typedef struct ClosedContour_struct {
+ Point* points; //The points that make up the closed contour.
+ int numPoints; //The number of points in the array.
+} ClosedContour;
+
+typedef struct ColorHistogramReport_struct {
+ HistogramReport plane1; //The histogram report of the first color plane.
+ HistogramReport plane2; //The histogram report of the second plane.
+ HistogramReport plane3; //The histogram report of the third plane.
+} ColorHistogramReport;
+
+typedef struct ColorInformation_struct {
+ int infoCount; //The size of the info array.
+ int saturation; //The saturation level the function uses to learn the color information.
+ double* info; //An array of color information that represents the color spectrum analysis of a region of an image in a compact form.
+} ColorInformation;
+
+typedef struct Complex_struct {
+ float r; //The real part of the value.
+ float i; //The imaginary part of the value.
+} Complex;
+
+typedef struct ConcentricRakeReport_struct {
+ ArcInfo* rakeArcs; //An array containing the location of each concentric arc line used for edge detection.
+ int numArcs; //The number of arc lines in the rakeArcs array.
+ PointFloat* firstEdges; //The coordinate location of all edges detected as first edges.
+ int numFirstEdges; //The number of points in the first edges array.
+ PointFloat* lastEdges; //The coordinate location of all edges detected as last edges.
+ int numLastEdges; //The number of points in the last edges array.
+ EdgeLocationReport* allEdges; //An array of reports describing the location of the edges located by each concentric rake arc line.
+ int* linesWithEdges; //An array of indices into the rakeArcs array indicating the concentric rake arc lines on which the function detected at least one edge.
+ int numLinesWithEdges; //The number of concentric rake arc lines along which the function detected edges.
+} ConcentricRakeReport;
+
+typedef struct ConstructROIOptions_struct {
+ int windowNumber; //The window number of the image window.
+ const char* windowTitle; //Specifies the message string that the function displays in the title bar of the window.
+ PaletteType type; //The palette type to use.
+ RGBValue* palette; //If type is IMAQ_PALETTE_USER, this array is the palette of colors to use with the window.
+ int numColors; //If type is IMAQ_PALETTE_USER, this element is the number of colors in the palette array.
+} ConstructROIOptions;
+
+typedef struct ContourInfo_struct {
+ ContourType type; //The contour type.
+ unsigned numPoints; //The number of points that make up the contour.
+ Point* points; //The points describing the contour.
+ RGBValue contourColor; //The contour color.
+} ContourInfo;
+
+typedef union ContourUnion_union {
+ Point* point; //Use this member when the contour is of type IMAQ_POINT.
+ Line* line; //Use this member when the contour is of type IMAQ_LINE.
+ Rect* rect; //Use this member when the contour is of type IMAQ_RECT.
+ Rect* ovalBoundingBox; //Use this member when the contour is of type IMAQ_OVAL.
+ ClosedContour* closedContour; //Use this member when the contour is of type IMAQ_CLOSED_CONTOUR.
+ OpenContour* openContour; //Use this member when the contour is of type IMAQ_OPEN_CONTOUR.
+ Annulus* annulus; //Use this member when the contour is of type IMAQ_ANNULUS.
+ RotatedRect* rotatedRect; //Use this member when the contour is of type IMAQ_ROTATED_RECT.
+} ContourUnion;
+
+typedef struct ContourInfo2_struct {
+ ContourType type; //The contour type.
+ RGBValue color; //The contour color.
+ ContourUnion structure; //The information necessary to describe the contour in coordinate space.
+} ContourInfo2;
+
+typedef struct ContourPoint_struct {
+ double x; //The x-coordinate value in the image.
+ double y; //The y-coordinate value in the image.
+ double curvature; //The change in slope at this edge point of the segment.
+ double xDisplacement; //The x displacement of the current edge pixel from a cubic spline fit of the current edge segment.
+ double yDisplacement; //The y displacement of the current edge pixel from a cubic spline fit of the current edge segment.
+} ContourPoint;
+
+typedef struct CoordinateTransform_struct {
+ Point initialOrigin; //The origin of the initial coordinate system.
+ float initialAngle; //The angle, in degrees, of the x-axis of the initial coordinate system relative to the image x-axis.
+ Point finalOrigin; //The origin of the final coordinate system.
+ float finalAngle; //The angle, in degrees, of the x-axis of the final coordinate system relative to the image x-axis.
+} CoordinateTransform;
+
+typedef struct CoordinateTransform2_struct {
+ CoordinateSystem referenceSystem; //Defines the coordinate system for input coordinates.
+ CoordinateSystem measurementSystem; //Defines the coordinate system in which the function should perform measurements.
+} CoordinateTransform2;
+
+typedef struct CannyOptions_struct {
+ float sigma; //The sigma of the Gaussian smoothing filter that the function applies to the image before edge detection.
+ float upperThreshold; //The upper fraction of pixel values in the image from which the function chooses a seed or starting point of an edge segment.
+ float lowerThreshold; //The function multiplies this value by upperThreshold to determine the lower threshold for all the pixels in an edge segment.
+ int windowSize; //The window size of the Gaussian filter that the function applies to the image.
+} CannyOptions;
+
+typedef struct Range_struct {
+ int minValue; //The minimum value of the range.
+ int maxValue; //The maximum value of the range.
+} Range;
+
+typedef struct UserPointSymbol_struct {
+ int cols; //Number of columns in the symbol.
+ int rows; //Number of rows in the symbol.
+ int* pixels; //The pixels of the symbol.
+} UserPointSymbol;
+
+typedef struct View3DOptions_struct {
+ int sizeReduction; //A divisor the function uses when determining the final height and width of the 3D image.
+ int maxHeight; //Defines the maximum height of a pixel from the image source drawn in 3D.
+ Direction3D direction; //Defines the 3D orientation.
+ float alpha; //Determines the angle between the horizontal and the baseline.
+ float beta; //Determines the angle between the horizontal and the second baseline.
+ int border; //Defines the border size.
+ int background; //Defines the background color.
+ Plane3D plane; //Indicates the view a function uses to show complex images.
+} View3DOptions;
+
+typedef struct MatchPatternOptions_struct {
+ MatchingMode mode; //Specifies the method to use when looking for the pattern in the image.
+ int minContrast; //Specifies the minimum contrast expected in the image.
+ int subpixelAccuracy; //Set this element to TRUE to return areas in the image that match the pattern area with subpixel accuracy.
+ RotationAngleRange* angleRanges; //An array of angle ranges, in degrees, where each range specifies how much you expect the pattern to be rotated in the image.
+ int numRanges; //Number of angle ranges in the angleRanges array.
+ int numMatchesRequested; //Number of valid matches expected.
+ int matchFactor; //Controls the number of potential matches that the function examines.
+ float minMatchScore; //The minimum score a match can have for the function to consider the match valid.
+} MatchPatternOptions;
+
+typedef struct TIFFFileOptions_struct {
+ int rowsPerStrip; //Indicates the number of rows that the function writes per strip.
+ PhotometricMode photoInterp; //Designates which photometric interpretation to use.
+ TIFFCompressionType compressionType; //Indicates the type of compression to use on the TIFF file.
+} TIFFFileOptions;
+
+typedef union Color_union {
+ RGBValue rgb; //The information needed to describe a color in the RGB (Red, Green, and Blue) color space.
+ HSLValue hsl; //The information needed to describe a color in the HSL (Hue, Saturation, and Luminance) color space.
+ HSVValue hsv; //The information needed to describe a color in the HSI (Hue, Saturation, and Value) color space.
+ HSIValue hsi; //The information needed to describe a color in the HSI (Hue, Saturation, and Intensity) color space.
+ int rawValue; //The integer value for the data in the color union.
+} Color;
+
+typedef union PixelValue_union {
+ float grayscale; //A grayscale pixel value.
+ RGBValue rgb; //A RGB pixel value.
+ HSLValue hsl; //A HSL pixel value.
+ Complex complex; //A complex pixel value.
+ RGBU64Value rgbu64; //An unsigned 64-bit RGB pixel value.
+} PixelValue;
+
+typedef struct OpenContour_struct {
+ Point* points; //The points that make up the open contour.
+ int numPoints; //The number of points in the array.
+} OpenContour;
+
+typedef struct OverlayTextOptions_struct {
+ const char* fontName; //The name of the font to use.
+ int fontSize; //The size of the font.
+ int bold; //Set this element to TRUE to bold the text.
+ int italic; //Set this element to TRUE to italicize the text.
+ int underline; //Set this element to TRUE to underline the text.
+ int strikeout; //Set this element to TRUE to strikeout the text.
+ TextAlignment horizontalTextAlignment; //Sets the alignment of the text.
+ VerticalTextAlignment verticalTextAlignment; //Sets the vertical alignment for the text.
+ RGBValue backgroundColor; //Sets the color for the text background pixels.
+ double angle; //The counterclockwise angle, in degrees, of the text relative to the x-axis.
+} OverlayTextOptions;
+
+typedef struct ParticleFilterCriteria_struct {
+ MeasurementValue parameter; //The morphological measurement that the function uses for filtering.
+ float lower; //The lower bound of the criteria range.
+ float upper; //The upper bound of the criteria range.
+ int exclude; //Set this element to TRUE to indicate that a match occurs when the value is outside the criteria range.
+} ParticleFilterCriteria;
+
+typedef struct ParticleReport_struct {
+ int area; //The number of pixels in the particle.
+ float calibratedArea; //The size of the particle, calibrated to the calibration information of the image.
+ float perimeter; //The length of the perimeter, calibrated to the calibration information of the image.
+ int numHoles; //The number of holes in the particle.
+ int areaOfHoles; //The total surface area, in pixels, of all the holes in a particle.
+ float perimeterOfHoles; //The length of the perimeter of all the holes in the particle calibrated to the calibration information of the image.
+ Rect boundingBox; //The smallest rectangle that encloses the particle.
+ float sigmaX; //The sum of the particle pixels on the x-axis.
+ float sigmaY; //The sum of the particle pixels on the y-axis.
+ float sigmaXX; //The sum of the particle pixels on the x-axis, squared.
+ float sigmaYY; //The sum of the particle pixels on the y-axis, squared.
+ float sigmaXY; //The sum of the particle pixels on the x-axis and y-axis.
+ int longestLength; //The length of the longest horizontal line segment.
+ Point longestPoint; //The location of the leftmost pixel of the longest segment in the particle.
+ int projectionX; //The length of the particle when projected onto the x-axis.
+ int projectionY; //The length of the particle when projected onto the y-axis.
+ int connect8; //This element is TRUE if the function used connectivity-8 to determine if particles are touching.
+} ParticleReport;
+
+typedef struct PatternMatch_struct {
+ PointFloat position; //The location of the center of the match.
+ float rotation; //The rotation of the match relative to the template image, in degrees.
+ float scale; //The size of the match relative to the size of the template image, expressed as a percentage.
+ float score; //The accuracy of the match.
+ PointFloat corner[4]; //An array of four points describing the rectangle surrounding the template image.
+} PatternMatch;
+
+typedef struct QuantifyData_struct {
+ float mean; //The mean value of the pixel values.
+ float stdDev; //The standard deviation of the pixel values.
+ float min; //The smallest pixel value.
+ float max; //The largest pixel value.
+ float calibratedArea; //The area, calibrated to the calibration information of the image.
+ int pixelArea; //The area, in number of pixels.
+ float relativeSize; //The proportion, expressed as a percentage, of the associated region relative to the whole image.
+} QuantifyData;
+
+typedef struct QuantifyReport_struct {
+ QuantifyData global; //Statistical data of the whole image.
+ QuantifyData* regions; //An array of QuantifyData structures containing statistical data of each region of the image.
+ int regionCount; //The number of regions.
+} QuantifyReport;
+
+typedef struct RakeOptions_struct {
+ int threshold; //Specifies the threshold value for the contrast of the edge.
+ int width; //The number of pixels that the function averages to find the contrast at either side of the edge.
+ int steepness; //The span, in pixels, of the slope of the edge projected along the path specified by the input points.
+ int subsamplingRatio; //Specifies the number of pixels that separate two consecutive search lines.
+ InterpolationMethod subpixelType; //The method for interpolating.
+ int subpixelDivisions; //The number of samples the function obtains from a pixel.
+} RakeOptions;
+
+typedef struct RakeReport_struct {
+ LineFloat* rakeLines; //The coordinate location of each of the rake lines used by the function.
+ int numRakeLines; //The number of lines in the rakeLines array.
+ PointFloat* firstEdges; //The coordinate location of all edges detected as first edges.
+ unsigned int numFirstEdges; //The number of points in the firstEdges array.
+ PointFloat* lastEdges; //The coordinate location of all edges detected as last edges.
+ unsigned int numLastEdges; //The number of points in the lastEdges array.
+ EdgeLocationReport* allEdges; //An array of reports describing the location of the edges located by each rake line.
+ int* linesWithEdges; //An array of indices into the rakeLines array indicating the rake lines on which the function detected at least one edge.
+ int numLinesWithEdges; //The number of rake lines along which the function detected edges.
+} RakeReport;
+
+typedef struct TransformReport_struct {
+ PointFloat* points; //An array of transformed coordinates.
+ int* validPoints; //An array of values that describe the validity of each of the coordinates according to the region of interest you calibrated using either imaqLearnCalibrationGrid() or imaqLearnCalibrationPoints().
+ int numPoints; //The length of both the points array and the validPoints array.
+} TransformReport;
+
+typedef struct ShapeReport_struct {
+ Rect coordinates; //The bounding rectangle of the object.
+ Point centroid; //The coordinate location of the centroid of the object.
+ int size; //The size, in pixels, of the object.
+ double score; //A value ranging between 1 and 1,000 that specifies how similar the object in the image is to the template.
+} ShapeReport;
+
+typedef struct MeterArc_struct {
+ PointFloat needleBase; //The coordinate location of the base of the meter needle.
+ PointFloat* arcCoordPoints; //An array of points describing the coordinate location of the meter arc.
+ int numOfArcCoordPoints; //The number of points in the arcCoordPoints array.
+ int needleColor; //This element is TRUE when the meter has a light-colored needle on a dark background.
+} MeterArc;
+
+typedef struct ThresholdData_struct {
+ float rangeMin; //The lower boundary of the range to keep.
+ float rangeMax; //The upper boundary of the range to keep.
+ float newValue; //If useNewValue is TRUE, newValue is the replacement value for pixels within the range.
+ int useNewValue; //If TRUE, the function sets pixel values within [rangeMin, rangeMax] to the value specified in newValue.
+} ThresholdData;
+
+typedef struct StructuringElement_struct {
+ int matrixCols; //Number of columns in the matrix.
+ int matrixRows; //Number of rows in the matrix.
+ int hexa; //Set this element to TRUE if you specify a hexagonal structuring element in kernel.
+ int* kernel; //The values of the structuring element.
+} StructuringElement;
+
+typedef struct SpokeReport_struct {
+ LineFloat* spokeLines; //The coordinate location of each of the spoke lines used by the function.
+ int numSpokeLines; //The number of lines in the spokeLines array.
+ PointFloat* firstEdges; //The coordinate location of all edges detected as first edges.
+ int numFirstEdges; //The number of points in the firstEdges array.
+ PointFloat* lastEdges; //The coordinate location of all edges detected as last edges.
+ int numLastEdges; //The number of points in the lastEdges array.
+ EdgeLocationReport* allEdges; //An array of reports describing the location of the edges located by each spoke line.
+ int* linesWithEdges; //An array of indices into the spokeLines array indicating the rake lines on which the function detected at least one edge.
+ int numLinesWithEdges; //The number of spoke lines along which the function detects edges.
+} SpokeReport;
+
+typedef struct SimpleEdgeOptions_struct {
+ LevelType type; //Determines how the function evaluates the threshold and hysteresis values.
+ int threshold; //The pixel value at which an edge occurs.
+ int hysteresis; //A value that helps determine edges in noisy images.
+ EdgeProcess process; //Determines which edges the function looks for.
+ int subpixel; //Set this element to TRUE to find edges with subpixel accuracy by interpolating between points to find the crossing of the given threshold.
+} SimpleEdgeOptions;
+
+typedef struct SelectParticleCriteria_struct {
+ MeasurementValue parameter; //The morphological measurement that the function uses for filtering.
+ float lower; //The lower boundary of the criteria range.
+ float upper; //The upper boundary of the criteria range.
+} SelectParticleCriteria;
+
+typedef struct SegmentInfo_struct {
+ int numberOfPoints; //The number of points in the segment.
+ int isOpen; //If TRUE, the contour is open.
+ double weight; //The significance of the edge in terms of the gray values that constitute the edge.
+ ContourPoint* points; //The points of the segment.
+} SegmentInfo;
+
+typedef struct RotationAngleRange_struct {
+ float lower; //The lowest amount of rotation, in degrees, a valid pattern can have.
+ float upper; //The highest amount of rotation, in degrees, a valid pattern can have.
+} RotationAngleRange;
+
+typedef struct RotatedRect_struct {
+ int top; //Location of the top edge of the rectangle before rotation.
+ int left; //Location of the left edge of the rectangle before rotation.
+ int height; //Height of the rectangle.
+ int width; //Width of the rectangle.
+ double angle; //The rotation, in degrees, of the rectangle.
+} RotatedRect;
+
+typedef struct ROIProfile_struct {
+ LineProfile report; //Quantifying information about the points along the edge of each contour in the ROI.
+ Point* pixels; //An array of the points along the edge of each contour in the ROI.
+} ROIProfile;
+
+typedef struct ToolWindowOptions_struct {
+ int showSelectionTool; //If TRUE, the selection tool becomes visible.
+ int showZoomTool; //If TRUE, the zoom tool becomes visible.
+ int showPointTool; //If TRUE, the point tool becomes visible.
+ int showLineTool; //If TRUE, the line tool becomes visible.
+ int showRectangleTool; //If TRUE, the rectangle tool becomes visible.
+ int showOvalTool; //If TRUE, the oval tool becomes visible.
+ int showPolygonTool; //If TRUE, the polygon tool becomes visible.
+ int showClosedFreehandTool; //If TRUE, the closed freehand tool becomes visible.
+ int showPolyLineTool; //If TRUE, the polyline tool becomes visible.
+ int showFreehandTool; //If TRUE, the freehand tool becomes visible.
+ int showAnnulusTool; //If TRUE, the annulus becomes visible.
+ int showRotatedRectangleTool; //If TRUE, the rotated rectangle tool becomes visible.
+ int showPanTool; //If TRUE, the pan tool becomes visible.
+ int showZoomOutTool; //If TRUE, the zoom out tool becomes visible.
+ int reserved2; //This element is reserved and should be set to FALSE.
+ int reserved3; //This element is reserved and should be set to FALSE.
+ int reserved4; //This element is reserved and should be set to FALSE.
+} ToolWindowOptions;
+
+typedef struct SpokeOptions_struct {
+ int threshold; //Specifies the threshold value for the contrast of the edge.
+ int width; //The number of pixels that the function averages to find the contrast at either side of the edge.
+ int steepness; //The span, in pixels, of the slope of the edge projected along the path specified by the input points.
+ double subsamplingRatio; //The angle, in degrees, between each radial search line in the spoke.
+ InterpolationMethod subpixelType; //The method for interpolating.
+ int subpixelDivisions; //The number of samples the function obtains from a pixel.
+} SpokeOptions;
+
+#if !defined __GNUC__ && !defined _M_X64
+#pragma pack(pop)
+#endif
+
+//============================================================================
+// Callback Function Type
+//============================================================================
+#ifndef __GNUC__
+typedef void (IMAQ_CALLBACK* EventCallback)(WindowEventType event, int windowNumber, Tool tool, Rect rect);
+#endif
+
+//============================================================================
+// Globals
+//============================================================================
+#ifndef __GNUC__
+#pragma const_seg("IMAQVisionColorConstants")
+#endif
+static const RGBValue IMAQ_RGB_TRANSPARENT = { 0, 0, 0, 1 };
+static const RGBValue IMAQ_RGB_RED = { 0, 0, 255, 0 };
+static const RGBValue IMAQ_RGB_BLUE = { 255, 0, 0, 0 };
+static const RGBValue IMAQ_RGB_GREEN = { 0, 255, 0, 0 };
+static const RGBValue IMAQ_RGB_YELLOW = { 0, 255, 255, 0 };
+static const RGBValue IMAQ_RGB_WHITE = { 255, 255, 255, 0 };
+static const RGBValue IMAQ_RGB_BLACK = { 0, 0, 0, 0 };
+#ifndef __GNUC__
+#pragma const_seg()
+#endif
+
+//============================================================================
+// Backwards Compatibility
+//============================================================================
+typedef ColorSensitivity ColorComplexity;
+#define IMAQ_COMPLEXITY_LOW IMAQ_SENSITIVITY_LOW
+#define IMAQ_COMPLEXITY_MED IMAQ_SENSITIVITY_MED
+#define IMAQ_COMPLEXITY_HIGH IMAQ_SENSITIVITY_HIGH
+#define ERR_INVALID_COLORCOMPLEXITY ERR_INVALID_COLORSENSITIVITY
+
+//============================================================================
+// Logical functions
+//============================================================================
+IMAQ_FUNC int IMAQ_STDCALL imaqAnd(Image* dest, const Image* sourceA, const Image* sourceB);
+IMAQ_FUNC int IMAQ_STDCALL imaqAndConstant(Image* dest, const Image* source, PixelValue value);
+IMAQ_FUNC int IMAQ_STDCALL imaqCompare(Image* dest, const Image* source, const Image* compareImage, ComparisonFunction compare);
+IMAQ_FUNC int IMAQ_STDCALL imaqCompareConstant(Image* dest, const Image* source, PixelValue value, ComparisonFunction compare);
+IMAQ_FUNC int IMAQ_STDCALL imaqLogicalDifference(Image* dest, const Image* sourceA, const Image* sourceB);
+IMAQ_FUNC int IMAQ_STDCALL imaqLogicalDifferenceConstant(Image* dest, const Image* source, PixelValue value);
+IMAQ_FUNC int IMAQ_STDCALL imaqNand(Image* dest, const Image* sourceA, const Image* sourceB);
+IMAQ_FUNC int IMAQ_STDCALL imaqNandConstant(Image* dest, const Image* source, PixelValue value);
+IMAQ_FUNC int IMAQ_STDCALL imaqNor(Image* dest, const Image* sourceA, const Image* sourceB);
+IMAQ_FUNC int IMAQ_STDCALL imaqNorConstant(Image* dest, const Image* source, PixelValue value);
+IMAQ_FUNC int IMAQ_STDCALL imaqOr(Image* dest, const Image* sourceA, const Image* sourceB);
+IMAQ_FUNC int IMAQ_STDCALL imaqOrConstant(Image* dest, const Image* source, PixelValue value);
+IMAQ_FUNC int IMAQ_STDCALL imaqXnor(Image* dest, const Image* sourceA, const Image* sourceB);
+IMAQ_FUNC int IMAQ_STDCALL imaqXnorConstant(Image* dest, const Image* source, PixelValue value);
+IMAQ_FUNC int IMAQ_STDCALL imaqXor(Image* dest, const Image* sourceA, const Image* sourceB);
+IMAQ_FUNC int IMAQ_STDCALL imaqXorConstant(Image* dest, const Image* source, PixelValue value);
+
+//============================================================================
+// Particle Analysis functions
+//============================================================================
+IMAQ_FUNC int IMAQ_STDCALL imaqCountParticles(Image* image, int connectivity8, int* numParticles);
+IMAQ_FUNC int IMAQ_STDCALL imaqMeasureParticle(Image* image, int particleNumber, int calibrated, MeasurementType measurement, double* value);
+IMAQ_FUNC MeasureParticlesReport* IMAQ_STDCALL imaqMeasureParticles(Image* image, MeasureParticlesCalibrationMode calibrationMode, const MeasurementType* measurements, size_t numMeasurements);
+IMAQ_FUNC int IMAQ_STDCALL imaqParticleFilter4(Image* dest, Image* source, const ParticleFilterCriteria2* criteria, int criteriaCount, const ParticleFilterOptions2* options, const ROI* roi, int* numParticles);
+
+//============================================================================
+// Morphology functions
+//============================================================================
+IMAQ_FUNC int IMAQ_STDCALL imaqConvexHull(Image* dest, Image* source, int connectivity8);
+IMAQ_FUNC int IMAQ_STDCALL imaqDanielssonDistance(Image* dest, Image* source);
+IMAQ_FUNC int IMAQ_STDCALL imaqFillHoles(Image* dest, const Image* source, int connectivity8);
+IMAQ_FUNC CircleReport* IMAQ_STDCALL imaqFindCircles(Image* dest, Image* source, float minRadius, float maxRadius, int* numCircles);
+IMAQ_FUNC int IMAQ_STDCALL imaqLabel2(Image* dest, Image* source, int connectivity8, int* particleCount);
+IMAQ_FUNC int IMAQ_STDCALL imaqMorphology(Image* dest, Image* source, MorphologyMethod method, const StructuringElement* structuringElement);
+IMAQ_FUNC int IMAQ_STDCALL imaqRejectBorder(Image* dest, Image* source, int connectivity8);
+IMAQ_FUNC int IMAQ_STDCALL imaqSegmentation(Image* dest, Image* source);
+IMAQ_FUNC int IMAQ_STDCALL imaqSeparation(Image* dest, Image* source, int erosions, const StructuringElement* structuringElement);
+IMAQ_FUNC int IMAQ_STDCALL imaqSimpleDistance(Image* dest, Image* source, const StructuringElement* structuringElement);
+IMAQ_FUNC int IMAQ_STDCALL imaqSizeFilter(Image* dest, Image* source, int connectivity8, int erosions, SizeType keepSize, const StructuringElement* structuringElement);
+IMAQ_FUNC int IMAQ_STDCALL imaqSkeleton(Image* dest, Image* source, SkeletonMethod method);
+
+
+//============================================================================
+// Acquisition functions
+//============================================================================
+IMAQ_FUNC Image* IMAQ_STDCALL imaqCopyFromRing(SESSION_ID sessionID, Image* image, int imageToCopy, int* imageNumber, Rect rect);
+IMAQ_FUNC Image* IMAQ_STDCALL imaqEasyAcquire(const char* interfaceName);
+IMAQ_FUNC Image* IMAQ_STDCALL imaqExtractFromRing(SESSION_ID sessionID, int imageToExtract, int* imageNumber);
+IMAQ_FUNC Image* IMAQ_STDCALL imaqGrab(SESSION_ID sessionID, Image* image, int immediate);
+IMAQ_FUNC int IMAQ_STDCALL imaqReleaseImage(SESSION_ID sessionID);
+IMAQ_FUNC int IMAQ_STDCALL imaqSetupGrab(SESSION_ID sessionID, Rect rect);
+IMAQ_FUNC int IMAQ_STDCALL imaqSetupRing(SESSION_ID sessionID, Image** images, int numImages, int skipCount, Rect rect);
+IMAQ_FUNC int IMAQ_STDCALL imaqSetupSequence(SESSION_ID sessionID, Image** images, int numImages, int skipCount, Rect rect);
+IMAQ_FUNC Image* IMAQ_STDCALL imaqSnap(SESSION_ID sessionID, Image* image, Rect rect);
+IMAQ_FUNC int IMAQ_STDCALL imaqStartAcquisition(SESSION_ID sessionID);
+IMAQ_FUNC int IMAQ_STDCALL imaqStopAcquisition(SESSION_ID sessionID);
+
+//============================================================================
+// Arithmetic functions
+//============================================================================
+IMAQ_FUNC int IMAQ_STDCALL imaqAbsoluteDifference(Image* dest, const Image* sourceA, const Image* sourceB);
+IMAQ_FUNC int IMAQ_STDCALL imaqAbsoluteDifferenceConstant(Image* dest, const Image* source, PixelValue value);
+IMAQ_FUNC int IMAQ_STDCALL imaqAdd(Image* dest, const Image* sourceA, const Image* sourceB);
+IMAQ_FUNC int IMAQ_STDCALL imaqAddConstant(Image* dest, const Image* source, PixelValue value);
+IMAQ_FUNC int IMAQ_STDCALL imaqAverage(Image* dest, const Image* sourceA, const Image* sourceB);
+IMAQ_FUNC int IMAQ_STDCALL imaqAverageConstant(Image* dest, const Image* source, PixelValue value);
+IMAQ_FUNC int IMAQ_STDCALL imaqDivide2(Image* dest, const Image* sourceA, const Image* sourceB, RoundingMode roundingMode);
+IMAQ_FUNC int IMAQ_STDCALL imaqDivideConstant2(Image* dest, const Image* source, PixelValue value, RoundingMode roundingMode);
+IMAQ_FUNC int IMAQ_STDCALL imaqMax(Image* dest, const Image* sourceA, const Image* sourceB);
+IMAQ_FUNC int IMAQ_STDCALL imaqMaxConstant(Image* dest, const Image* source, PixelValue value);
+IMAQ_FUNC int IMAQ_STDCALL imaqMin(Image* dest, const Image* sourceA, const Image* sourceB);
+IMAQ_FUNC int IMAQ_STDCALL imaqMinConstant(Image* dest, const Image* source, PixelValue value);
+IMAQ_FUNC int IMAQ_STDCALL imaqModulo(Image* dest, const Image* sourceA, const Image* sourceB);
+IMAQ_FUNC int IMAQ_STDCALL imaqModuloConstant(Image* dest, const Image* source, PixelValue value);
+IMAQ_FUNC int IMAQ_STDCALL imaqMulDiv(Image* dest, const Image* sourceA, const Image* sourceB, float value);
+IMAQ_FUNC int IMAQ_STDCALL imaqMultiply(Image* dest, const Image* sourceA, const Image* sourceB);
+IMAQ_FUNC int IMAQ_STDCALL imaqMultiplyConstant(Image* dest, const Image* source, PixelValue value);
+IMAQ_FUNC int IMAQ_STDCALL imaqSubtract(Image* dest, const Image* sourceA, const Image* sourceB);
+IMAQ_FUNC int IMAQ_STDCALL imaqSubtractConstant(Image* dest, const Image* source, PixelValue value);
+
+//============================================================================
+// Caliper functions
+//============================================================================
+IMAQ_FUNC CaliperReport* IMAQ_STDCALL imaqCaliperTool(const Image* image, const Point* points, int numPoints, const EdgeOptions* edgeOptions, const CaliperOptions* caliperOptions, int* numEdgePairs);
+IMAQ_FUNC ConcentricRakeReport2* IMAQ_STDCALL imaqConcentricRake2(Image* image, ROI* roi, ConcentricRakeDirection direction, EdgeProcess process, int stepSize, EdgeOptions2* edgeOptions);
+IMAQ_FUNC ExtremeReport* IMAQ_STDCALL imaqDetectExtremes(const double* pixels, int numPixels, DetectionMode mode, const DetectExtremesOptions* options, int* numExtremes);
+IMAQ_FUNC int IMAQ_STDCALL imaqDetectRotation(const Image* referenceImage, const Image* testImage, PointFloat referenceCenter, PointFloat testCenter, int radius, float precision, double* angle);
+IMAQ_FUNC EdgeReport2* IMAQ_STDCALL imaqEdgeTool4(Image* image, ROI* roi, EdgeProcess processType, EdgeOptions2* edgeOptions, const unsigned int reverseDirection);
+IMAQ_FUNC FindEdgeReport* IMAQ_STDCALL imaqFindEdge2(Image* image, const ROI* roi, const CoordinateSystem* baseSystem, const CoordinateSystem* newSystem, const FindEdgeOptions2* findEdgeOptions, const StraightEdgeOptions* straightEdgeOptions);
+IMAQ_FUNC int IMAQ_STDCALL imaqFindTransformRect2(Image* image, const ROI* roi, FindTransformMode mode, CoordinateSystem* baseSystem, CoordinateSystem* newSystem, const FindTransformRectOptions2* findTransformOptions, const StraightEdgeOptions* straightEdgeOptions, AxisReport* axisReport);
+IMAQ_FUNC int IMAQ_STDCALL imaqFindTransformRects2(Image* image, const ROI* primaryROI, const ROI* secondaryROI, FindTransformMode mode, CoordinateSystem* baseSystem, CoordinateSystem* newSystem, const FindTransformRectsOptions2* findTransformOptions, const StraightEdgeOptions* primaryStraightEdgeOptions, const StraightEdgeOptions* secondaryStraightEdgeOptions, AxisReport* axisReport);
+IMAQ_FUNC int IMAQ_STDCALL imaqLineGaugeTool2(const Image* image, Point start, Point end, LineGaugeMethod method, const EdgeOptions* edgeOptions, const CoordinateTransform2* transform, float* distance);
+IMAQ_FUNC RakeReport2* IMAQ_STDCALL imaqRake2(Image* image, ROI* roi, RakeDirection direction, EdgeProcess process, int stepSize, EdgeOptions2* edgeOptions);
+IMAQ_FUNC PointFloat* IMAQ_STDCALL imaqSimpleEdge(const Image* image, const Point* points, int numPoints, const SimpleEdgeOptions* options, int* numEdges);
+IMAQ_FUNC SpokeReport2* IMAQ_STDCALL imaqSpoke2(Image* image, ROI* roi, SpokeDirection direction, EdgeProcess process, int stepSize, EdgeOptions2* edgeOptions);
+IMAQ_FUNC StraightEdgeReport2* IMAQ_STDCALL imaqStraightEdge(const Image* image, const ROI* roi, SearchDirection searchDirection, const EdgeOptions2* edgeOptions, const StraightEdgeOptions* straightEdgeOptions);
+IMAQ_FUNC StraightEdgeReport2* IMAQ_STDCALL imaqStraightEdge2(const Image* image, const ROI* roi, SearchDirection searchDirection, const EdgeOptions2* edgeOptions, const StraightEdgeOptions* straightEdgeOptions, unsigned int optimizedMode);
+
+//============================================================================
+// Spatial Filters functions
+//============================================================================
+IMAQ_FUNC int IMAQ_STDCALL imaqCannyEdgeFilter(Image* dest, const Image* source, const CannyOptions* options);
+IMAQ_FUNC int IMAQ_STDCALL imaqConvolve2(Image* dest, Image* source, float* kernel, int matrixRows, int matrixCols, float normalize, Image* mask, RoundingMode roundingMode);
+IMAQ_FUNC int IMAQ_STDCALL imaqCorrelate(Image* dest, Image* source, const Image* templateImage, Rect rect);
+IMAQ_FUNC int IMAQ_STDCALL imaqEdgeFilter(Image* dest, Image* source, OutlineMethod method, const Image* mask);
+IMAQ_FUNC int IMAQ_STDCALL imaqLowPass(Image* dest, Image* source, int width, int height, float tolerance, const Image* mask);
+IMAQ_FUNC int IMAQ_STDCALL imaqMedianFilter(Image* dest, Image* source, int width, int height, const Image* mask);
+IMAQ_FUNC int IMAQ_STDCALL imaqNthOrderFilter(Image* dest, Image* source, int width, int height, int n, const Image* mask);
+
+//============================================================================
+// Drawing functions
+//============================================================================
+IMAQ_FUNC int IMAQ_STDCALL imaqDrawLineOnImage(Image* dest, const Image* source, DrawMode mode, Point start, Point end, float newPixelValue);
+IMAQ_FUNC int IMAQ_STDCALL imaqDrawShapeOnImage(Image* dest, const Image* source, Rect rect, DrawMode mode, ShapeMode shape, float newPixelValue);
+IMAQ_FUNC int IMAQ_STDCALL imaqDrawTextOnImage(Image* dest, const Image* source, Point coord, const char* text, const DrawTextOptions* options, int* fontNameUsed);
+
+//============================================================================
+// Interlacing functions
+//============================================================================
+IMAQ_FUNC int IMAQ_STDCALL imaqInterlaceCombine(Image* frame, const Image* odd, const Image* even);
+IMAQ_FUNC int IMAQ_STDCALL imaqInterlaceSeparate(const Image* frame, Image* odd, Image* even);
+
+//============================================================================
+// Image Information functions
+//============================================================================
+IMAQ_FUNC char** IMAQ_STDCALL imaqEnumerateCustomKeys(const Image* image, unsigned int* size);
+IMAQ_FUNC int IMAQ_STDCALL imaqGetBitDepth(const Image* image, unsigned int* bitDepth);
+IMAQ_FUNC int IMAQ_STDCALL imaqGetBytesPerPixel(const Image* image, int* byteCount);
+IMAQ_FUNC int IMAQ_STDCALL imaqGetImageInfo(const Image* image, ImageInfo* info);
+IMAQ_FUNC int IMAQ_STDCALL imaqGetImageSize(const Image* image, int* width, int* height);
+IMAQ_FUNC int IMAQ_STDCALL imaqGetImageType(const Image* image, ImageType* type);
+IMAQ_FUNC int IMAQ_STDCALL imaqGetMaskOffset(const Image* image, Point* offset);
+IMAQ_FUNC void* IMAQ_STDCALL imaqGetPixelAddress(const Image* image, Point pixel);
+IMAQ_FUNC int IMAQ_STDCALL imaqGetVisionInfoTypes(const Image* image, unsigned int* present);
+IMAQ_FUNC int IMAQ_STDCALL imaqIsImageEmpty(const Image* image, int* empty);
+IMAQ_FUNC void* IMAQ_STDCALL imaqReadCustomData(const Image* image, const char* key, unsigned int* size);
+IMAQ_FUNC int IMAQ_STDCALL imaqRemoveCustomData(Image* image, const char* key);
+IMAQ_FUNC int IMAQ_STDCALL imaqRemoveVisionInfo2(const Image* image, unsigned int info);
+IMAQ_FUNC int IMAQ_STDCALL imaqSetBitDepth(Image* image, unsigned int bitDepth);
+IMAQ_FUNC int IMAQ_STDCALL imaqSetImageSize(Image* image, int width, int height);
+IMAQ_FUNC int IMAQ_STDCALL imaqSetMaskOffset(Image* image, Point offset);
+IMAQ_FUNC int IMAQ_STDCALL imaqWriteCustomData(Image* image, const char* key, const void* data, unsigned int size);
+
+//============================================================================
+// Display functions
+//============================================================================
+IMAQ_FUNC int IMAQ_STDCALL imaqAreToolsContextSensitive(int* sensitive);
+IMAQ_FUNC int IMAQ_STDCALL imaqCloseWindow(int windowNumber);
+IMAQ_FUNC int IMAQ_STDCALL imaqDisplayImage(const Image* image, int windowNumber, int resize);
+IMAQ_FUNC int IMAQ_STDCALL imaqGetLastKey(char* keyPressed, int* windowNumber, int* modifiers);
+IMAQ_FUNC void* IMAQ_STDCALL imaqGetSystemWindowHandle(int windowNumber);
+IMAQ_FUNC int IMAQ_STDCALL imaqGetWindowCenterPos(int windowNumber, Point* centerPosition);
+IMAQ_FUNC int IMAQ_STDCALL imaqSetToolContextSensitivity(int sensitive);
+IMAQ_FUNC int IMAQ_STDCALL imaqShowWindow(int windowNumber, int visible);
+
+//============================================================================
+// Image Manipulation functions
+//============================================================================
+IMAQ_FUNC int IMAQ_STDCALL imaqCast(Image* dest, const Image* source, ImageType type, const float* lookup, int shift);
+IMAQ_FUNC int IMAQ_STDCALL imaqCopyRect(Image* dest, const Image* source, Rect rect, Point destLoc);
+IMAQ_FUNC int IMAQ_STDCALL imaqDuplicate(Image* dest, const Image* source);
+IMAQ_FUNC void* IMAQ_STDCALL imaqFlatten(const Image* image, FlattenType type, CompressionType compression, int quality, unsigned int* size);
+IMAQ_FUNC int IMAQ_STDCALL imaqFlip(Image* dest, const Image* source, FlipAxis axis);
+IMAQ_FUNC int IMAQ_STDCALL imaqMask(Image* dest, const Image* source, const Image* mask);
+IMAQ_FUNC int IMAQ_STDCALL imaqResample(Image* dest, const Image* source, int newWidth, int newHeight, InterpolationMethod method, Rect rect);
+IMAQ_FUNC int IMAQ_STDCALL imaqRotate2(Image* dest, const Image* source, float angle, PixelValue fill, InterpolationMethod method, int maintainSize);
+IMAQ_FUNC int IMAQ_STDCALL imaqScale(Image* dest, const Image* source, int xScale, int yScale, ScalingMode scaleMode, Rect rect);
+IMAQ_FUNC int IMAQ_STDCALL imaqShift(Image* dest, const Image* source, int shiftX, int shiftY, PixelValue fill);
+IMAQ_FUNC int IMAQ_STDCALL imaqTranspose(Image* dest, const Image* source);
+IMAQ_FUNC int IMAQ_STDCALL imaqUnflatten(Image* image, const void* data, unsigned int size);
+IMAQ_FUNC int IMAQ_STDCALL imaqUnwrapImage(Image* dest, const Image* source, Annulus annulus, RectOrientation orientation, InterpolationMethod method);
+IMAQ_FUNC int IMAQ_STDCALL imaqView3D(Image* dest, Image* source, const View3DOptions* options);
+
+//============================================================================
+// File I/O functions
+//============================================================================
+IMAQ_FUNC int IMAQ_STDCALL imaqCloseAVI(AVISession session);
+IMAQ_FUNC AVISession IMAQ_STDCALL imaqCreateAVI(const char* fileName, const char* compressionFilter, int quality, unsigned int framesPerSecond, unsigned int maxDataSize);
+IMAQ_FUNC int IMAQ_STDCALL imaqGetAVIInfo(AVISession session, AVIInfo* info);
+IMAQ_FUNC int IMAQ_STDCALL imaqGetFileInfo(const char* fileName, CalibrationUnit* calibrationUnit, float* calibrationX, float* calibrationY, int* width, int* height, ImageType* imageType);
+IMAQ_FUNC FilterName* IMAQ_STDCALL imaqGetFilterNames(int* numFilters);
+IMAQ_FUNC char** IMAQ_STDCALL imaqLoadImagePopup(const char* defaultDirectory, const char* defaultFileSpec, const char* fileTypeList, const char* title, int allowMultiplePaths, ButtonLabel buttonLabel, int restrictDirectory, int restrictExtension, int allowCancel, int allowMakeDirectory, int* cancelled, int* numPaths);
+IMAQ_FUNC AVISession IMAQ_STDCALL imaqOpenAVI(const char* fileName);
+IMAQ_FUNC int IMAQ_STDCALL imaqReadAVIFrame(Image* image, AVISession session, unsigned int frameNum, void* data, unsigned int* dataSize);
+IMAQ_FUNC int IMAQ_STDCALL imaqReadFile(Image* image, const char* fileName, RGBValue* colorTable, int* numColors);
+IMAQ_FUNC int IMAQ_STDCALL imaqReadVisionFile(Image* image, const char* fileName, RGBValue* colorTable, int* numColors);
+IMAQ_FUNC int IMAQ_STDCALL imaqWriteAVIFrame(Image* image, AVISession session, const void* data, unsigned int dataLength);
+IMAQ_FUNC int IMAQ_STDCALL imaqWriteBMPFile(const Image* image, const char* fileName, int compress, const RGBValue* colorTable);
+IMAQ_FUNC int IMAQ_STDCALL imaqWriteFile(const Image* image, const char* fileName, const RGBValue* colorTable);
+IMAQ_FUNC int IMAQ_STDCALL imaqWriteJPEGFile(const Image* image, const char* fileName, unsigned int quality, void* colorTable);
+IMAQ_FUNC int IMAQ_STDCALL imaqWriteJPEG2000File(const Image* image, const char* fileName, int lossless, float compressionRatio, const JPEG2000FileAdvancedOptions* advancedOptions, const RGBValue* colorTable);
+IMAQ_FUNC int IMAQ_STDCALL imaqWritePNGFile2(const Image* image, const char* fileName, unsigned int compressionSpeed, const RGBValue* colorTable, int useBitDepth);
+IMAQ_FUNC int IMAQ_STDCALL imaqWriteTIFFFile(const Image* image, const char* fileName, const TIFFFileOptions* options, const RGBValue* colorTable);
+IMAQ_FUNC int IMAQ_STDCALL imaqWriteVisionFile(const Image* image, const char* fileName, const RGBValue* colorTable);
+
+
+//============================================================================
+// Analytic Geometry functions
+//============================================================================
+IMAQ_FUNC int IMAQ_STDCALL imaqBuildCoordinateSystem(const Point* points, ReferenceMode mode, AxisOrientation orientation, CoordinateSystem* system);
+IMAQ_FUNC BestCircle2* IMAQ_STDCALL imaqFitCircle2(const PointFloat* points, int numPoints, const FitCircleOptions* options);
+IMAQ_FUNC BestEllipse2* IMAQ_STDCALL imaqFitEllipse2(const PointFloat* points, int numPoints, const FitEllipseOptions* options);
+IMAQ_FUNC BestLine* IMAQ_STDCALL imaqFitLine(const PointFloat* points, int numPoints, const FitLineOptions* options);
+IMAQ_FUNC int IMAQ_STDCALL imaqGetAngle(PointFloat start1, PointFloat end1, PointFloat start2, PointFloat end2, float* angle);
+IMAQ_FUNC int IMAQ_STDCALL imaqGetBisectingLine(PointFloat start1, PointFloat end1, PointFloat start2, PointFloat end2, PointFloat* bisectStart, PointFloat* bisectEnd);
+IMAQ_FUNC int IMAQ_STDCALL imaqGetDistance(PointFloat point1, PointFloat point2, float* distance);
+IMAQ_FUNC int IMAQ_STDCALL imaqGetIntersection(PointFloat start1, PointFloat end1, PointFloat start2, PointFloat end2, PointFloat* intersection);
+IMAQ_FUNC int IMAQ_STDCALL imaqGetMidLine(PointFloat refLineStart, PointFloat refLineEnd, PointFloat point, PointFloat* midLineStart, PointFloat* midLineEnd);
+IMAQ_FUNC int IMAQ_STDCALL imaqGetPerpendicularLine(PointFloat refLineStart, PointFloat refLineEnd, PointFloat point, PointFloat* perpLineStart, PointFloat* perpLineEnd, double* distance);
+IMAQ_FUNC SegmentInfo* IMAQ_STDCALL imaqGetPointsOnContour(const Image* image, int* numSegments);
+IMAQ_FUNC Point* IMAQ_STDCALL imaqGetPointsOnLine(Point start, Point end, int* numPoints);
+IMAQ_FUNC int IMAQ_STDCALL imaqGetPolygonArea(const PointFloat* points, int numPoints, float* area);
+IMAQ_FUNC float* IMAQ_STDCALL imaqInterpolatePoints(const Image* image, const Point* points, int numPoints, InterpolationMethod method, int subpixel, int* interpCount);
+
+//============================================================================
+// Clipboard functions
+//============================================================================
+IMAQ_FUNC int IMAQ_STDCALL imaqClipboardToImage(Image* dest, RGBValue* palette);
+IMAQ_FUNC int IMAQ_STDCALL imaqImageToClipboard(const Image* image, const RGBValue* palette);
+
+//============================================================================
+// Border functions
+//============================================================================
+IMAQ_FUNC int IMAQ_STDCALL imaqFillBorder(Image* image, BorderMethod method);
+IMAQ_FUNC int IMAQ_STDCALL imaqGetBorderSize(const Image* image, int* borderSize);
+IMAQ_FUNC int IMAQ_STDCALL imaqSetBorderSize(Image* image, int size);
+
+//============================================================================
+// Image Management functions
+//============================================================================
+IMAQ_FUNC int IMAQ_STDCALL imaqArrayToImage(Image* image, const void* array, int numCols, int numRows);
+IMAQ_FUNC Image* IMAQ_STDCALL imaqCreateImage(ImageType type, int borderSize);
+IMAQ_FUNC void* IMAQ_STDCALL imaqImageToArray(const Image* image, Rect rect, int* columns, int* rows);
+
+//============================================================================
+// Color Processing functions
+//============================================================================
+IMAQ_FUNC Color2 IMAQ_STDCALL imaqChangeColorSpace2(const Color2* sourceColor, ColorMode sourceSpace, ColorMode destSpace, double offset, const CIEXYZValue* whiteReference);
+IMAQ_FUNC int IMAQ_STDCALL imaqColorBCGTransform(Image* dest, const Image* source, const BCGOptions* redOptions, const BCGOptions* greenOptions, const BCGOptions* blueOptions, const Image* mask);
+IMAQ_FUNC int IMAQ_STDCALL imaqColorEqualize(Image* dest, const Image* source, int colorEqualization);
+IMAQ_FUNC ColorHistogramReport* IMAQ_STDCALL imaqColorHistogram2(Image* image, int numClasses, ColorMode mode, const CIEXYZValue* whiteReference, Image* mask);
+IMAQ_FUNC int IMAQ_STDCALL imaqColorLookup(Image* dest, const Image* source, ColorMode mode, const Image* mask, const short* plane1, const short* plane2, const short* plane3);
+IMAQ_FUNC int IMAQ_STDCALL imaqColorThreshold(Image* dest, const Image* source, int replaceValue, ColorMode mode, const Range* plane1Range, const Range* plane2Range, const Range* plane3Range);
+IMAQ_FUNC SupervisedColorSegmentationReport* IMAQ_STDCALL imaqSupervisedColorSegmentation(ClassifierSession* session, Image* labelImage, const Image* srcImage, const ROI* roi, const ROILabel* labelIn, unsigned int numLabelIn, int maxDistance, int minIdentificationScore, const ColorSegmenationOptions* segmentOptions);
+IMAQ_FUNC int IMAQ_STDCALL imaqGetColorSegmentationMaxDistance(ClassifierSession* session, const ColorSegmenationOptions* segmentOptions, SegmentationDistanceLevel distLevel, int* maxDistance);
+
+//============================================================================
+// Transform functions
+//============================================================================
+IMAQ_FUNC int IMAQ_STDCALL imaqBCGTransform(Image* dest, const Image* source, const BCGOptions* options, const Image* mask);
+IMAQ_FUNC int IMAQ_STDCALL imaqEqualize(Image* dest, const Image* source, float min, float max, const Image* mask);
+IMAQ_FUNC int IMAQ_STDCALL imaqInverse(Image* dest, const Image* source, const Image* mask);
+IMAQ_FUNC int IMAQ_STDCALL imaqMathTransform(Image* dest, const Image* source, MathTransformMethod method, float rangeMin, float rangeMax, float power, const Image* mask);
+IMAQ_FUNC int IMAQ_STDCALL imaqWatershedTransform(Image* dest, const Image* source, int connectivity8, int* zoneCount);
+IMAQ_FUNC int IMAQ_STDCALL imaqLookup2(Image* dest, const Image* source, const int* table, const Image* mask);
+
+
+//============================================================================
+// Window Management functions
+//============================================================================
+IMAQ_FUNC int IMAQ_STDCALL imaqAreScrollbarsVisible(int windowNumber, int* visible);
+IMAQ_FUNC int IMAQ_STDCALL imaqBringWindowToTop(int windowNumber);
+IMAQ_FUNC int IMAQ_STDCALL imaqGetMousePos(Point* position, int* windowNumber);
+IMAQ_FUNC int IMAQ_STDCALL imaqGetWindowBackground(int windowNumber, WindowBackgroundFillStyle* fillStyle, WindowBackgroundHatchStyle* hatchStyle, RGBValue* fillColor, RGBValue* backgroundColor);
+IMAQ_FUNC int IMAQ_STDCALL imaqGetWindowDisplayMapping(int windowNum, DisplayMapping* mapping);
+IMAQ_FUNC int IMAQ_STDCALL imaqGetWindowGrid(int windowNumber, int* xResolution, int* yResolution);
+IMAQ_FUNC int IMAQ_STDCALL imaqGetWindowHandle(int* handle);
+IMAQ_FUNC int IMAQ_STDCALL imaqGetWindowPos(int windowNumber, Point* position);
+IMAQ_FUNC int IMAQ_STDCALL imaqGetWindowSize(int windowNumber, int* width, int* height);
+IMAQ_FUNC char* IMAQ_STDCALL imaqGetWindowTitle(int windowNumber);
+IMAQ_FUNC int IMAQ_STDCALL imaqGetWindowZoom2(int windowNumber, float* xZoom, float* yZoom);
+IMAQ_FUNC int IMAQ_STDCALL imaqIsWindowNonTearing(int windowNumber, int* nonTearing);
+IMAQ_FUNC int IMAQ_STDCALL imaqIsWindowVisible(int windowNumber, int* visible);
+IMAQ_FUNC int IMAQ_STDCALL imaqMoveWindow(int windowNumber, Point position);
+IMAQ_FUNC int IMAQ_STDCALL imaqSetupWindow(int windowNumber, int configuration);
+IMAQ_FUNC int IMAQ_STDCALL imaqSetWindowBackground(int windowNumber, WindowBackgroundFillStyle fillStyle, WindowBackgroundHatchStyle hatchStyle, const RGBValue* fillColor, const RGBValue* backgroundColor);
+IMAQ_FUNC int IMAQ_STDCALL imaqSetWindowDisplayMapping(int windowNumber, const DisplayMapping* mapping);
+IMAQ_FUNC int IMAQ_STDCALL imaqSetWindowGrid(int windowNumber, int xResolution, int yResolution);
+IMAQ_FUNC int IMAQ_STDCALL imaqSetWindowMaxContourCount(int windowNumber, unsigned int maxContourCount);
+IMAQ_FUNC int IMAQ_STDCALL imaqSetWindowNonTearing(int windowNumber, int nonTearing);
+IMAQ_FUNC int IMAQ_STDCALL imaqSetWindowPalette(int windowNumber, PaletteType type, const RGBValue* palette, int numColors);
+IMAQ_FUNC int IMAQ_STDCALL imaqSetWindowSize(int windowNumber, int width, int height);
+IMAQ_FUNC int IMAQ_STDCALL imaqSetWindowThreadPolicy(WindowThreadPolicy policy);
+IMAQ_FUNC int IMAQ_STDCALL imaqSetWindowTitle(int windowNumber, const char* title);
+IMAQ_FUNC int IMAQ_STDCALL imaqSetWindowZoomToFit(int windowNumber, int zoomToFit);
+IMAQ_FUNC int IMAQ_STDCALL imaqShowScrollbars(int windowNumber, int visible);
+IMAQ_FUNC int IMAQ_STDCALL imaqZoomWindow2(int windowNumber, float xZoom, float yZoom, Point center);
+
+//============================================================================
+// Utilities functions
+//============================================================================
+IMAQ_FUNC const float* IMAQ_STDCALL imaqGetKernel(KernelFamily family, int size, int number);
+IMAQ_FUNC Annulus IMAQ_STDCALL imaqMakeAnnulus(Point center, int innerRadius, int outerRadius, double startAngle, double endAngle);
+IMAQ_FUNC Point IMAQ_STDCALL imaqMakePoint(int xCoordinate, int yCoordinate);
+IMAQ_FUNC PointFloat IMAQ_STDCALL imaqMakePointFloat(float xCoordinate, float yCoordinate);
+IMAQ_FUNC Rect IMAQ_STDCALL imaqMakeRect(int top, int left, int height, int width);
+IMAQ_FUNC Rect IMAQ_STDCALL imaqMakeRectFromRotatedRect(RotatedRect rotatedRect);
+IMAQ_FUNC RotatedRect IMAQ_STDCALL imaqMakeRotatedRect(int top, int left, int height, int width, double angle);
+IMAQ_FUNC RotatedRect IMAQ_STDCALL imaqMakeRotatedRectFromRect(Rect rect);
+IMAQ_FUNC int IMAQ_STDCALL imaqMulticoreOptions(MulticoreOperation operation, unsigned int* customNumCores);
+
+//============================================================================
+// Tool Window functions
+//============================================================================
+IMAQ_FUNC int IMAQ_STDCALL imaqCloseToolWindow(void);
+IMAQ_FUNC int IMAQ_STDCALL imaqGetCurrentTool(Tool* currentTool);
+IMAQ_FUNC int IMAQ_STDCALL imaqGetLastEvent(WindowEventType* type, int* windowNumber, Tool* tool, Rect* rect);
+IMAQ_FUNC void* IMAQ_STDCALL imaqGetToolWindowHandle(void);
+IMAQ_FUNC int IMAQ_STDCALL imaqGetToolWindowPos(Point* position);
+IMAQ_FUNC int IMAQ_STDCALL imaqIsToolWindowVisible(int* visible);
+IMAQ_FUNC int IMAQ_STDCALL imaqMoveToolWindow(Point position);
+IMAQ_FUNC int IMAQ_STDCALL imaqSetCurrentTool(Tool currentTool);
+#ifndef __GNUC__
+IMAQ_FUNC int IMAQ_STDCALL imaqSetEventCallback(EventCallback callback, int synchronous);
+#endif
+IMAQ_FUNC int IMAQ_STDCALL imaqSetToolColor(const RGBValue* color);
+IMAQ_FUNC int IMAQ_STDCALL imaqSetupToolWindow(int showCoordinates, int maxIconsPerLine, const ToolWindowOptions* options);
+IMAQ_FUNC int IMAQ_STDCALL imaqShowToolWindow(int visible);
+
+//============================================================================
+// Meter functions
+//============================================================================
+IMAQ_FUNC MeterArc* IMAQ_STDCALL imaqGetMeterArc(int lightNeedle, MeterArcMode mode, const ROI* roi, PointFloat base, PointFloat start, PointFloat end);
+IMAQ_FUNC int IMAQ_STDCALL imaqReadMeter(const Image* image, const MeterArc* arcInfo, double* percentage, PointFloat* endOfNeedle);
+
+//============================================================================
+// Calibration functions
+//============================================================================
+IMAQ_FUNC int IMAQ_STDCALL imaqCopyCalibrationInfo2(Image* dest, Image* source, Point offset);
+IMAQ_FUNC int IMAQ_STDCALL imaqCorrectCalibratedImage(Image* dest, const Image* source, PixelValue fill, InterpolationMethod method, const ROI* roi);
+IMAQ_FUNC CalibrationInfo* IMAQ_STDCALL imaqGetCalibrationInfo2(const Image* image);
+IMAQ_FUNC CalibrationInfo* IMAQ_STDCALL imaqGetCalibrationInfo3(Image* image, unsigned int isGetErrorMap);
+IMAQ_FUNC int IMAQ_STDCALL imaqLearnCalibrationGrid(Image* image, const ROI* roi, const LearnCalibrationOptions* options, const GridDescriptor* grid, const CoordinateSystem* system, const RangeFloat* range, float* quality);
+IMAQ_FUNC int IMAQ_STDCALL imaqLearnCalibrationPoints(Image* image, const CalibrationPoints* points, const ROI* roi, const LearnCalibrationOptions* options, const GridDescriptor* grid, const CoordinateSystem* system, float* quality);
+IMAQ_FUNC int IMAQ_STDCALL imaqSetCoordinateSystem(Image* image, const CoordinateSystem* system);
+IMAQ_FUNC int IMAQ_STDCALL imaqSetSimpleCalibration(Image* image, ScalingMethod method, int learnTable, const GridDescriptor* grid, const CoordinateSystem* system);
+IMAQ_FUNC TransformReport* IMAQ_STDCALL imaqTransformPixelToRealWorld(const Image* image, const PointFloat* pixelCoordinates, int numCoordinates);
+IMAQ_FUNC TransformReport* IMAQ_STDCALL imaqTransformRealWorldToPixel(const Image* image, const PointFloat* realWorldCoordinates, int numCoordinates);
+IMAQ_FUNC int IMAQ_STDCALL imaqSetSimpleCalibration2(Image* image, const GridDescriptor* gridDescriptor);
+IMAQ_FUNC int IMAQ_STDCALL imaqCalibrationSetAxisInfo(Image* image, CoordinateSystem* axisInfo);
+IMAQ_FUNC int IMAQ_STDCALL imaqCalibrationGetThumbnailImage(Image* templateImage, Image* image, CalibrationThumbnailType type, unsigned int index);
+IMAQ_FUNC GetCalibrationInfoReport* IMAQ_STDCALL imaqCalibrationGetCalibrationInfo(Image* image, unsigned int isGetErrorMap);
+IMAQ_FUNC GetCameraParametersReport* IMAQ_STDCALL imaqCalibrationGetCameraParameters(Image* templateImage);
+IMAQ_FUNC int IMAQ_STDCALL imaqCalibrationCompactInformation(Image* image);
+
+//============================================================================
+// Pixel Manipulation functions
+//============================================================================
+IMAQ_FUNC int IMAQ_STDCALL imaqArrayToComplexPlane(Image* dest, const Image* source, const float* newPixels, ComplexPlane plane);
+IMAQ_FUNC float* IMAQ_STDCALL imaqComplexPlaneToArray(const Image* image, ComplexPlane plane, Rect rect, int* rows, int* columns);
+IMAQ_FUNC int IMAQ_STDCALL imaqExtractColorPlanes(const Image* image, ColorMode mode, Image* plane1, Image* plane2, Image* plane3);
+IMAQ_FUNC int IMAQ_STDCALL imaqExtractComplexPlane(Image* dest, const Image* source, ComplexPlane plane);
+IMAQ_FUNC int IMAQ_STDCALL imaqFillImage(Image* image, PixelValue value, const Image* mask);
+IMAQ_FUNC void* IMAQ_STDCALL imaqGetLine(const Image* image, Point start, Point end, int* numPoints);
+IMAQ_FUNC int IMAQ_STDCALL imaqGetPixel(const Image* image, Point pixel, PixelValue* value);
+IMAQ_FUNC int IMAQ_STDCALL imaqReplaceColorPlanes(Image* dest, const Image* source, ColorMode mode, const Image* plane1, const Image* plane2, const Image* plane3);
+IMAQ_FUNC int IMAQ_STDCALL imaqReplaceComplexPlane(Image* dest, const Image* source, const Image* newValues, ComplexPlane plane);
+IMAQ_FUNC int IMAQ_STDCALL imaqSetLine(Image* image, const void* array, int arraySize, Point start, Point end);
+IMAQ_FUNC int IMAQ_STDCALL imaqSetPixel(Image* image, Point coord, PixelValue value);
+
+//============================================================================
+// Color Matching functions
+//============================================================================
+IMAQ_FUNC ColorInformation* IMAQ_STDCALL imaqLearnColor(const Image* image, const ROI* roi, ColorSensitivity sensitivity, int saturation);
+IMAQ_FUNC int* IMAQ_STDCALL imaqMatchColor(const Image* image, const ColorInformation* info, const ROI* roi, int* numScores);
+
+//============================================================================
+// Frequency Domain Analysis functions
+//============================================================================
+IMAQ_FUNC int IMAQ_STDCALL imaqAttenuate(Image* dest, const Image* source, AttenuateMode highlow);
+IMAQ_FUNC int IMAQ_STDCALL imaqConjugate(Image* dest, const Image* source);
+IMAQ_FUNC int IMAQ_STDCALL imaqFFT(Image* dest, const Image* source);
+IMAQ_FUNC int IMAQ_STDCALL imaqFlipFrequencies(Image* dest, const Image* source);
+IMAQ_FUNC int IMAQ_STDCALL imaqInverseFFT(Image* dest, const Image* source);
+IMAQ_FUNC int IMAQ_STDCALL imaqTruncate(Image* dest, const Image* source, TruncateMode highlow, float ratioToKeep);
+
+//============================================================================
+// Barcode I/O functions
+//============================================================================
+IMAQ_FUNC int IMAQ_STDCALL imaqGradeDataMatrixBarcodeAIM(const Image* image, AIMGradeReport* report);
+IMAQ_FUNC BarcodeInfo* IMAQ_STDCALL imaqReadBarcode(const Image* image, BarcodeType type, const ROI* roi, int validate);
+IMAQ_FUNC DataMatrixReport* IMAQ_STDCALL imaqReadDataMatrixBarcode2(Image* image, const ROI* roi, DataMatrixGradingMode prepareForGrading, const DataMatrixDescriptionOptions* descriptionOptions, const DataMatrixSizeOptions* sizeOptions, const DataMatrixSearchOptions* searchOptions);
+IMAQ_FUNC Barcode2DInfo* IMAQ_STDCALL imaqReadPDF417Barcode(const Image* image, const ROI* roi, Barcode2DSearchMode searchMode, unsigned int* numBarcodes);
+IMAQ_FUNC QRCodeReport* IMAQ_STDCALL imaqReadQRCode(Image* image, const ROI* roi, QRGradingMode reserved, const QRCodeDescriptionOptions* descriptionOptions, const QRCodeSizeOptions* sizeOptions, const QRCodeSearchOptions* searchOptions);
+
+//============================================================================
+// LCD functions
+//============================================================================
+IMAQ_FUNC int IMAQ_STDCALL imaqFindLCDSegments(ROI* roi, const Image* image, const LCDOptions* options);
+IMAQ_FUNC LCDReport* IMAQ_STDCALL imaqReadLCD(const Image* image, const ROI* roi, const LCDOptions* options);
+
+//============================================================================
+// Shape Matching functions
+//============================================================================
+IMAQ_FUNC ShapeReport* IMAQ_STDCALL imaqMatchShape(Image* dest, Image* source, const Image* templateImage, int scaleInvariant, int connectivity8, double tolerance, int* numMatches);
+
+//============================================================================
+// Contours functions
+//============================================================================
+IMAQ_FUNC ContourID IMAQ_STDCALL imaqAddAnnulusContour(ROI* roi, Annulus annulus);
+IMAQ_FUNC ContourID IMAQ_STDCALL imaqAddClosedContour(ROI* roi, const Point* points, int numPoints);
+IMAQ_FUNC ContourID IMAQ_STDCALL imaqAddLineContour(ROI* roi, Point start, Point end);
+IMAQ_FUNC ContourID IMAQ_STDCALL imaqAddOpenContour(ROI* roi, const Point* points, int numPoints);
+IMAQ_FUNC ContourID IMAQ_STDCALL imaqAddOvalContour(ROI* roi, Rect boundingBox);
+IMAQ_FUNC ContourID IMAQ_STDCALL imaqAddPointContour(ROI* roi, Point point);
+IMAQ_FUNC ContourID IMAQ_STDCALL imaqAddRectContour(ROI* roi, Rect rect);
+IMAQ_FUNC ContourID IMAQ_STDCALL imaqAddRotatedRectContour2(ROI* roi, RotatedRect rect);
+IMAQ_FUNC ContourID IMAQ_STDCALL imaqCopyContour(ROI* destRoi, const ROI* sourceRoi, ContourID id);
+IMAQ_FUNC ContourID IMAQ_STDCALL imaqGetContour(const ROI* roi, unsigned int index);
+IMAQ_FUNC int IMAQ_STDCALL imaqGetContourColor(const ROI* roi, ContourID id, RGBValue* contourColor);
+IMAQ_FUNC int IMAQ_STDCALL imaqGetContourCount(const ROI* roi);
+IMAQ_FUNC ContourInfo2* IMAQ_STDCALL imaqGetContourInfo2(const ROI* roi, ContourID id);
+IMAQ_FUNC int IMAQ_STDCALL imaqMoveContour(ROI* roi, ContourID id, int deltaX, int deltaY);
+IMAQ_FUNC int IMAQ_STDCALL imaqRemoveContour(ROI* roi, ContourID id);
+IMAQ_FUNC int IMAQ_STDCALL imaqSetContourColor(ROI* roi, ContourID id, const RGBValue* color);
+
+//============================================================================
+// Regions of Interest functions
+//============================================================================
+IMAQ_FUNC int IMAQ_STDCALL imaqConstructROI2(const Image* image, ROI* roi, Tool initialTool, const ToolWindowOptions* tools, const ConstructROIOptions2* options, int* okay);
+IMAQ_FUNC ROI* IMAQ_STDCALL imaqCreateROI(void);
+IMAQ_FUNC int IMAQ_STDCALL imaqGetROIBoundingBox(const ROI* roi, Rect* boundingBox);
+IMAQ_FUNC int IMAQ_STDCALL imaqGetROIColor(const ROI* roi, RGBValue* roiColor);
+IMAQ_FUNC ROI* IMAQ_STDCALL imaqGetWindowROI(int windowNumber);
+IMAQ_FUNC int IMAQ_STDCALL imaqSetROIColor(ROI* roi, const RGBValue* color);
+IMAQ_FUNC int IMAQ_STDCALL imaqSetWindowROI(int windowNumber, const ROI* roi);
+
+//============================================================================
+// Image Analysis functions
+//============================================================================
+IMAQ_FUNC int IMAQ_STDCALL imaqCentroid(const Image* image, PointFloat* centroid, const Image* mask);
+IMAQ_FUNC Curve* IMAQ_STDCALL imaqExtractCurves(const Image* image, const ROI* roi, const CurveOptions* curveOptions, unsigned int* numCurves);
+IMAQ_FUNC HistogramReport* IMAQ_STDCALL imaqHistogram(const Image* image, int numClasses, float min, float max, const Image* mask);
+IMAQ_FUNC LinearAverages* IMAQ_STDCALL imaqLinearAverages2(Image* image, LinearAveragesMode mode, Rect rect);
+IMAQ_FUNC LineProfile* IMAQ_STDCALL imaqLineProfile(const Image* image, Point start, Point end);
+IMAQ_FUNC QuantifyReport* IMAQ_STDCALL imaqQuantify(const Image* image, const Image* mask);
+
+//============================================================================
+// Error Management functions
+//============================================================================
+IMAQ_FUNC int IMAQ_STDCALL imaqClearError(void);
+IMAQ_FUNC char* IMAQ_STDCALL imaqGetErrorText(int errorCode);
+IMAQ_FUNC int IMAQ_STDCALL imaqGetLastError(void);
+IMAQ_FUNC const char* IMAQ_STDCALL imaqGetLastErrorFunc(void);
+IMAQ_FUNC int IMAQ_STDCALL imaqSetError(int errorCode, const char* function);
+
+//============================================================================
+// Threshold functions
+//============================================================================
+IMAQ_FUNC ThresholdData* IMAQ_STDCALL imaqAutoThreshold2(Image* dest, const Image* source, int numClasses, ThresholdMethod method, const Image* mask);
+IMAQ_FUNC int IMAQ_STDCALL imaqLocalThreshold(Image* dest, const Image* source, unsigned int windowWidth, unsigned int windowHeight, LocalThresholdMethod method, double deviationWeight, ObjectType type, float replaceValue);
+IMAQ_FUNC int IMAQ_STDCALL imaqMagicWand(Image* dest, const Image* source, Point coord, float tolerance, int connectivity8, float replaceValue);
+IMAQ_FUNC int IMAQ_STDCALL imaqMultithreshold(Image* dest, const Image* source, const ThresholdData* ranges, int numRanges);
+IMAQ_FUNC int IMAQ_STDCALL imaqThreshold(Image* dest, const Image* source, float rangeMin, float rangeMax, int useNewValue, float newValue);
+
+//============================================================================
+// Memory Management functions
+//============================================================================
+IMAQ_FUNC int IMAQ_STDCALL imaqDispose(void* object);
+
+//============================================================================
+// Pattern Matching functions
+//============================================================================
+IMAQ_FUNC CircleMatch* IMAQ_STDCALL imaqDetectCircles(const Image* image, const CircleDescriptor* circleDescriptor, const CurveOptions* curveOptions, const ShapeDetectionOptions* shapeDetectionOptions, const ROI* roi, int* numMatchesReturned);
+IMAQ_FUNC EllipseMatch* IMAQ_STDCALL imaqDetectEllipses(const Image* image, const EllipseDescriptor* ellipseDescriptor, const CurveOptions* curveOptions, const ShapeDetectionOptions* shapeDetectionOptions, const ROI* roi, int* numMatchesReturned);
+IMAQ_FUNC LineMatch* IMAQ_STDCALL imaqDetectLines(const Image* image, const LineDescriptor* lineDescriptor, const CurveOptions* curveOptions, const ShapeDetectionOptions* shapeDetectionOptions, const ROI* roi, int* numMatchesReturned);
+IMAQ_FUNC RectangleMatch* IMAQ_STDCALL imaqDetectRectangles(const Image* image, const RectangleDescriptor* rectangleDescriptor, const CurveOptions* curveOptions, const ShapeDetectionOptions* shapeDetectionOptions, const ROI* roi, int* numMatchesReturned);
+IMAQ_FUNC FeatureData* IMAQ_STDCALL imaqGetGeometricFeaturesFromCurves(const Curve* curves, unsigned int numCurves, const FeatureType* featureTypes, unsigned int numFeatureTypes, unsigned int* numFeatures);
+IMAQ_FUNC FeatureData* IMAQ_STDCALL imaqGetGeometricTemplateFeatureInfo(const Image* pattern, unsigned int* numFeatures);
+IMAQ_FUNC int IMAQ_STDCALL imaqLearnColorPattern(Image* image, const LearnColorPatternOptions* options);
+IMAQ_FUNC int IMAQ_STDCALL imaqLearnGeometricPattern(Image* image, PointFloat originOffset, const CurveOptions* curveOptions, const LearnGeometricPatternAdvancedOptions* advancedLearnOptions, const Image* mask);
+IMAQ_FUNC MultipleGeometricPattern* IMAQ_STDCALL imaqLearnMultipleGeometricPatterns(const Image** patterns, unsigned int numberOfPatterns, const String255* labels);
+IMAQ_FUNC int IMAQ_STDCALL imaqLearnPattern3(Image* image, LearningMode learningMode, LearnPatternAdvancedOptions* advancedOptions, const Image* mask);
+IMAQ_FUNC PatternMatch* IMAQ_STDCALL imaqMatchColorPattern(const Image* image, Image* pattern, const MatchColorPatternOptions* options, Rect searchRect, int* numMatches);
+IMAQ_FUNC GeometricPatternMatch2* IMAQ_STDCALL imaqMatchGeometricPattern2(const Image* image, const Image* pattern, const CurveOptions* curveOptions, const MatchGeometricPatternOptions* matchOptions, const MatchGeometricPatternAdvancedOptions2* advancedMatchOptions, const ROI* roi, int* numMatches);
+IMAQ_FUNC GeometricPatternMatch2* IMAQ_STDCALL imaqMatchMultipleGeometricPatterns(const Image* image, const MultipleGeometricPattern* multiplePattern, const ROI* roi, int* numMatches);
+IMAQ_FUNC MultipleGeometricPattern* IMAQ_STDCALL imaqReadMultipleGeometricPatternFile(const char* fileName, String255 description);
+IMAQ_FUNC PatternMatch* IMAQ_STDCALL imaqRefineMatches(const Image* image, const Image* pattern, const PatternMatch* candidatesIn, int numCandidatesIn, MatchPatternOptions* options, MatchPatternAdvancedOptions* advancedOptions, int* numCandidatesOut);
+IMAQ_FUNC int IMAQ_STDCALL imaqSetMultipleGeometricPatternsOptions(MultipleGeometricPattern* multiplePattern, const char* label, const CurveOptions* curveOptions, const MatchGeometricPatternOptions* matchOptions, const MatchGeometricPatternAdvancedOptions2* advancedMatchOptions);
+IMAQ_FUNC int IMAQ_STDCALL imaqWriteMultipleGeometricPatternFile(const MultipleGeometricPattern* multiplePattern, const char* fileName, const char* description);
+IMAQ_FUNC GeometricPatternMatch3* IMAQ_STDCALL imaqMatchGeometricPattern3(const Image* image, const Image* pattern, const CurveOptions* curveOptions, const MatchGeometricPatternOptions* matchOptions, const MatchGeometricPatternAdvancedOptions3* advancedMatchOptions, const ROI* roi, size_t* numMatches);
+IMAQ_FUNC int IMAQ_STDCALL imaqLearnGeometricPattern2(Image* image, PointFloat originOffset, double angleOffset, const CurveOptions* curveOptions, const LearnGeometricPatternAdvancedOptions2* advancedLearnOptions, const Image* mask);
+IMAQ_FUNC PatternMatch* IMAQ_STDCALL imaqMatchPattern3(const Image* image, const Image* pattern, const MatchPatternOptions* options, const MatchPatternAdvancedOptions* advancedOptions, const ROI* roi, int* numMatches);
+
+//============================================================================
+// Overlay functions
+//============================================================================
+IMAQ_FUNC int IMAQ_STDCALL imaqClearOverlay(Image* image, const char* group);
+IMAQ_FUNC int IMAQ_STDCALL imaqCopyOverlay(Image* dest, const Image* source, const char* group);
+IMAQ_FUNC int IMAQ_STDCALL imaqGetOverlayProperties(const Image* image, const char* group, TransformBehaviors* transformBehaviors);
+IMAQ_FUNC int IMAQ_STDCALL imaqMergeOverlay(Image* dest, const Image* source, const RGBValue* palette, unsigned int numColors, const char* group);
+IMAQ_FUNC int IMAQ_STDCALL imaqOverlayArc(Image* image, const ArcInfo* arc, const RGBValue* color, DrawMode drawMode, const char* group);
+IMAQ_FUNC int IMAQ_STDCALL imaqOverlayBitmap(Image* image, Point destLoc, const RGBValue* bitmap, unsigned int numCols, unsigned int numRows, const char* group);
+IMAQ_FUNC int IMAQ_STDCALL imaqOverlayClosedContour(Image* image, const Point* points, int numPoints, const RGBValue* color, DrawMode drawMode, const char* group);
+IMAQ_FUNC int IMAQ_STDCALL imaqOverlayLine(Image* image, Point start, Point end, const RGBValue* color, const char* group);
+IMAQ_FUNC int IMAQ_STDCALL imaqOverlayMetafile(Image* image, const void* metafile, Rect rect, const char* group);
+IMAQ_FUNC int IMAQ_STDCALL imaqOverlayOpenContour(Image* image, const Point* points, int numPoints, const RGBValue* color, const char* group);
+IMAQ_FUNC int IMAQ_STDCALL imaqOverlayOval(Image* image, Rect boundingBox, const RGBValue* color, DrawMode drawMode, char* group);
+IMAQ_FUNC int IMAQ_STDCALL imaqOverlayPoints(Image* image, const Point* points, int numPoints, const RGBValue* colors, int numColors, PointSymbol symbol, const UserPointSymbol* userSymbol, const char* group);
+IMAQ_FUNC int IMAQ_STDCALL imaqOverlayRect(Image* image, Rect rect, const RGBValue* color, DrawMode drawMode, const char* group);
+IMAQ_FUNC int IMAQ_STDCALL imaqOverlayROI(Image* image, const ROI* roi, PointSymbol symbol, const UserPointSymbol* userSymbol, const char* group);
+IMAQ_FUNC int IMAQ_STDCALL imaqOverlayText(Image* image, Point origin, const char* text, const RGBValue* color, const OverlayTextOptions* options, const char* group);
+IMAQ_FUNC int IMAQ_STDCALL imaqSetOverlayProperties(Image* image, const char* group, TransformBehaviors* transformBehaviors);
+
+//============================================================================
+// OCR functions
+//============================================================================
+IMAQ_FUNC CharSet* IMAQ_STDCALL imaqCreateCharSet(void);
+IMAQ_FUNC int IMAQ_STDCALL imaqDeleteChar(CharSet* set, int index);
+IMAQ_FUNC int IMAQ_STDCALL imaqGetCharCount(const CharSet* set);
+IMAQ_FUNC CharInfo2* IMAQ_STDCALL imaqGetCharInfo2(const CharSet* set, int index);
+IMAQ_FUNC int IMAQ_STDCALL imaqReadOCRFile(const char* fileName, CharSet* set, String255 setDescription, ReadTextOptions* readOptions, OCRProcessingOptions* processingOptions, OCRSpacingOptions* spacingOptions);
+IMAQ_FUNC ReadTextReport3* IMAQ_STDCALL imaqReadText3(const Image* image, const CharSet* set, const ROI* roi, const ReadTextOptions* readOptions, const OCRProcessingOptions* processingOptions, const OCRSpacingOptions* spacingOptions);
+IMAQ_FUNC int IMAQ_STDCALL imaqRenameChar(CharSet* set, int index, const char* newCharValue);
+IMAQ_FUNC int IMAQ_STDCALL imaqSetReferenceChar(const CharSet* set, int index, int isReferenceChar);
+IMAQ_FUNC int IMAQ_STDCALL imaqTrainChars(const Image* image, CharSet* set, int index, const char* charValue, const ROI* roi, const OCRProcessingOptions* processingOptions, const OCRSpacingOptions* spacingOptions);
+IMAQ_FUNC int* IMAQ_STDCALL imaqVerifyPatterns(const Image* image, const CharSet* set, const String255* expectedPatterns, int patternCount, const ROI* roi, int* numScores);
+IMAQ_FUNC int* IMAQ_STDCALL imaqVerifyText(const Image* image, const CharSet* set, const char* expectedString, const ROI* roi, int* numScores);
+IMAQ_FUNC int IMAQ_STDCALL imaqWriteOCRFile(const char* fileName, const CharSet* set, const char* setDescription, const ReadTextOptions* readOptions, const OCRProcessingOptions* processingOptions, const OCRSpacingOptions* spacingOptions);
+
+//============================================================================
+// Geometric Matching functions
+//============================================================================
+IMAQ_FUNC ExtractContourReport* IMAQ_STDCALL imaqExtractContour(Image* image, const ROI* roi, ExtractContourDirection direction, CurveParameters* curveParams, const ConnectionConstraint* connectionConstraintParams, unsigned int numOfConstraints, ExtractContourSelection selection, Image* contourImage);
+IMAQ_FUNC int IMAQ_STDCALL imaqContourOverlay(Image* image, const Image* contourImage, const ContourOverlaySettings* pointsSettings, const ContourOverlaySettings* eqnSettings, const char* groupName);
+IMAQ_FUNC ContourComputeCurvatureReport* IMAQ_STDCALL imaqContourComputeCurvature(const Image* contourImage, unsigned int kernel);
+IMAQ_FUNC CurvatureAnalysisReport* IMAQ_STDCALL imaqContourClassifyCurvature(const Image* contourImage, unsigned int kernel, RangeLabel* curvatureClasses, unsigned int numCurvatureClasses);
+IMAQ_FUNC ComputeDistancesReport* IMAQ_STDCALL imaqContourComputeDistances(const Image* targetImage, const Image* templateImage, const SetupMatchPatternData* matchSetupData, unsigned int smoothingKernel);
+IMAQ_FUNC ClassifyDistancesReport* IMAQ_STDCALL imaqContourClassifyDistances(const Image* targetImage, const Image* templateImage, const SetupMatchPatternData* matchSetupData, unsigned int smoothingKernel, const RangeLabel* distanceRanges, unsigned int numDistanceRanges);
+IMAQ_FUNC ContourInfoReport* IMAQ_STDCALL imaqContourInfo(const Image* contourImage);
+IMAQ_FUNC SetupMatchPatternData* IMAQ_STDCALL imaqContourSetupMatchPattern(MatchMode* matchMode, unsigned int enableSubPixelAccuracy, CurveParameters* curveParams, unsigned int useLearnCurveParameters, const RangeSettingDouble* rangeSettings, unsigned int numRangeSettings);
+IMAQ_FUNC int IMAQ_STDCALL imaqContourAdvancedSetupMatchPattern(SetupMatchPatternData* matchSetupData, GeometricAdvancedSetupDataOption* geometricOptions, unsigned int numGeometricOptions);
+IMAQ_FUNC ContourFitLineReport* IMAQ_STDCALL imaqContourFitLine(Image* image, double pixelRadius);
+IMAQ_FUNC PartialCircle* IMAQ_STDCALL imaqContourFitCircle(Image* image, double pixelRadius, int rejectOutliers);
+IMAQ_FUNC PartialEllipse* IMAQ_STDCALL imaqContourFitEllipse(Image* image, double pixelRadius, int rejectOutliers);
+IMAQ_FUNC ContourFitSplineReport* IMAQ_STDCALL imaqContourFitSpline(Image* image, int degree, int numberOfControlPoints);
+IMAQ_FUNC ContourFitPolynomialReport* IMAQ_STDCALL imaqContourFitPolynomial(Image* image, int order);
+
+//============================================================================
+// Edge Detection functions
+//============================================================================
+IMAQ_FUNC FindCircularEdgeReport* IMAQ_STDCALL imaqFindCircularEdge2(Image* image, const ROI* roi, const CoordinateSystem* baseSystem, const CoordinateSystem* newSystem, const FindCircularEdgeOptions* edgeOptions, const CircleFitOptions* circleFitOptions);
+IMAQ_FUNC FindConcentricEdgeReport* IMAQ_STDCALL imaqFindConcentricEdge2(Image* image, const ROI* roi, const CoordinateSystem* baseSystem, const CoordinateSystem* newSystem, const FindConcentricEdgeOptions* edgeOptions, const ConcentricEdgeFitOptions* concentricEdgeFitOptions);
+
+//============================================================================
+// Morphology Reconstruction functions
+//============================================================================
+IMAQ_FUNC int IMAQ_STDCALL imaqGrayMorphologyReconstruct(Image* dstImage, Image* srcImage, const Image* markerImage, PointFloat* points, int numOfPoints, MorphologyReconstructOperation operation, const StructuringElement* structuringElement, const ROI* roi);
+IMAQ_FUNC int IMAQ_STDCALL imaqMorphologyReconstruct(Image* dstImage, Image* srcImage, const Image* markerImage, PointFloat* points, int numOfPoints, MorphologyReconstructOperation operation, Connectivity connectivity, const ROI* roi);
+
+//============================================================================
+// Texture functions
+//============================================================================
+IMAQ_FUNC int IMAQ_STDCALL imaqDetectTextureDefect(ClassifierSession* session, Image* destImage, const Image* srcImage, const ROI* roi, int initialStepSize, int finalStepSize, unsigned char defectPixelValue, double minClassificationScore);
+IMAQ_FUNC int IMAQ_STDCALL imaqClassificationTextureDefectOptions(ClassifierSession* session, WindowSize* windowOptions, WaveletOptions* waveletOptions, void** bandsUsed, int* numBandsUsed, CooccurrenceOptions* cooccurrenceOptions, unsigned char setOperation);
+IMAQ_FUNC int IMAQ_STDCALL imaqCooccurrenceMatrix(const Image* srcImage, const ROI* roi, int levelPixel, const DisplacementVector* displacementVec, void* featureOptionArray, unsigned int featureOptionArraySize, void** cooccurrenceMatrixArray, int* coocurrenceMatrixRows, int* coocurrenceMatrixCols, void** featureVectorArray, int* featureVectorArraySize);
+IMAQ_FUNC ExtractTextureFeaturesReport* IMAQ_STDCALL imaqExtractTextureFeatures(const Image* srcImage, const ROI* roi, const WindowSize* windowOptions, const WaveletOptions* waveletOptions, void* waveletBands, unsigned int numWaveletBands, const CooccurrenceOptions* cooccurrenceOptions, unsigned char useWindow);
+IMAQ_FUNC WaveletBandsReport* IMAQ_STDCALL imaqExtractWaveletBands(const Image* srcImage, const WaveletOptions* waveletOptions, void* waveletBands, unsigned int numWaveletBands);
+
+//============================================================================
+// Regions of Interest Manipulation functions
+//============================================================================
+IMAQ_FUNC ROI* IMAQ_STDCALL imaqMaskToROI(const Image* mask, int* withinLimit);
+IMAQ_FUNC ROIProfile* IMAQ_STDCALL imaqROIProfile(const Image* image, const ROI* roi);
+IMAQ_FUNC int IMAQ_STDCALL imaqROIToMask(Image* mask, const ROI* roi, int fillValue, const Image* imageModel, int* inSpace);
+IMAQ_FUNC int IMAQ_STDCALL imaqTransformROI2(ROI* roi, const CoordinateSystem* baseSystem, const CoordinateSystem* newSystem);
+IMAQ_FUNC LabelToROIReport* IMAQ_STDCALL imaqLabelToROI(const Image* image, const unsigned int* labelsIn, unsigned int numLabelsIn, int maxNumVectors, int isExternelEdges);
+
+//============================================================================
+// Morphology functions
+//============================================================================
+IMAQ_FUNC int IMAQ_STDCALL imaqGrayMorphology(Image* dest, Image* source, MorphologyMethod method, const StructuringElement* structuringElement);
+
+//============================================================================
+// Classification functions
+//============================================================================
+IMAQ_FUNC int IMAQ_STDCALL imaqAddClassifierSample(Image* image, ClassifierSession* session, const ROI* roi, const char* sampleClass, double* featureVector, unsigned int vectorSize);
+IMAQ_FUNC ClassifierReportAdvanced* IMAQ_STDCALL imaqAdvanceClassify(Image* image, const ClassifierSession* session, const ROI* roi, double* featureVector, unsigned int vectorSize);
+IMAQ_FUNC ClassifierReport* IMAQ_STDCALL imaqClassify(Image* image, const ClassifierSession* session, const ROI* roi, double* featureVector, unsigned int vectorSize);
+IMAQ_FUNC ClassifierSession* IMAQ_STDCALL imaqCreateClassifier(ClassifierType type);
+IMAQ_FUNC int IMAQ_STDCALL imaqDeleteClassifierSample(ClassifierSession* session, int index);
+IMAQ_FUNC ClassifierAccuracyReport* IMAQ_STDCALL imaqGetClassifierAccuracy(const ClassifierSession* session);
+IMAQ_FUNC ClassifierSampleInfo* IMAQ_STDCALL imaqGetClassifierSampleInfo(const ClassifierSession* session, int index, int* numSamples);
+IMAQ_FUNC int IMAQ_STDCALL imaqGetColorClassifierOptions(const ClassifierSession* session, ColorOptions* options);
+IMAQ_FUNC int IMAQ_STDCALL imaqGetNearestNeighborOptions(const ClassifierSession* session, NearestNeighborOptions* options);
+IMAQ_FUNC int IMAQ_STDCALL imaqGetParticleClassifierOptions2(const ClassifierSession* session, ParticleClassifierPreprocessingOptions2* preprocessingOptions, ParticleClassifierOptions* options);
+IMAQ_FUNC ClassifierSession* IMAQ_STDCALL imaqReadClassifierFile(ClassifierSession* session, const char* fileName, ReadClassifierFileMode mode, ClassifierType* type, ClassifierEngineType* engine, String255 description);
+IMAQ_FUNC int IMAQ_STDCALL imaqRelabelClassifierSample(ClassifierSession* session, int index, const char* newClass);
+IMAQ_FUNC int IMAQ_STDCALL imaqSetParticleClassifierOptions2(ClassifierSession* session, const ParticleClassifierPreprocessingOptions2* preprocessingOptions, const ParticleClassifierOptions* options);
+IMAQ_FUNC int IMAQ_STDCALL imaqSetColorClassifierOptions(ClassifierSession* session, const ColorOptions* options);
+IMAQ_FUNC NearestNeighborTrainingReport* IMAQ_STDCALL imaqTrainNearestNeighborClassifier(ClassifierSession* session, const NearestNeighborOptions* options);
+IMAQ_FUNC int IMAQ_STDCALL imaqWriteClassifierFile(const ClassifierSession* session, const char* fileName, WriteClassifierFileMode mode, const String255 description);
+
+//============================================================================
+// Measure Distances functions
+//============================================================================
+IMAQ_FUNC ClampMax2Report* IMAQ_STDCALL imaqClampMax2(Image* image, const ROI* roi, const CoordinateSystem* baseSystem, const CoordinateSystem* newSystem, const CurveOptions* curveSettings, const ClampSettings* clampSettings, const ClampOverlaySettings* clampOverlaySettings);
+
+//============================================================================
+// Inspection functions
+//============================================================================
+IMAQ_FUNC int IMAQ_STDCALL imaqCompareGoldenTemplate(const Image* image, const Image* goldenTemplate, Image* brightDefects, Image* darkDefects, const InspectionAlignment* alignment, const InspectionOptions* options);
+IMAQ_FUNC int IMAQ_STDCALL imaqLearnGoldenTemplate(Image* goldenTemplate, PointFloat originOffset, const Image* mask);
+//============================================================================
+// Obsolete functions
+//============================================================================
+IMAQ_FUNC int IMAQ_STDCALL imaqRotate(Image* dest, const Image* source, float angle, PixelValue fill, InterpolationMethod method);
+IMAQ_FUNC int IMAQ_STDCALL imaqWritePNGFile(const Image* image, const char* fileName, unsigned int compressionSpeed, const RGBValue* colorTable);
+IMAQ_FUNC ParticleReport* IMAQ_STDCALL imaqSelectParticles(const Image* image, const ParticleReport* reports, int reportCount, int rejectBorder, const SelectParticleCriteria* criteria, int criteriaCount, int* selectedCount);
+IMAQ_FUNC int IMAQ_STDCALL imaqParticleFilter(Image* dest, Image* source, const ParticleFilterCriteria* criteria, int criteriaCount, int rejectMatches, int connectivity8);
+IMAQ_FUNC ParticleReport* IMAQ_STDCALL imaqGetParticleInfo(Image* image, int connectivity8, ParticleInfoMode mode, int* reportCount);
+IMAQ_FUNC int IMAQ_STDCALL imaqCalcCoeff(const Image* image, const ParticleReport* report, MeasurementValue parameter, float* coefficient);
+IMAQ_FUNC EdgeReport* IMAQ_STDCALL imaqEdgeTool(const Image* image, const Point* points, int numPoints, const EdgeOptions* options, int* numEdges);
+IMAQ_FUNC CircleReport* IMAQ_STDCALL imaqCircles(Image* dest, const Image* source, float minRadius, float maxRadius, int* numCircles);
+IMAQ_FUNC int IMAQ_STDCALL imaqLabel(Image* dest, Image* source, int connectivity8, int* particleCount);
+IMAQ_FUNC int IMAQ_STDCALL imaqFitEllipse(const PointFloat* points, int numPoints, BestEllipse* ellipse);
+IMAQ_FUNC int IMAQ_STDCALL imaqFitCircle(const PointFloat* points, int numPoints, BestCircle* circle);
+IMAQ_FUNC Color IMAQ_STDCALL imaqChangeColorSpace(const Color* sourceColor, ColorMode sourceSpace, ColorMode destSpace);
+IMAQ_FUNC PatternMatch* IMAQ_STDCALL imaqMatchPattern(const Image* image, Image* pattern, const MatchPatternOptions* options, Rect searchRect, int* numMatches);
+IMAQ_FUNC int IMAQ_STDCALL imaqConvex(Image* dest, const Image* source);
+IMAQ_FUNC int IMAQ_STDCALL imaqIsVisionInfoPresent(const Image* image, VisionInfoType type, int* present);
+IMAQ_FUNC int IMAQ_STDCALL imaqLineGaugeTool(const Image* image, Point start, Point end, LineGaugeMethod method, const EdgeOptions* edgeOptions, const CoordinateTransform* reference, float* distance);
+IMAQ_FUNC int IMAQ_STDCALL imaqBestCircle(const PointFloat* points, int numPoints, PointFloat* center, double* radius);
+IMAQ_FUNC int IMAQ_STDCALL imaqSavePattern(const Image* pattern, const char* fileName);
+IMAQ_FUNC int IMAQ_STDCALL imaqLoadPattern(Image* pattern, const char* fileName);
+IMAQ_FUNC int IMAQ_STDCALL imaqTransformROI(ROI* roi, Point originStart, float angleStart, Point originFinal, float angleFinal);
+IMAQ_FUNC int IMAQ_STDCALL imaqCoordinateReference(const Point* points, ReferenceMode mode, Point* origin, float* angle);
+IMAQ_FUNC ContourInfo* IMAQ_STDCALL imaqGetContourInfo(const ROI* roi, ContourID id);
+IMAQ_FUNC int IMAQ_STDCALL imaqSetWindowOverlay(int windowNumber, const Overlay* overlay);
+IMAQ_FUNC Overlay* IMAQ_STDCALL imaqCreateOverlayFromROI(const ROI* roi);
+IMAQ_FUNC Overlay* IMAQ_STDCALL imaqCreateOverlayFromMetafile(const void* metafile);
+IMAQ_FUNC int IMAQ_STDCALL imaqSetCalibrationInfo(Image* image, CalibrationUnit unit, float xDistance, float yDistance);
+IMAQ_FUNC int IMAQ_STDCALL imaqGetCalibrationInfo(const Image* image, CalibrationUnit* unit, float* xDistance, float* yDistance);
+IMAQ_FUNC int IMAQ_STDCALL imaqConstructROI(const Image* image, ROI* roi, Tool initialTool, const ToolWindowOptions* tools, const ConstructROIOptions* options, int* okay);
+IMAQ_FUNC int IMAQ_STDCALL imaqGetParticleClassifierOptions(const ClassifierSession* session, ParticleClassifierPreprocessingOptions* preprocessingOptions, ParticleClassifierOptions* options);
+IMAQ_FUNC int IMAQ_STDCALL imaqZoomWindow(int windowNumber, int xZoom, int yZoom, Point center);
+IMAQ_FUNC int IMAQ_STDCALL imaqGetWindowZoom(int windowNumber, int* xZoom, int* yZoom);
+IMAQ_FUNC int IMAQ_STDCALL imaqParticleFilter3(Image* dest, Image* source, const ParticleFilterCriteria2* criteria, int criteriaCount, const ParticleFilterOptions* options, const ROI* roi, int* numParticles);
+IMAQ_FUNC ReadTextReport2* IMAQ_STDCALL imaqReadText2(const Image* image, const CharSet* set, const ROI* roi, const ReadTextOptions* readOptions, const OCRProcessingOptions* processingOptions, const OCRSpacingOptions* spacingOptions);
+IMAQ_FUNC int IMAQ_STDCALL imaqLearnPattern2(Image* image, LearningMode learningMode, LearnPatternAdvancedOptions* advancedOptions);
+IMAQ_FUNC int IMAQ_STDCALL imaqConvolve(Image* dest, Image* source, const float* kernel, int matrixRows, int matrixCols, float normalize, const Image* mask);
+IMAQ_FUNC int IMAQ_STDCALL imaqDivideConstant(Image* dest, const Image* source, PixelValue value);
+IMAQ_FUNC int IMAQ_STDCALL imaqDivide(Image* dest, const Image* sourceA, const Image* sourceB);
+IMAQ_FUNC EdgeReport2* IMAQ_STDCALL imaqEdgeTool3(const Image* image, const ROI* roi, EdgeProcess processType, const EdgeOptions2* edgeOptions);
+IMAQ_FUNC ConcentricRakeReport* IMAQ_STDCALL imaqConcentricRake(const Image* image, const ROI* roi, ConcentricRakeDirection direction, EdgeProcess process, const RakeOptions* options);
+IMAQ_FUNC SpokeReport* IMAQ_STDCALL imaqSpoke(const Image* image, const ROI* roi, SpokeDirection direction, EdgeProcess process, const SpokeOptions* options);
+IMAQ_FUNC int IMAQ_STDCALL imaqLearnPattern(Image* image, LearningMode learningMode);
+IMAQ_FUNC int IMAQ_STDCALL imaqLookup(Image* dest, const Image* source, const short* table, const Image* mask);
+IMAQ_FUNC PatternMatch* IMAQ_STDCALL imaqMatchPattern2(const Image* image, const Image* pattern, const MatchPatternOptions* options, const MatchPatternAdvancedOptions* advancedOptions, Rect searchRect, int* numMatches);
+IMAQ_FUNC int IMAQ_STDCALL imaqSetParticleClassifierOptions(ClassifierSession* session, const ParticleClassifierPreprocessingOptions* preprocessingOptions, const ParticleClassifierOptions* options);
+IMAQ_FUNC int IMAQ_STDCALL imaqCopyCalibrationInfo(Image* dest, const Image* source);
+IMAQ_FUNC int IMAQ_STDCALL imaqParticleFilter2(Image* dest, Image* source, const ParticleFilterCriteria2* criteria, int criteriaCount, int rejectMatches, int connectivity8, int* numParticles);
+IMAQ_FUNC EdgeReport* IMAQ_STDCALL imaqEdgeTool2(const Image* image, const Point* points, int numPoints, EdgeProcess process, const EdgeOptions* options, int* numEdges);
+IMAQ_FUNC ContourID IMAQ_STDCALL imaqAddRotatedRectContour(ROI* roi, RotatedRect rect);
+IMAQ_FUNC Barcode2DInfo* IMAQ_STDCALL imaqReadDataMatrixBarcode(const Image* image, const ROI* roi, const DataMatrixOptions* options, unsigned int* numBarcodes);
+IMAQ_FUNC LinearAverages* IMAQ_STDCALL imaqLinearAverages(const Image* image, Rect rect);
+IMAQ_FUNC GeometricPatternMatch* IMAQ_STDCALL imaqMatchGeometricPattern(const Image* image, const Image* pattern, const CurveOptions* curveOptions, const MatchGeometricPatternOptions* matchOptions, const MatchGeometricPatternAdvancedOptions* advancedMatchOptions, const ROI* roi, int* numMatches);
+IMAQ_FUNC CharInfo* IMAQ_STDCALL imaqGetCharInfo(const CharSet* set, int index);
+IMAQ_FUNC ReadTextReport* IMAQ_STDCALL imaqReadText(const Image* image, const CharSet* set, const ROI* roi, const ReadTextOptions* readOptions, const OCRProcessingOptions* processingOptions, const OCRSpacingOptions* spacingOptions);
+IMAQ_FUNC ThresholdData* IMAQ_STDCALL imaqAutoThreshold(Image* dest, Image* source, int numClasses, ThresholdMethod method);
+IMAQ_FUNC ColorHistogramReport* IMAQ_STDCALL imaqColorHistogram(Image* image, int numClasses, ColorMode mode, const Image* mask);
+IMAQ_FUNC RakeReport* IMAQ_STDCALL imaqRake(const Image* image, const ROI* roi, RakeDirection direction, EdgeProcess process, const RakeOptions* options);
+
+IMAQ_FUNC int IMAQ_STDCALL Priv_ReadJPEGString_C(Image* image, const unsigned char* string, unsigned int stringLength);
+#endif
+
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/pcre.h b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/pcre.h
new file mode 100644
index 0000000..f8ddcbf
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/include/pcre.h
@@ -0,0 +1,338 @@
+/*************************************************
+* Perl-Compatible Regular Expressions *
+*************************************************/
+
+/* This is the public header file for the PCRE library, to be #included by
+applications that call the PCRE functions.
+
+ Copyright (c) 1997-2008 University of Cambridge
+
+-----------------------------------------------------------------------------
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ * Neither the name of the University of Cambridge nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+-----------------------------------------------------------------------------
+*/
+
+#ifndef _PCRE_H
+#define _PCRE_H
+
+/* The current PCRE version information. */
+
+#define PCRE_MAJOR 7
+#define PCRE_MINOR 8
+#define PCRE_PRERELEASE
+#define PCRE_DATE 2008-09-05
+
+/* When an application links to a PCRE DLL in Windows, the symbols that are
+imported have to be identified as such. When building PCRE, the appropriate
+export setting is defined in pcre_internal.h, which includes this file. So we
+don't change existing definitions of PCRE_EXP_DECL and PCRECPP_EXP_DECL. */
+
+/**
+ * NI CHANGE
+ *
+ * We don't build the DLL version. We only build the static lib version.
+ * Since we don't want to have to #define PCRE_STATIC in every component that
+ * includes pcre.h, we're just going to go ahead and define it here.
+ *
+ * Adam Kemp, 12/15/2008
+*/
+#define PCRE_STATIC
+
+#if defined(_WIN32) && !defined(PCRE_STATIC)
+# ifndef PCRE_EXP_DECL
+# define PCRE_EXP_DECL extern __declspec(dllimport)
+# endif
+# ifdef __cplusplus
+# ifndef PCRECPP_EXP_DECL
+# define PCRECPP_EXP_DECL extern __declspec(dllimport)
+# endif
+# ifndef PCRECPP_EXP_DEFN
+# define PCRECPP_EXP_DEFN __declspec(dllimport)
+# endif
+# endif
+#endif
+
+/* By default, we use the standard "extern" declarations. */
+
+#ifndef PCRE_EXP_DECL
+# ifdef __cplusplus
+# define PCRE_EXP_DECL extern "C"
+# else
+# define PCRE_EXP_DECL extern
+# endif
+#endif
+
+#ifdef __cplusplus
+# ifndef PCRECPP_EXP_DECL
+# define PCRECPP_EXP_DECL extern
+# endif
+# ifndef PCRECPP_EXP_DEFN
+# define PCRECPP_EXP_DEFN
+# endif
+#endif
+
+/**
+ * NI CHANGE
+ *
+ * We use __cdecl on win32 and the default calling convention elsewhere.
+ *
+ * Originall this macro did not appear in this file, but it was used in
+ * internal headers. I consider it an oversight on the part of the pcre
+ * developers that * it was not used in this file. If these functions use
+ * specific calling conventions then their prototypes should include that
+ * calling convention in case some other project uses a different default.
+ *
+ * Adam Kemp 12/15/2008
+*/
+#ifndef PCRE_CALL_CONVENTION
+# if defined(_WIN32) /* 32-bit and 64-bit */
+# define PCRE_CALL_CONVENTION __cdecl
+# else
+# define PCRE_CALL_CONVENTION
+# endif
+#else
+# define PCRE_CALL_CONVENTION
+#endif
+
+/* Have to include stdlib.h in order to ensure that size_t is defined;
+it is needed here for malloc. */
+
+#include <stdlib.h>
+
+/* Allow for C++ users */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Options */
+
+#define PCRE_CASELESS 0x00000001
+#define PCRE_MULTILINE 0x00000002
+#define PCRE_DOTALL 0x00000004
+#define PCRE_EXTENDED 0x00000008
+#define PCRE_ANCHORED 0x00000010
+#define PCRE_DOLLAR_ENDONLY 0x00000020
+#define PCRE_EXTRA 0x00000040
+#define PCRE_NOTBOL 0x00000080
+#define PCRE_NOTEOL 0x00000100
+#define PCRE_UNGREEDY 0x00000200
+#define PCRE_NOTEMPTY 0x00000400
+#define PCRE_UTF8 0x00000800
+#define PCRE_NO_AUTO_CAPTURE 0x00001000
+#define PCRE_NO_UTF8_CHECK 0x00002000
+#define PCRE_AUTO_CALLOUT 0x00004000
+#define PCRE_PARTIAL 0x00008000
+#define PCRE_DFA_SHORTEST 0x00010000
+#define PCRE_DFA_RESTART 0x00020000
+#define PCRE_FIRSTLINE 0x00040000
+#define PCRE_DUPNAMES 0x00080000
+#define PCRE_NEWLINE_CR 0x00100000
+#define PCRE_NEWLINE_LF 0x00200000
+#define PCRE_NEWLINE_CRLF 0x00300000
+#define PCRE_NEWLINE_ANY 0x00400000
+#define PCRE_NEWLINE_ANYCRLF 0x00500000
+#define PCRE_BSR_ANYCRLF 0x00800000
+#define PCRE_BSR_UNICODE 0x01000000
+#define PCRE_JAVASCRIPT_COMPAT 0x02000000
+
+/* Exec-time and get/set-time error codes */
+
+#define PCRE_ERROR_NOMATCH (-1)
+#define PCRE_ERROR_NULL (-2)
+#define PCRE_ERROR_BADOPTION (-3)
+#define PCRE_ERROR_BADMAGIC (-4)
+#define PCRE_ERROR_UNKNOWN_OPCODE (-5)
+#define PCRE_ERROR_UNKNOWN_NODE (-5) /* For backward compatibility */
+#define PCRE_ERROR_NOMEMORY (-6)
+#define PCRE_ERROR_NOSUBSTRING (-7)
+#define PCRE_ERROR_MATCHLIMIT (-8)
+#define PCRE_ERROR_CALLOUT (-9) /* Never used by PCRE itself */
+#define PCRE_ERROR_BADUTF8 (-10)
+#define PCRE_ERROR_BADUTF8_OFFSET (-11)
+#define PCRE_ERROR_PARTIAL (-12)
+#define PCRE_ERROR_BADPARTIAL (-13)
+#define PCRE_ERROR_INTERNAL (-14)
+#define PCRE_ERROR_BADCOUNT (-15)
+#define PCRE_ERROR_DFA_UITEM (-16)
+#define PCRE_ERROR_DFA_UCOND (-17)
+#define PCRE_ERROR_DFA_UMLIMIT (-18)
+#define PCRE_ERROR_DFA_WSSIZE (-19)
+#define PCRE_ERROR_DFA_RECURSE (-20)
+#define PCRE_ERROR_RECURSIONLIMIT (-21)
+#define PCRE_ERROR_NULLWSLIMIT (-22) /* No longer actually used */
+#define PCRE_ERROR_BADNEWLINE (-23)
+
+/* Request types for pcre_fullinfo() */
+
+#define PCRE_INFO_OPTIONS 0
+#define PCRE_INFO_SIZE 1
+#define PCRE_INFO_CAPTURECOUNT 2
+#define PCRE_INFO_BACKREFMAX 3
+#define PCRE_INFO_FIRSTBYTE 4
+#define PCRE_INFO_FIRSTCHAR 4 /* For backwards compatibility */
+#define PCRE_INFO_FIRSTTABLE 5
+#define PCRE_INFO_LASTLITERAL 6
+#define PCRE_INFO_NAMEENTRYSIZE 7
+#define PCRE_INFO_NAMECOUNT 8
+#define PCRE_INFO_NAMETABLE 9
+#define PCRE_INFO_STUDYSIZE 10
+#define PCRE_INFO_DEFAULT_TABLES 11
+#define PCRE_INFO_OKPARTIAL 12
+#define PCRE_INFO_JCHANGED 13
+#define PCRE_INFO_HASCRORLF 14
+
+/* Request types for pcre_config(). Do not re-arrange, in order to remain
+compatible. */
+
+#define PCRE_CONFIG_UTF8 0
+#define PCRE_CONFIG_NEWLINE 1
+#define PCRE_CONFIG_LINK_SIZE 2
+#define PCRE_CONFIG_POSIX_MALLOC_THRESHOLD 3
+#define PCRE_CONFIG_MATCH_LIMIT 4
+#define PCRE_CONFIG_STACKRECURSE 5
+#define PCRE_CONFIG_UNICODE_PROPERTIES 6
+#define PCRE_CONFIG_MATCH_LIMIT_RECURSION 7
+#define PCRE_CONFIG_BSR 8
+
+/* Bit flags for the pcre_extra structure. Do not re-arrange or redefine
+these bits, just add new ones on the end, in order to remain compatible. */
+
+#define PCRE_EXTRA_STUDY_DATA 0x0001
+#define PCRE_EXTRA_MATCH_LIMIT 0x0002
+#define PCRE_EXTRA_CALLOUT_DATA 0x0004
+#define PCRE_EXTRA_TABLES 0x0008
+#define PCRE_EXTRA_MATCH_LIMIT_RECURSION 0x0010
+
+/* Types */
+
+struct real_pcre; /* declaration; the definition is private */
+typedef struct real_pcre pcre;
+
+/* When PCRE is compiled as a C++ library, the subject pointer type can be
+replaced with a custom type. For conventional use, the public interface is a
+const char *. */
+
+#ifndef PCRE_SPTR
+#define PCRE_SPTR const char *
+#endif
+
+/* The structure for passing additional data to pcre_exec(). This is defined in
+such as way as to be extensible. Always add new fields at the end, in order to
+remain compatible. */
+
+typedef struct pcre_extra {
+ unsigned long int flags; /* Bits for which fields are set */
+ void *study_data; /* Opaque data from pcre_study() */
+ unsigned long int match_limit; /* Maximum number of calls to match() */
+ void *callout_data; /* Data passed back in callouts */
+ const unsigned char *tables; /* Pointer to character tables */
+ unsigned long int match_limit_recursion; /* Max recursive calls to match() */
+} pcre_extra;
+
+/* The structure for passing out data via the pcre_callout_function. We use a
+structure so that new fields can be added on the end in future versions,
+without changing the API of the function, thereby allowing old clients to work
+without modification. */
+
+typedef struct pcre_callout_block {
+ int version; /* Identifies version of block */
+ /* ------------------------ Version 0 ------------------------------- */
+ int callout_number; /* Number compiled into pattern */
+ int *offset_vector; /* The offset vector */
+ PCRE_SPTR subject; /* The subject being matched */
+ int subject_length; /* The length of the subject */
+ int start_match; /* Offset to start of this match attempt */
+ int current_position; /* Where we currently are in the subject */
+ int capture_top; /* Max current capture */
+ int capture_last; /* Most recently closed capture */
+ void *callout_data; /* Data passed in with the call */
+ /* ------------------- Added for Version 1 -------------------------- */
+ int pattern_position; /* Offset to next item in the pattern */
+ int next_item_length; /* Length of next item in the pattern */
+ /* ------------------------------------------------------------------ */
+} pcre_callout_block;
+
+/* Indirection for store get and free functions. These can be set to
+alternative malloc/free functions if required. Special ones are used in the
+non-recursive case for "frames". There is also an optional callout function
+that is triggered by the (?) regex item. For Virtual Pascal, these definitions
+have to take another form. */
+
+#ifndef VPCOMPAT
+PCRE_EXP_DECL void* (PCRE_CALL_CONVENTION *pcre_malloc)(size_t);
+PCRE_EXP_DECL void (PCRE_CALL_CONVENTION *pcre_free)(void *);
+PCRE_EXP_DECL void* (PCRE_CALL_CONVENTION *pcre_stack_malloc)(size_t);
+PCRE_EXP_DECL void (PCRE_CALL_CONVENTION *pcre_stack_free)(void *);
+PCRE_EXP_DECL int (PCRE_CALL_CONVENTION *pcre_callout)(pcre_callout_block *);
+#else /* VPCOMPAT */
+PCRE_EXP_DECL void* PCRE_CALL_CONVENTION pcre_malloc(size_t);
+PCRE_EXP_DECL void PCRE_CALL_CONVENTION pcre_free(void *);
+PCRE_EXP_DECL void* PCRE_CALL_CONVENTION pcre_stack_malloc(size_t);
+PCRE_EXP_DECL void PCRE_CALL_CONVENTION pcre_stack_free(void *);
+PCRE_EXP_DECL int PCRE_CALL_CONVENTION pcre_callout(pcre_callout_block *);
+#endif /* VPCOMPAT */
+
+/* Exported PCRE functions */
+
+PCRE_EXP_DECL pcre* PCRE_CALL_CONVENTION pcre_compile(const char *, int, const char **, int *,
+ const unsigned char *);
+PCRE_EXP_DECL pcre* PCRE_CALL_CONVENTION pcre_compile2(const char *, int, int *, const char **,
+ int *, const unsigned char *);
+PCRE_EXP_DECL int PCRE_CALL_CONVENTION pcre_config(int, void *);
+PCRE_EXP_DECL int PCRE_CALL_CONVENTION pcre_copy_named_substring(const pcre *, const char *,
+ int *, int, const char *, char *, int);
+PCRE_EXP_DECL int PCRE_CALL_CONVENTION pcre_copy_substring(const char *, int *, int, int, char *,
+ int);
+PCRE_EXP_DECL int PCRE_CALL_CONVENTION pcre_dfa_exec(const pcre *, const pcre_extra *,
+ const char *, int, int, int, int *, int , int *, int);
+PCRE_EXP_DECL int PCRE_CALL_CONVENTION pcre_exec(const pcre *, const pcre_extra *, PCRE_SPTR,
+ int, int, int, int *, int);
+PCRE_EXP_DECL void PCRE_CALL_CONVENTION pcre_free_substring(const char *);
+PCRE_EXP_DECL void PCRE_CALL_CONVENTION pcre_free_substring_list(const char **);
+PCRE_EXP_DECL int PCRE_CALL_CONVENTION pcre_fullinfo(const pcre *, const pcre_extra *, int,
+ void *);
+PCRE_EXP_DECL int PCRE_CALL_CONVENTION pcre_get_named_substring(const pcre *, const char *,
+ int *, int, const char *, const char **);
+PCRE_EXP_DECL int PCRE_CALL_CONVENTION pcre_get_stringnumber(const pcre *, const char *);
+PCRE_EXP_DECL int PCRE_CALL_CONVENTION pcre_get_stringtable_entries(const pcre *, const char *,
+ char **, char **);
+PCRE_EXP_DECL int PCRE_CALL_CONVENTION pcre_get_substring(const char *, int *, int, int,
+ const char **);
+PCRE_EXP_DECL int PCRE_CALL_CONVENTION pcre_get_substring_list(const char *, int *, int,
+ const char ***);
+PCRE_EXP_DECL int PCRE_CALL_CONVENTION pcre_info(const pcre *, int *, int *);
+PCRE_EXP_DECL const unsigned char* PCRE_CALL_CONVENTION pcre_maketables(void);
+PCRE_EXP_DECL int PCRE_CALL_CONVENTION pcre_refcount(pcre *, int);
+PCRE_EXP_DECL pcre_extra* PCRE_CALL_CONVENTION pcre_study(const pcre *, int, const char **);
+PCRE_EXP_DECL const char* PCRE_CALL_CONVENTION pcre_version(void);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* End of pcre.h */
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/ADXL345_I2C.cpp b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/ADXL345_I2C.cpp
new file mode 100644
index 0000000..997cf59
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/ADXL345_I2C.cpp
@@ -0,0 +1,103 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+
+#include "ADXL345_I2C.h"
+#include "I2C.h"
+#include "HAL/HAL.hpp"
+
+const uint8_t ADXL345_I2C::kAddress;
+const uint8_t ADXL345_I2C::kPowerCtlRegister;
+const uint8_t ADXL345_I2C::kDataFormatRegister;
+const uint8_t ADXL345_I2C::kDataRegister;
+constexpr double ADXL345_I2C::kGsPerLSB;
+
+/**
+ * Constructor.
+ *
+ * @param port The I2C port the accelerometer is attached to
+ * @param range The range (+ or -) that the accelerometer will measure.
+ */
+ADXL345_I2C::ADXL345_I2C(Port port, Range range):
+ I2C(port, kAddress)
+{
+ //m_i2c = new I2C((I2C::Port)port, kAddress);
+
+ // Turn on the measurements
+ Write(kPowerCtlRegister, kPowerCtl_Measure);
+ // Specify the data format to read
+ SetRange(range);
+
+ HALReport(HALUsageReporting::kResourceType_ADXL345, HALUsageReporting::kADXL345_I2C, 0);
+}
+
+/**
+ * Destructor.
+ */
+ADXL345_I2C::~ADXL345_I2C()
+{
+ //delete m_i2c;
+ //m_i2c = NULL;
+}
+
+/** {@inheritdoc} */
+void ADXL345_I2C::SetRange(Range range)
+{
+ Write(kDataFormatRegister, kDataFormat_FullRes | (uint8_t)range);
+}
+
+/** {@inheritdoc} */
+double ADXL345_I2C::GetX()
+{
+ return GetAcceleration(kAxis_X);
+}
+
+/** {@inheritdoc} */
+double ADXL345_I2C::GetY()
+{
+ return GetAcceleration(kAxis_Y);
+}
+
+/** {@inheritdoc} */
+double ADXL345_I2C::GetZ()
+{
+ return GetAcceleration(kAxis_Z);
+}
+
+/**
+ * Get the acceleration of one axis in Gs.
+ *
+ * @param axis The axis to read from.
+ * @return Acceleration of the ADXL345 in Gs.
+ */
+double ADXL345_I2C::GetAcceleration(ADXL345_I2C::Axes axis)
+{
+ int16_t rawAccel = 0;
+ //if(m_i2c)
+ //{
+ Read(kDataRegister + (uint8_t)axis, sizeof(rawAccel), (uint8_t *)&rawAccel);
+ //}
+ return rawAccel * kGsPerLSB;
+}
+
+/**
+ * Get the acceleration of all axes in Gs.
+ *
+ * @return An object containing the acceleration measured on each axis of the ADXL345 in Gs.
+ */
+ADXL345_I2C::AllAxes ADXL345_I2C::GetAccelerations()
+{
+ AllAxes data = AllAxes();
+ int16_t rawData[3];
+ //if (m_i2c)
+ //{
+ Read(kDataRegister, sizeof(rawData), (uint8_t*)rawData);
+
+ data.XAxis = rawData[0] * kGsPerLSB;
+ data.YAxis = rawData[1] * kGsPerLSB;
+ data.ZAxis = rawData[2] * kGsPerLSB;
+ //}
+ return data;
+}
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/ADXL345_SPI.cpp b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/ADXL345_SPI.cpp
new file mode 100644
index 0000000..77c793d
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/ADXL345_SPI.cpp
@@ -0,0 +1,140 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+
+#include "ADXL345_SPI.h"
+#include "DigitalInput.h"
+#include "DigitalOutput.h"
+#include "SPI.h"
+
+const uint8_t ADXL345_SPI::kPowerCtlRegister;
+const uint8_t ADXL345_SPI::kDataFormatRegister;
+const uint8_t ADXL345_SPI::kDataRegister;
+constexpr double ADXL345_SPI::kGsPerLSB;
+
+
+/**
+ * Constructor.
+ *
+ * @param port The SPI port the accelerometer is attached to
+ * @param range The range (+ or -) that the accelerometer will measure.
+ */
+ADXL345_SPI::ADXL345_SPI(SPI::Port port, ADXL345_SPI::Range range)
+{
+ m_port = port;
+ Init(range);
+}
+
+/**
+ * Internal common init function.
+ */
+void ADXL345_SPI::Init(Range range)
+{
+ m_spi = new SPI(m_port);
+ m_spi->SetClockRate(500000);
+ m_spi->SetMSBFirst();
+ m_spi->SetSampleDataOnFalling();
+ m_spi->SetClockActiveLow();
+ m_spi->SetChipSelectActiveHigh();
+
+ uint8_t commands[2];
+ // Turn on the measurements
+ commands[0] = kPowerCtlRegister;
+ commands[1] = kPowerCtl_Measure;
+ m_spi->Transaction(commands, commands, 2);
+
+ SetRange(range);
+
+ HALReport(HALUsageReporting::kResourceType_ADXL345, HALUsageReporting::kADXL345_SPI);
+}
+
+/**
+ * Destructor.
+ */
+ADXL345_SPI::~ADXL345_SPI()
+{
+ delete m_spi;
+ m_spi = NULL;
+}
+
+/** {@inheritdoc} */
+void ADXL345_SPI::SetRange(Range range)
+{
+ uint8_t commands[2];
+
+ // Specify the data format to read
+ commands[0] = kDataFormatRegister;
+ commands[1] = kDataFormat_FullRes| (uint8_t)(range & 0x03);
+ m_spi->Transaction(commands, commands, 2);
+}
+
+/** {@inheritdoc} */
+double ADXL345_SPI::GetX()
+{
+ return GetAcceleration(kAxis_X);
+}
+
+/** {@inheritdoc} */
+double ADXL345_SPI::GetY()
+{
+ return GetAcceleration(kAxis_Y);
+}
+
+/** {@inheritdoc} */
+double ADXL345_SPI::GetZ()
+{
+ return GetAcceleration(kAxis_Z);
+}
+
+/**
+ * Get the acceleration of one axis in Gs.
+ *
+ * @param axis The axis to read from.
+ * @return Acceleration of the ADXL345 in Gs.
+ */
+double ADXL345_SPI::GetAcceleration(ADXL345_SPI::Axes axis)
+{
+ int16_t rawAccel = 0;
+ if(m_spi)
+ {
+ uint8_t buffer[3];
+ uint8_t command[3] = {0,0,0};
+ command[0] = (kAddress_Read | kAddress_MultiByte | kDataRegister) + (uint8_t)axis;
+ m_spi->Transaction(command, buffer, 3);
+
+ // Sensor is little endian... swap bytes
+ rawAccel = buffer[2]<<8 | buffer[1];
+ }
+ return rawAccel * kGsPerLSB;
+}
+
+/**
+ * Get the acceleration of all axes in Gs.
+ *
+ * @return An object containing the acceleration measured on each axis of the ADXL345 in Gs.
+ */
+ADXL345_SPI::AllAxes ADXL345_SPI::GetAccelerations()
+{
+ AllAxes data = AllAxes();
+ uint8_t dataBuffer[7] = {0,0,0,0,0,0,0};
+ int16_t rawData[3];
+ if (m_spi)
+ {
+ // Select the data address.
+ dataBuffer[0] = (kAddress_Read | kAddress_MultiByte | kDataRegister);
+ m_spi->Transaction(dataBuffer, dataBuffer, 7);
+
+ for (int32_t i=0; i<3; i++)
+ {
+ // Sensor is little endian... swap bytes
+ rawData[i] = dataBuffer[i*2+2] << 8 | dataBuffer[i*2+1];
+ }
+
+ data.XAxis = rawData[0] * kGsPerLSB;
+ data.YAxis = rawData[1] * kGsPerLSB;
+ data.ZAxis = rawData[2] * kGsPerLSB;
+ }
+ return data;
+}
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/AnalogAccelerometer.cpp b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/AnalogAccelerometer.cpp
new file mode 100644
index 0000000..15edf4b
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/AnalogAccelerometer.cpp
@@ -0,0 +1,110 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+
+#include "AnalogAccelerometer.h"
+//#include "NetworkCommunication/UsageReporting.h"
+#include "WPIErrors.h"
+
+/**
+ * Common function for initializing the accelerometer.
+ */
+void AnalogAccelerometer::InitAccelerometer()
+{
+ m_voltsPerG = 1.0;
+ m_zeroGVoltage = 2.5;
+ HALReport(HALUsageReporting::kResourceType_Accelerometer, m_AnalogInput->GetChannel());
+}
+
+/**
+ * Create a new instance of an accelerometer.
+ * The constructor allocates desired analog input.
+ * @param channel The channel number for the analog input the accelerometer is connected to
+ */
+AnalogAccelerometer::AnalogAccelerometer(int32_t channel)
+{
+ m_AnalogInput = new AnalogInput(channel);
+ m_allocatedChannel = true;
+ InitAccelerometer();
+}
+
+/**
+ * Create a new instance of Accelerometer from an existing AnalogInput.
+ * Make a new instance of accelerometer given an AnalogInput. This is particularly
+ * useful if the port is going to be read as an analog channel as well as through
+ * the Accelerometer class.
+ * @param channel The existing AnalogInput object for the analog input the accelerometer is connected to
+ */
+AnalogAccelerometer::AnalogAccelerometer(AnalogInput *channel)
+{
+ if (channel == NULL)
+ {
+ wpi_setWPIError(NullParameter);
+ }
+ else
+ {
+ m_AnalogInput = channel;
+ InitAccelerometer();
+ }
+ m_allocatedChannel = false;
+}
+
+/**
+ * Delete the analog components used for the accelerometer.
+ */
+AnalogAccelerometer::~AnalogAccelerometer()
+{
+ if (m_allocatedChannel)
+ {
+ delete m_AnalogInput;
+ }
+}
+
+/**
+ * Return the acceleration in Gs.
+ *
+ * The acceleration is returned units of Gs.
+ *
+ * @return The current acceleration of the sensor in Gs.
+ */
+float AnalogAccelerometer::GetAcceleration()
+{
+ return (m_AnalogInput->GetAverageVoltage() - m_zeroGVoltage) / m_voltsPerG;
+}
+
+/**
+ * Set the accelerometer sensitivity.
+ *
+ * This sets the sensitivity of the accelerometer used for calculating the acceleration.
+ * The sensitivity varies by accelerometer model. There are constants defined for various models.
+ *
+ * @param sensitivity The sensitivity of accelerometer in Volts per G.
+ */
+void AnalogAccelerometer::SetSensitivity(float sensitivity)
+{
+ m_voltsPerG = sensitivity;
+}
+
+/**
+ * Set the voltage that corresponds to 0 G.
+ *
+ * The zero G voltage varies by accelerometer model. There are constants defined for various models.
+ *
+ * @param zero The zero G voltage.
+ */
+void AnalogAccelerometer::SetZero(float zero)
+{
+ m_zeroGVoltage = zero;
+}
+
+/**
+ * Get the Acceleration for the PID Source parent.
+ *
+ * @return The current acceleration in Gs.
+ */
+double AnalogAccelerometer::PIDGet()
+{
+ return GetAcceleration();
+}
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/AnalogInput.cpp b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/AnalogInput.cpp
new file mode 100644
index 0000000..bf19a9c
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/AnalogInput.cpp
@@ -0,0 +1,414 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+
+#include "AnalogInput.h"
+//#include "NetworkCommunication/UsageReporting.h"
+#include "Resource.h"
+#include "Timer.h"
+#include "WPIErrors.h"
+
+static Resource *inputs = NULL;
+
+const uint8_t AnalogInput::kAccumulatorModuleNumber;
+const uint32_t AnalogInput::kAccumulatorNumChannels;
+const uint32_t AnalogInput::kAccumulatorChannels[] = {0, 1};
+
+/**
+ * Common initialization.
+ */
+void AnalogInput::InitAnalogInput(uint32_t channel)
+{
+ char buf[64];
+ Resource::CreateResourceObject(&inputs, kAnalogInputs);
+
+ if (!checkAnalogInputChannel(channel))
+ {
+ snprintf(buf, 64, "analog input %d", channel);
+ wpi_setWPIErrorWithContext(ChannelIndexOutOfRange, buf);
+ return;
+ }
+
+ snprintf(buf, 64, "Analog Input %d", channel);
+ if (inputs->Allocate(channel, buf) == ~0ul)
+ {
+ CloneError(inputs);
+ return;
+ }
+
+ m_channel = channel;
+
+ void* port = getPort(channel);
+ int32_t status = 0;
+ m_port = initializeAnalogInputPort(port, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+
+ HALReport(HALUsageReporting::kResourceType_AnalogChannel, channel);
+}
+
+/**
+ * Construct an analog input.
+ *
+ * @param channel The channel number on the roboRIO to represent. 0-3 are on-board 4-7 are on the MXP port.
+ */
+AnalogInput::AnalogInput(uint32_t channel)
+{
+ InitAnalogInput(channel);
+}
+
+/**
+ * Channel destructor.
+ */
+AnalogInput::~AnalogInput()
+{
+ inputs->Free(m_channel);
+}
+
+/**
+ * Get a sample straight from this channel.
+ * The sample is a 12-bit value representing the 0V to 5V range of the A/D converter in the module.
+ * The units are in A/D converter codes. Use GetVoltage() to get the analog value in calibrated units.
+ * @return A sample straight from this channel.
+ */
+int16_t AnalogInput::GetValue()
+{
+ if (StatusIsFatal()) return 0;
+ int32_t status = 0;
+ int16_t value = getAnalogValue(m_port, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ return value;
+}
+
+/**
+ * Get a sample from the output of the oversample and average engine for this channel.
+ * The sample is 12-bit + the bits configured in SetOversampleBits().
+ * The value configured in SetAverageBits() will cause this value to be averaged 2**bits number of samples.
+ * This is not a sliding window. The sample will not change until 2**(OversampleBits + AverageBits) samples
+ * have been acquired from the module on this channel.
+ * Use GetAverageVoltage() to get the analog value in calibrated units.
+ * @return A sample from the oversample and average engine for this channel.
+ */
+int32_t AnalogInput::GetAverageValue()
+{
+ if (StatusIsFatal()) return 0;
+ int32_t status = 0;
+ int32_t value = getAnalogAverageValue(m_port, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ return value;
+}
+
+/**
+ * Get a scaled sample straight from this channel.
+ * The value is scaled to units of Volts using the calibrated scaling data from GetLSBWeight() and GetOffset().
+ * @return A scaled sample straight from this channel.
+ */
+float AnalogInput::GetVoltage()
+{
+ if (StatusIsFatal()) return 0.0f;
+ int32_t status = 0;
+ float voltage = getAnalogVoltage(m_port, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ return voltage;
+}
+
+/**
+ * Get a scaled sample from the output of the oversample and average engine for this channel.
+ * The value is scaled to units of Volts using the calibrated scaling data from GetLSBWeight() and GetOffset().
+ * Using oversampling will cause this value to be higher resolution, but it will update more slowly.
+ * Using averaging will cause this value to be more stable, but it will update more slowly.
+ * @return A scaled sample from the output of the oversample and average engine for this channel.
+ */
+float AnalogInput::GetAverageVoltage()
+{
+ if (StatusIsFatal()) return 0.0f;
+ int32_t status = 0;
+ float voltage = getAnalogAverageVoltage(m_port, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ return voltage;
+}
+
+/**
+ * Get the factory scaling least significant bit weight constant.
+ *
+ * Volts = ((LSB_Weight * 1e-9) * raw) - (Offset * 1e-9)
+ *
+ * @return Least significant bit weight.
+ */
+uint32_t AnalogInput::GetLSBWeight()
+{
+ if (StatusIsFatal()) return 0;
+ int32_t status = 0;
+ int32_t lsbWeight = getAnalogLSBWeight(m_port, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ return lsbWeight;
+}
+
+/**
+ * Get the factory scaling offset constant.
+ *
+ * Volts = ((LSB_Weight * 1e-9) * raw) - (Offset * 1e-9)
+ *
+ * @return Offset constant.
+ */
+int32_t AnalogInput::GetOffset()
+{
+ if (StatusIsFatal()) return 0;
+ int32_t status = 0;
+ int32_t offset = getAnalogOffset(m_port, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ return offset;
+}
+
+/**
+ * Get the channel number.
+ * @return The channel number.
+ */
+uint32_t AnalogInput::GetChannel()
+{
+ if (StatusIsFatal()) return 0;
+ return m_channel;
+}
+
+/**
+ * Set the number of averaging bits.
+ * This sets the number of averaging bits. The actual number of averaged samples is 2^bits.
+ * Use averaging to improve the stability of your measurement at the expense of sampling rate.
+ * The averaging is done automatically in the FPGA.
+ *
+ * @param bits Number of bits of averaging.
+ */
+void AnalogInput::SetAverageBits(uint32_t bits)
+{
+ if (StatusIsFatal()) return;
+ int32_t status = 0;
+ setAnalogAverageBits(m_port, bits, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+}
+
+/**
+ * Get the number of averaging bits previously configured.
+ * This gets the number of averaging bits from the FPGA. The actual number of averaged samples is 2^bits.
+ * The averaging is done automatically in the FPGA.
+ *
+ * @return Number of bits of averaging previously configured.
+ */
+uint32_t AnalogInput::GetAverageBits()
+{
+ int32_t status = 0;
+ int32_t averageBits = getAnalogAverageBits(m_port, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ return averageBits;
+}
+
+/**
+ * Set the number of oversample bits.
+ * This sets the number of oversample bits. The actual number of oversampled values is 2^bits.
+ * Use oversampling to improve the resolution of your measurements at the expense of sampling rate.
+ * The oversampling is done automatically in the FPGA.
+ *
+ * @param bits Number of bits of oversampling.
+ */
+void AnalogInput::SetOversampleBits(uint32_t bits)
+{
+ if (StatusIsFatal()) return;
+ int32_t status = 0;
+ setAnalogOversampleBits(m_port, bits, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+}
+
+/**
+ * Get the number of oversample bits previously configured.
+ * This gets the number of oversample bits from the FPGA. The actual number of oversampled values is
+ * 2^bits. The oversampling is done automatically in the FPGA.
+ *
+ * @return Number of bits of oversampling previously configured.
+ */
+uint32_t AnalogInput::GetOversampleBits()
+{
+ if (StatusIsFatal()) return 0;
+ int32_t status = 0;
+ int32_t oversampleBits = getAnalogOversampleBits(m_port, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ return oversampleBits;
+}
+
+/**
+ * Is the channel attached to an accumulator.
+ *
+ * @return The analog input is attached to an accumulator.
+ */
+bool AnalogInput::IsAccumulatorChannel()
+{
+ if (StatusIsFatal()) return false;
+ int32_t status = 0;
+ bool isAccum = isAccumulatorChannel(m_port, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ return isAccum;
+}
+
+/**
+ * Initialize the accumulator.
+ */
+void AnalogInput::InitAccumulator()
+{
+ if (StatusIsFatal()) return;
+ m_accumulatorOffset = 0;
+ int32_t status = 0;
+ initAccumulator(m_port, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+}
+
+
+/**
+ * Set an initial value for the accumulator.
+ *
+ * This will be added to all values returned to the user.
+ * @param initialValue The value that the accumulator should start from when reset.
+ */
+void AnalogInput::SetAccumulatorInitialValue(int64_t initialValue)
+{
+ if (StatusIsFatal()) return;
+ m_accumulatorOffset = initialValue;
+}
+
+/**
+ * Resets the accumulator to the initial value.
+ */
+void AnalogInput::ResetAccumulator()
+{
+ if (StatusIsFatal()) return;
+ int32_t status = 0;
+ resetAccumulator(m_port, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+
+ if(!StatusIsFatal())
+ {
+ // Wait until the next sample, so the next call to GetAccumulator*()
+ // won't have old values.
+ const float sampleTime = 1.0f / GetSampleRate();
+ const float overSamples = 1 << GetOversampleBits();
+ const float averageSamples = 1 << GetAverageBits();
+ Wait(sampleTime * overSamples * averageSamples);
+ }
+}
+
+/**
+ * Set the center value of the accumulator.
+ *
+ * The center value is subtracted from each A/D value before it is added to the accumulator. This
+ * is used for the center value of devices like gyros and accelerometers to
+ * take the device offset into account when integrating.
+ *
+ * This center value is based on the output of the oversampled and averaged source from the accumulator
+ * channel. Because of this, any non-zero oversample bits will affect the size of the value for this field.
+ */
+void AnalogInput::SetAccumulatorCenter(int32_t center)
+{
+ if (StatusIsFatal()) return;
+ int32_t status = 0;
+ setAccumulatorCenter(m_port, center, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+}
+
+/**
+ * Set the accumulator's deadband.
+ * @param
+ */
+void AnalogInput::SetAccumulatorDeadband(int32_t deadband)
+{
+ if (StatusIsFatal()) return;
+ int32_t status = 0;
+ setAccumulatorDeadband(m_port, deadband, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+}
+
+/**
+ * Read the accumulated value.
+ *
+ * Read the value that has been accumulating.
+ * The accumulator is attached after the oversample and average engine.
+ *
+ * @return The 64-bit value accumulated since the last Reset().
+ */
+int64_t AnalogInput::GetAccumulatorValue()
+{
+ if (StatusIsFatal()) return 0;
+ int32_t status = 0;
+ int64_t value = getAccumulatorValue(m_port, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ return value + m_accumulatorOffset;
+}
+
+/**
+ * Read the number of accumulated values.
+ *
+ * Read the count of the accumulated values since the accumulator was last Reset().
+ *
+ * @return The number of times samples from the channel were accumulated.
+ */
+uint32_t AnalogInput::GetAccumulatorCount()
+{
+ if (StatusIsFatal()) return 0;
+ int32_t status = 0;
+ uint32_t count = getAccumulatorCount(m_port, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ return count;
+}
+
+
+/**
+ * Read the accumulated value and the number of accumulated values atomically.
+ *
+ * This function reads the value and count from the FPGA atomically.
+ * This can be used for averaging.
+ *
+ * @param value Pointer to the 64-bit accumulated output.
+ * @param count Pointer to the number of accumulation cycles.
+ */
+void AnalogInput::GetAccumulatorOutput(int64_t *value, uint32_t *count)
+{
+ if (StatusIsFatal()) return;
+ int32_t status = 0;
+ getAccumulatorOutput(m_port, value, count, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ *value += m_accumulatorOffset;
+}
+
+/**
+ * Set the sample rate per channel for all analog channels.
+ * The maximum rate is 500kS/s divided by the number of channels in use.
+ * This is 62500 samples/s per channel.
+ * @param samplesPerSecond The number of samples per second.
+ */
+void AnalogInput::SetSampleRate(float samplesPerSecond)
+{
+ int32_t status = 0;
+ setAnalogSampleRate(samplesPerSecond, &status);
+ wpi_setGlobalErrorWithContext(status, getHALErrorMessage(status));
+}
+
+/**
+ * Get the current sample rate for all channels
+ *
+ * @return Sample rate.
+ */
+float AnalogInput::GetSampleRate()
+{
+ int32_t status = 0;
+ float sampleRate = getAnalogSampleRate(&status);
+ wpi_setGlobalErrorWithContext(status, getHALErrorMessage(status));
+ return sampleRate;
+}
+
+/**
+ * Get the Average value for the PID Source base object.
+ *
+ * @return The average voltage.
+ */
+double AnalogInput::PIDGet()
+{
+ if (StatusIsFatal()) return 0.0;
+ return GetAverageVoltage();
+}
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/AnalogOutput.cpp b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/AnalogOutput.cpp
new file mode 100644
index 0000000..6cdfaa6
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/AnalogOutput.cpp
@@ -0,0 +1,82 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2014. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+
+#include "AnalogOutput.h"
+#include "Resource.h"
+#include "WPIErrors.h"
+
+static Resource *outputs = NULL;
+
+void AnalogOutput::InitAnalogOutput(uint32_t channel) {
+ Resource::CreateResourceObject(&outputs, kAnalogOutputs);
+
+ char buf[64];
+
+ if(!checkAnalogOutputChannel(channel))
+ {
+ snprintf(buf, 64, "analog input %d", channel);
+ wpi_setWPIErrorWithContext(ChannelIndexOutOfRange, buf);
+ return;
+ }
+
+ if(outputs->Allocate(channel, buf) == ~0ul)
+ {
+ CloneError(outputs);
+ return;
+ }
+
+ m_channel = channel;
+
+ void* port = getPort(m_channel);
+ int32_t status = 0;
+ m_port = initializeAnalogOutputPort(port, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+
+ HALReport(HALUsageReporting::kResourceType_AnalogChannel, m_channel, 0);
+}
+
+/**
+ * Construct an analog output on the given channel.
+ * All analog outputs are located on the MXP port.
+ * @param The channel number on the roboRIO to represent.
+ */
+AnalogOutput::AnalogOutput(uint32_t channel) {
+ InitAnalogOutput(channel);
+}
+
+/**
+ * Destructor. Frees analog output resource
+ */
+AnalogOutput::~AnalogOutput() {
+ outputs->Free(m_channel);
+}
+
+/**
+ * Set the value of the analog output
+ *
+ * @param voltage The output value in Volts, from 0.0 to +5.0
+ */
+void AnalogOutput::SetVoltage(float voltage) {
+ int32_t status = 0;
+ setAnalogOutput(m_port, voltage, &status);
+
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+}
+
+/**
+ * Get the voltage of the analog output
+ *
+ * @return The value in Volts, from 0.0 to +5.0
+ */
+float AnalogOutput::GetVoltage() {
+ int32_t status = 0;
+ float voltage = getAnalogOutput(m_port, &status);
+
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+
+ return voltage;
+}
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/AnalogPotentiometer.cpp b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/AnalogPotentiometer.cpp
new file mode 100644
index 0000000..0ba7dbd
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/AnalogPotentiometer.cpp
@@ -0,0 +1,73 @@
+
+#include "AnalogPotentiometer.h"
+#include "ControllerPower.h"
+
+/**
+ * Common initialization code called by all constructors.
+ */
+void AnalogPotentiometer::initPot(AnalogInput *input, double fullRange, double offset) {
+ m_fullRange = fullRange;
+ m_offset = offset;
+ m_analog_input = input;
+}
+
+/**
+ * Construct an Analog Potentiometer object from a channel number.
+ * @param channel The channel number on the roboRIO to represent. 0-3 are on-board 4-7 are on the MXP port.
+ * @param fullRange The angular value (in desired units) representing the full 0-5V range of the input.
+ * @param offset The angular value (in desired units) representing the angular output at 0V.
+ */
+AnalogPotentiometer::AnalogPotentiometer(int channel, double fullRange, double offset) {
+ m_init_analog_input = true;
+ initPot(new AnalogInput(channel), fullRange, offset);
+}
+
+/**
+ * Construct an Analog Potentiometer object from an existing Analog Input pointer.
+ * @param channel The existing Analog Input pointer
+ * @param fullRange The angular value (in desired units) representing the full 0-5V range of the input.
+ * @param offset The angular value (in desired units) representing the angular output at 0V.
+ */
+AnalogPotentiometer::AnalogPotentiometer(AnalogInput *input, double fullRange, double offset) {
+ m_init_analog_input = false;
+ initPot(input, fullRange, offset);
+}
+
+/**
+ * Construct an Analog Potentiometer object from an existing Analog Input reference.
+ * @param channel The existing Analog Input reference
+ * @param fullRange The angular value (in desired units) representing the full 0-5V range of the input.
+ * @param offset The angular value (in desired units) representing the angular output at 0V.
+ */
+AnalogPotentiometer::AnalogPotentiometer(AnalogInput &input, double fullRange, double offset) {
+ m_init_analog_input = false;
+ initPot(&input, fullRange, offset);
+}
+
+/**
+ * Destructor. Releases the Analog Input resource if it was allocated by this object
+ */
+AnalogPotentiometer::~AnalogPotentiometer() {
+ if(m_init_analog_input){
+ delete m_analog_input;
+ m_init_analog_input = false;
+ }
+}
+
+/**
+ * Get the current reading of the potentiometer.
+ *
+ * @return The current position of the potentiometer (in the units used for fullRaneg and offset).
+ */
+double AnalogPotentiometer::Get() {
+ return (m_analog_input->GetVoltage() / ControllerPower::GetVoltage5V()) * m_fullRange + m_offset;
+}
+
+/**
+ * Implement the PIDSource interface.
+ *
+ * @return The current reading.
+ */
+double AnalogPotentiometer::PIDGet() {
+ return Get();
+}
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/AnalogTrigger.cpp b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/AnalogTrigger.cpp
new file mode 100644
index 0000000..61cd014
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/AnalogTrigger.cpp
@@ -0,0 +1,166 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+
+#include "AnalogTrigger.h"
+
+#include "AnalogInput.h"
+//#include "NetworkCommunication/UsageReporting.h"
+#include "Resource.h"
+#include "WPIErrors.h"
+
+/**
+ * Initialize an analog trigger from a channel.
+ */
+void AnalogTrigger::InitTrigger(uint32_t channel)
+{
+ void* port = getPort(channel);
+ int32_t status = 0;
+ uint32_t index = 0;
+ m_trigger = initializeAnalogTrigger(port, &index, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ m_index = index;
+
+ HALReport(HALUsageReporting::kResourceType_AnalogTrigger, channel);
+}
+
+/**
+ * Constructor for an analog trigger given a channel number.
+ *
+ * @param channel The channel number on the roboRIO to represent. 0-3 are on-board 4-7 are on the MXP port.
+ */
+AnalogTrigger::AnalogTrigger(int32_t channel)
+{
+ InitTrigger(channel);
+}
+
+/**
+ * Construct an analog trigger given an analog input.
+ * This should be used in the case of sharing an analog channel between the
+ * trigger and an analog input object.
+ * @param channel The pointer to the existing AnalogInput object
+ */
+AnalogTrigger::AnalogTrigger(AnalogInput *input)
+{
+ InitTrigger(input->GetChannel());
+}
+
+AnalogTrigger::~AnalogTrigger()
+{
+ int32_t status = 0;
+ cleanAnalogTrigger(m_trigger, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+}
+
+/**
+ * Set the upper and lower limits of the analog trigger.
+ * The limits are given in ADC codes. If oversampling is used, the units must be scaled
+ * appropriately.
+ * @param lower The lower limit of the trigger in ADC codes (12-bit values).
+ * @param upper The upper limit of the trigger in ADC codes (12-bit values).
+ */
+void AnalogTrigger::SetLimitsRaw(int32_t lower, int32_t upper)
+{
+ if (StatusIsFatal()) return;
+ int32_t status = 0;
+ setAnalogTriggerLimitsRaw(m_trigger, lower, upper, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+}
+
+/**
+ * Set the upper and lower limits of the analog trigger.
+ * The limits are given as floating point voltage values.
+ * @param lower The lower limit of the trigger in Volts.
+ * @param upper The upper limit of the trigger in Volts.
+ */
+void AnalogTrigger::SetLimitsVoltage(float lower, float upper)
+{
+ if (StatusIsFatal()) return;
+ int32_t status = 0;
+ setAnalogTriggerLimitsVoltage(m_trigger, lower, upper, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+}
+
+/**
+ * Configure the analog trigger to use the averaged vs. raw values.
+ * If the value is true, then the averaged value is selected for the analog trigger, otherwise
+ * the immediate value is used.
+ * @param useAveragedValue If true, use the Averaged value, otherwise use the instantaneous reading
+ */
+void AnalogTrigger::SetAveraged(bool useAveragedValue)
+{
+ if (StatusIsFatal()) return;
+ int32_t status = 0;
+ setAnalogTriggerAveraged(m_trigger, useAveragedValue, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+}
+
+/**
+ * Configure the analog trigger to use a filtered value.
+ * The analog trigger will operate with a 3 point average rejection filter. This is designed to
+ * help with 360 degree pot applications for the period where the pot crosses through zero.
+ * @param useFilteredValue If true, use the 3 point rejection filter, otherwise use the unfiltered value
+ */
+void AnalogTrigger::SetFiltered(bool useFilteredValue)
+{
+ if (StatusIsFatal()) return;
+ int32_t status = 0;
+ setAnalogTriggerFiltered(m_trigger, useFilteredValue, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+}
+
+/**
+ * Return the index of the analog trigger.
+ * This is the FPGA index of this analog trigger instance.
+ * @return The index of the analog trigger.
+ */
+uint32_t AnalogTrigger::GetIndex()
+{
+ if (StatusIsFatal()) return ~0ul;
+ return m_index;
+}
+
+/**
+ * Return the InWindow output of the analog trigger.
+ * True if the analog input is between the upper and lower limits.
+ * @return True if the analog input is between the upper and lower limits.
+ */
+bool AnalogTrigger::GetInWindow()
+{
+ if (StatusIsFatal()) return false;
+ int32_t status = 0;
+ bool result = getAnalogTriggerInWindow(m_trigger, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ return result;
+}
+
+/**
+ * Return the TriggerState output of the analog trigger.
+ * True if above upper limit.
+ * False if below lower limit.
+ * If in Hysteresis, maintain previous state.
+ * @return True if above upper limit. False if below lower limit. If in Hysteresis, maintain previous state.
+ */
+bool AnalogTrigger::GetTriggerState()
+{
+ if (StatusIsFatal()) return false;
+ int32_t status = 0;
+ bool result = getAnalogTriggerTriggerState(m_trigger, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ return result;
+}
+
+/**
+ * Creates an AnalogTriggerOutput object.
+ * Gets an output object that can be used for routing.
+ * Caller is responsible for deleting the AnalogTriggerOutput object.
+ * @param type An enum of the type of output object to create.
+ * @return A pointer to a new AnalogTriggerOutput object.
+ */
+AnalogTriggerOutput *AnalogTrigger::CreateOutput(AnalogTriggerType type)
+{
+ if (StatusIsFatal()) return NULL;
+ return new AnalogTriggerOutput(this, type);
+}
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/AnalogTriggerOutput.cpp b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/AnalogTriggerOutput.cpp
new file mode 100644
index 0000000..65febfa
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/AnalogTriggerOutput.cpp
@@ -0,0 +1,74 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+
+#include "AnalogTriggerOutput.h"
+#include "AnalogTrigger.h"
+//#include "NetworkCommunication/UsageReporting.h"
+#include "WPIErrors.h"
+
+/**
+ * Create an object that represents one of the four outputs from an analog trigger.
+ *
+ * Because this class derives from DigitalSource, it can be passed into routing functions
+ * for Counter, Encoder, etc.
+ *
+ * @param trigger A pointer to the trigger for which this is an output.
+ * @param outputType An enum that specifies the output on the trigger to represent.
+ */
+AnalogTriggerOutput::AnalogTriggerOutput(AnalogTrigger *trigger, AnalogTriggerType outputType)
+ : m_trigger (trigger)
+ , m_outputType (outputType)
+{
+ HALReport(HALUsageReporting::kResourceType_AnalogTriggerOutput, trigger->GetIndex(), outputType);
+}
+
+AnalogTriggerOutput::~AnalogTriggerOutput()
+{
+ if (m_interrupt != NULL)
+ {
+ int32_t status = 0;
+ cleanInterrupts(m_interrupt, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ m_interrupt = NULL;
+ m_interrupts->Free(m_interruptIndex);
+ }
+}
+
+/**
+ * Get the state of the analog trigger output.
+ * @return The state of the analog trigger output.
+ */
+bool AnalogTriggerOutput::Get()
+{
+ int32_t status = 0;
+ bool result = getAnalogTriggerOutput(m_trigger->m_trigger, m_outputType, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ return result;
+}
+
+/**
+ * @return The value to be written to the channel field of a routing mux.
+ */
+uint32_t AnalogTriggerOutput::GetChannelForRouting()
+{
+ return (m_trigger->m_index << 2) + m_outputType;
+}
+
+/**
+ * @return The value to be written to the module field of a routing mux.
+ */
+uint32_t AnalogTriggerOutput::GetModuleForRouting()
+{
+ return 0;
+}
+
+/**
+ * @return The value to be written to the module field of a routing mux.
+ */
+bool AnalogTriggerOutput::GetAnalogTriggerForRouting()
+{
+ return true;
+}
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/BuiltInAccelerometer.cpp b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/BuiltInAccelerometer.cpp
new file mode 100644
index 0000000..57f7b57
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/BuiltInAccelerometer.cpp
@@ -0,0 +1,61 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2014. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+
+#include "BuiltInAccelerometer.h"
+#include "HAL/HAL.hpp"
+#include "WPIErrors.h"
+
+/**
+ * Constructor.
+ * @param range The range the accelerometer will measure
+ */
+BuiltInAccelerometer::BuiltInAccelerometer(Range range)
+{
+ SetRange(range);
+
+ HALReport(HALUsageReporting::kResourceType_Accelerometer, 0, 0, "Built-in accelerometer");
+}
+
+BuiltInAccelerometer::~BuiltInAccelerometer()
+{
+}
+
+/** {@inheritdoc} */
+void BuiltInAccelerometer::SetRange(Range range)
+{
+ if(range == kRange_16G)
+ {
+ wpi_setWPIErrorWithContext(ParameterOutOfRange, "16G range not supported (use k2G, k4G, or k8G)");
+ }
+
+ setAccelerometerActive(false);
+ setAccelerometerRange((AccelerometerRange)range);
+ setAccelerometerActive(true);
+}
+
+/**
+ * @return The acceleration of the RoboRIO along the X axis in g-forces
+ */
+double BuiltInAccelerometer::GetX()
+{
+ return getAccelerometerX();
+}
+
+/**
+ * @return The acceleration of the RoboRIO along the Y axis in g-forces
+ */
+double BuiltInAccelerometer::GetY()
+{
+ return getAccelerometerY();
+}
+
+/**
+ * @return The acceleration of the RoboRIO along the Z axis in g-forces
+ */
+double BuiltInAccelerometer::GetZ()
+{
+ return getAccelerometerZ();
+}
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/CANJaguar.cpp b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/CANJaguar.cpp
new file mode 100644
index 0000000..909fec5
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/CANJaguar.cpp
@@ -0,0 +1,2061 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2009. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+
+#include "CANJaguar.h"
+#include "Timer.h"
+#define tNIRIO_i32 int
+#include "NetworkCommunication/CANSessionMux.h"
+#include "CAN/can_proto.h"
+//#include "NetworkCommunication/UsageReporting.h"
+#include "WPIErrors.h"
+#include <cstdio>
+#include <cassert>
+
+/* we are on ARM-LE now, not Freescale so no need to swap */
+#define swap16(x) (x)
+#define swap32(x) (x)
+
+/* Compare floats for equality as fixed point numbers */
+#define FXP8_EQ(a,b) ((int16_t)((a)*256.0)==(int16_t)((b)*256.0))
+#define FXP16_EQ(a,b) ((int32_t)((a)*65536.0)==(int32_t)((b)*65536.0))
+
+const int32_t CANJaguar::kControllerRate;
+constexpr double CANJaguar::kApproxBusVoltage;
+
+static const int32_t kSendMessagePeriod = 20;
+static const uint32_t kFullMessageIDMask = (CAN_MSGID_API_M | CAN_MSGID_MFR_M | CAN_MSGID_DTYPE_M);
+
+static const int32_t kReceiveStatusAttempts = 50;
+
+static Resource *allocated = NULL;
+
+static int32_t sendMessageHelper(uint32_t messageID, const uint8_t *data, uint8_t dataSize, int32_t period)
+{
+ static const uint32_t kTrustedMessages[] = {
+ LM_API_VOLT_T_EN, LM_API_VOLT_T_SET, LM_API_SPD_T_EN, LM_API_SPD_T_SET,
+ LM_API_VCOMP_T_EN, LM_API_VCOMP_T_SET, LM_API_POS_T_EN, LM_API_POS_T_SET,
+ LM_API_ICTRL_T_EN, LM_API_ICTRL_T_SET};
+
+ int32_t status=0;
+
+ for (uint8_t i=0; i<(sizeof(kTrustedMessages)/sizeof(kTrustedMessages[0])); i++)
+ {
+ if ((kFullMessageIDMask & messageID) == kTrustedMessages[i])
+ {
+ uint8_t dataBuffer[8];
+ dataBuffer[0] = 0;
+ dataBuffer[1] = 0;
+
+ // Make sure the data will still fit after adjusting for the token.
+ assert(dataSize <= 6);
+
+ for (uint8_t j=0; j < dataSize; j++)
+ {
+ dataBuffer[j + 2] = data[j];
+ }
+
+ FRC_NetworkCommunication_CANSessionMux_sendMessage(messageID, dataBuffer, dataSize + 2, period, &status);
+
+ return status;
+ }
+ }
+
+ FRC_NetworkCommunication_CANSessionMux_sendMessage(messageID, data, dataSize, period, &status);
+
+ return status;
+}
+
+/**
+ * Common initialization code called by all constructors.
+ */
+void CANJaguar::InitCANJaguar()
+{
+ m_safetyHelper = new MotorSafetyHelper(this);
+
+ m_value = 0.0f;
+ m_speedReference = LM_REF_NONE;
+ m_positionReference = LM_REF_NONE;
+ m_p = 0.0;
+ m_i = 0.0;
+ m_d = 0.0;
+ m_busVoltage = 0.0f;
+ m_outputVoltage = 0.0f;
+ m_outputCurrent = 0.0f;
+ m_temperature = 0.0f;
+ m_position = 0.0;
+ m_speed = 0.0;
+ m_limits = 0x00;
+ m_faults = 0x0000;
+ m_firmwareVersion = 0;
+ m_hardwareVersion = 0;
+ m_neutralMode = kNeutralMode_Jumper;
+ m_encoderCodesPerRev = 0;
+ m_potentiometerTurns = 0;
+ m_limitMode = kLimitMode_SwitchInputsOnly;
+ m_forwardLimit = 0.0;
+ m_reverseLimit = 0.0;
+ m_maxOutputVoltage = 30.0;
+ m_voltageRampRate = 0.0;
+ m_faultTime = 0.0f;
+
+ // Parameters only need to be verified if they are set
+ m_controlModeVerified = false; // Needs to be verified because it's set in the constructor
+ m_speedRefVerified = true;
+ m_posRefVerified = true;
+ m_pVerified = true;
+ m_iVerified = true;
+ m_dVerified = true;
+ m_neutralModeVerified = true;
+ m_encoderCodesPerRevVerified = true;
+ m_potentiometerTurnsVerified = true;
+ m_limitModeVerified = true;
+ m_forwardLimitVerified = true;
+ m_reverseLimitVerified = true;
+ m_maxOutputVoltageVerified = true;
+ m_voltageRampRateVerified = true;
+ m_faultTimeVerified = true;
+
+ m_receivedStatusMessage0 = false;
+ m_receivedStatusMessage1 = false;
+ m_receivedStatusMessage2 = false;
+
+ bool receivedFirmwareVersion = false;
+ uint8_t dataBuffer[8];
+ uint8_t dataSize;
+
+ // Request firmware and hardware version only once
+ requestMessage(CAN_IS_FRAME_REMOTE | CAN_MSGID_API_FIRMVER);
+ requestMessage(LM_API_HWVER);
+
+ // Wait until we've gotten all of the status data at least once.
+ for(int i = 0; i < kReceiveStatusAttempts; i++)
+ {
+ Wait(0.001);
+
+ setupPeriodicStatus();
+ updatePeriodicStatus();
+
+ if(!receivedFirmwareVersion &&
+ getMessage(CAN_MSGID_API_FIRMVER, CAN_MSGID_FULL_M, dataBuffer, &dataSize))
+ {
+ m_firmwareVersion = unpackint32_t(dataBuffer);
+ receivedFirmwareVersion = true;
+ }
+
+ if(m_receivedStatusMessage0 &&
+ m_receivedStatusMessage1 &&
+ m_receivedStatusMessage2 &&
+ receivedFirmwareVersion)
+ {
+ break;
+ }
+ }
+
+ if(!m_receivedStatusMessage0 ||
+ !m_receivedStatusMessage1 ||
+ !m_receivedStatusMessage2 ||
+ !receivedFirmwareVersion)
+ {
+ wpi_setWPIErrorWithContext(JaguarMessageNotFound, "Status data not found");
+ }
+
+ if(getMessage(LM_API_HWVER, CAN_MSGID_FULL_M, dataBuffer, &dataSize))
+ m_hardwareVersion = dataBuffer[0];
+
+ if (m_deviceNumber < 1 || m_deviceNumber > 63)
+ {
+ char buf[256];
+ snprintf(buf, 256, "device number \"%d\" must be between 1 and 63", m_deviceNumber);
+ wpi_setWPIErrorWithContext(ParameterOutOfRange, buf);
+ return;
+ }
+
+ if (StatusIsFatal())
+ return;
+
+ // 3330 was the first shipping RDK firmware version for the Jaguar
+ if (m_firmwareVersion >= 3330 || m_firmwareVersion < 108)
+ {
+ char buf[256];
+ if (m_firmwareVersion < 3330)
+ {
+ snprintf(buf, 256, "Jag #%d firmware (%d) is too old (must be at least version 108 of the FIRST approved firmware)", m_deviceNumber, m_firmwareVersion);
+ }
+ else
+ {
+ snprintf(buf, 256, "Jag #%d firmware (%d) is not FIRST approved (must be at least version 108 of the FIRST approved firmware)", m_deviceNumber, m_firmwareVersion);
+ }
+ wpi_setWPIErrorWithContext(JaguarVersionError, buf);
+ return;
+ }
+
+ switch (m_controlMode)
+ {
+ case kPercentVbus:
+ case kVoltage:
+ // No additional configuration required... start enabled.
+ EnableControl();
+ break;
+ default:
+ break;
+ }
+
+ HALReport(HALUsageReporting::kResourceType_CANJaguar, m_deviceNumber, m_controlMode);
+}
+
+/**
+ * Constructor for the CANJaguar device.<br>
+ * By default the device is configured in Percent mode.
+ * The control mode can be changed by calling one of the control modes listed below.
+ *
+ * @param deviceNumber The address of the Jaguar on the CAN bus.
+ * @see CANJaguar#SetCurrentMode(double, double, double)
+ * @see CANJaguar#SetCurrentMode(PotentiometerTag, double, double, double)
+ * @see CANJaguar#SetCurrentMode(EncoderTag, int, double, double, double)
+ * @see CANJaguar#SetCurrentMode(QuadEncoderTag, int, double, double, double)
+ * @see CANJaguar#SetPercentMode()
+ * @see CANJaguar#SetPercentMode(PotentiometerTag)
+ * @see CANJaguar#SetPercentMode(EncoderTag, int)
+ * @see CANJaguar#SetPercentMode(QuadEncoderTag, int)
+ * @see CANJaguar#SetPositionMode(PotentiometerTag, double, double, double)
+ * @see CANJaguar#SetPositionMode(QuadEncoderTag, int, double, double, double)
+ * @see CANJaguar#SetSpeedMode(EncoderTag, int, double, double, double)
+ * @see CANJaguar#SetSpeedMode(QuadEncoderTag, int, double, double, double)
+ * @see CANJaguar#SetVoltageMode()
+ * @see CANJaguar#SetVoltageMode(PotentiometerTag)
+ * @see CANJaguar#SetVoltageMode(EncoderTag, int)
+ * @see CANJaguar#SetVoltageMode(QuadEncoderTag, int)
+ */
+CANJaguar::CANJaguar(uint8_t deviceNumber)
+ : m_deviceNumber (deviceNumber)
+ , m_maxOutputVoltage (kApproxBusVoltage)
+ , m_safetyHelper (NULL)
+{
+ char buf[64];
+ snprintf(buf, 64, "CANJaguar device number %d", m_deviceNumber);
+ Resource::CreateResourceObject(&allocated, 63);
+
+ if (allocated->Allocate(m_deviceNumber-1, buf) == ~0ul)
+ {
+ CloneError(allocated);
+ return;
+ }
+
+ SetPercentMode();
+ InitCANJaguar();
+ ConfigMaxOutputVoltage(kApproxBusVoltage);
+}
+
+CANJaguar::~CANJaguar()
+{
+ allocated->Free(m_deviceNumber-1);
+
+ int32_t status;
+
+ // Disable periodic setpoints
+ if(m_controlMode == kPercentVbus)
+ FRC_NetworkCommunication_CANSessionMux_sendMessage(m_deviceNumber | LM_API_VOLT_T_SET, NULL, 0, CAN_SEND_PERIOD_STOP_REPEATING, &status);
+ else if(m_controlMode == kSpeed)
+ FRC_NetworkCommunication_CANSessionMux_sendMessage(m_deviceNumber | LM_API_SPD_T_SET, NULL, 0, CAN_SEND_PERIOD_STOP_REPEATING, &status);
+ else if(m_controlMode == kPosition)
+ FRC_NetworkCommunication_CANSessionMux_sendMessage(m_deviceNumber | LM_API_POS_T_SET, NULL, 0, CAN_SEND_PERIOD_STOP_REPEATING, &status);
+ else if(m_controlMode == kCurrent)
+ FRC_NetworkCommunication_CANSessionMux_sendMessage(m_deviceNumber | LM_API_ICTRL_T_SET, NULL, 0, CAN_SEND_PERIOD_STOP_REPEATING, &status);
+ else if(m_controlMode == kVoltage)
+ FRC_NetworkCommunication_CANSessionMux_sendMessage(m_deviceNumber | LM_API_VCOMP_T_SET, NULL, 0, CAN_SEND_PERIOD_STOP_REPEATING, &status);
+
+ delete m_safetyHelper;
+ m_safetyHelper = NULL;
+}
+
+/**
+ * @return The CAN ID passed in the constructor
+ */
+uint8_t CANJaguar::getDeviceNumber() const
+{
+ return m_deviceNumber;
+}
+
+/**
+ * Sets the output set-point value.
+ *
+ * The scale and the units depend on the mode the Jaguar is in.<br>
+ * In percentVbus Mode, the outputValue is from -1.0 to 1.0 (same as PWM Jaguar).<br>
+ * In voltage Mode, the outputValue is in volts. <br>
+ * In current Mode, the outputValue is in amps. <br>
+ * In speed Mode, the outputValue is in rotations/minute.<br>
+ * In position Mode, the outputValue is in rotations.
+ *
+ * @param outputValue The set-point to sent to the motor controller.
+ * @param syncGroup The update group to add this Set() to, pending UpdateSyncGroup(). If 0, update immediately.
+ */
+void CANJaguar::Set(float outputValue, uint8_t syncGroup)
+{
+ uint32_t messageID;
+ uint8_t dataBuffer[8];
+ uint8_t dataSize;
+
+ if(m_safetyHelper && !m_safetyHelper->IsAlive() && m_controlEnabled)
+ {
+ EnableControl();
+ }
+
+ if(m_controlEnabled)
+ {
+ switch(m_controlMode)
+ {
+ case kPercentVbus:
+ {
+ messageID = LM_API_VOLT_T_SET;
+ if (outputValue > 1.0) outputValue = 1.0;
+ if (outputValue < -1.0) outputValue = -1.0;
+ dataSize = packPercentage(dataBuffer, outputValue);
+ }
+ break;
+ case kSpeed:
+ {
+ messageID = LM_API_SPD_T_SET;
+ dataSize = packFXP16_16(dataBuffer, outputValue);
+ }
+ break;
+ case kPosition:
+ {
+ messageID = LM_API_POS_T_SET;
+ dataSize = packFXP16_16(dataBuffer, outputValue);
+ }
+ break;
+ case kCurrent:
+ {
+ messageID = LM_API_ICTRL_T_SET;
+ dataSize = packFXP8_8(dataBuffer, outputValue);
+ }
+ break;
+ case kVoltage:
+ {
+ messageID = LM_API_VCOMP_T_SET;
+ dataSize = packFXP8_8(dataBuffer, outputValue);
+ }
+ break;
+ default:
+ wpi_setWPIErrorWithContext(IncompatibleMode, "The Jaguar only supports Current, Voltage, Position, Speed, and Percent (Throttle) modes.");
+ return;
+ }
+ if (syncGroup != 0)
+ {
+ dataBuffer[dataSize] = syncGroup;
+ dataSize++;
+ }
+
+ sendMessage(messageID, dataBuffer, dataSize, kSendMessagePeriod);
+
+ if (m_safetyHelper) m_safetyHelper->Feed();
+ }
+
+ m_value = outputValue;
+
+ verify();
+}
+
+/**
+ * Get the recently set outputValue setpoint.
+ *
+ * The scale and the units depend on the mode the Jaguar is in.<br>
+ * In percentVbus Mode, the outputValue is from -1.0 to 1.0 (same as PWM Jaguar).<br>
+ * In voltage Mode, the outputValue is in volts.<br>
+ * In current Mode, the outputValue is in amps.<br>
+ * In speed Mode, the outputValue is in rotations/minute.<br>
+ * In position Mode, the outputValue is in rotations.<br>
+ *
+ * @return The most recently set outputValue setpoint.
+ */
+float CANJaguar::Get()
+{
+ return m_value;
+}
+
+/**
+* Common interface for disabling a motor.
+*
+* @deprecated Call {@link #DisableControl()} instead.
+*/
+void CANJaguar::Disable()
+{
+ DisableControl();
+}
+
+/**
+ * Write out the PID value as seen in the PIDOutput base object.
+ *
+ * @deprecated Call Set instead.
+ *
+ * @param output Write out the PercentVbus value as was computed by the PIDController
+ */
+void CANJaguar::PIDWrite(float output)
+{
+ if (m_controlMode == kPercentVbus)
+ {
+ Set(output);
+ }
+ else
+ {
+ wpi_setWPIErrorWithContext(IncompatibleMode, "PID only supported in PercentVbus mode");
+ }
+}
+
+uint8_t CANJaguar::packPercentage(uint8_t *buffer, double value)
+{
+ int16_t intValue = (int16_t)(value * 32767.0);
+ *((int16_t*)buffer) = swap16(intValue);
+ return sizeof(int16_t);
+}
+
+uint8_t CANJaguar::packFXP8_8(uint8_t *buffer, double value)
+{
+ int16_t intValue = (int16_t)(value * 256.0);
+ *((int16_t*)buffer) = swap16(intValue);
+ return sizeof(int16_t);
+}
+
+uint8_t CANJaguar::packFXP16_16(uint8_t *buffer, double value)
+{
+ int32_t intValue = (int32_t)(value * 65536.0);
+ *((int32_t*)buffer) = swap32(intValue);
+ return sizeof(int32_t);
+}
+
+uint8_t CANJaguar::packint16_t(uint8_t *buffer, int16_t value)
+{
+ *((int16_t*)buffer) = swap16(value);
+ return sizeof(int16_t);
+}
+
+uint8_t CANJaguar::packint32_t(uint8_t *buffer, int32_t value)
+{
+ *((int32_t*)buffer) = swap32(value);
+ return sizeof(int32_t);
+}
+
+double CANJaguar::unpackPercentage(uint8_t *buffer)
+{
+ int16_t value = *((int16_t*)buffer);
+ value = swap16(value);
+ return value / 32767.0;
+}
+
+double CANJaguar::unpackFXP8_8(uint8_t *buffer)
+{
+ int16_t value = *((int16_t*)buffer);
+ value = swap16(value);
+ return value / 256.0;
+}
+
+double CANJaguar::unpackFXP16_16(uint8_t *buffer)
+{
+ int32_t value = *((int32_t*)buffer);
+ value = swap32(value);
+ return value / 65536.0;
+}
+
+int16_t CANJaguar::unpackint16_t(uint8_t *buffer)
+{
+ int16_t value = *((int16_t*)buffer);
+ return swap16(value);
+}
+
+int32_t CANJaguar::unpackint32_t(uint8_t *buffer)
+{
+ int32_t value = *((int32_t*)buffer);
+ return swap32(value);
+}
+
+/**
+ * Send a message to the Jaguar.
+ *
+ * @param messageID The messageID to be used on the CAN bus (device number is added internally)
+ * @param data The up to 8 bytes of data to be sent with the message
+ * @param dataSize Specify how much of the data in "data" to send
+ * @param periodic If positive, tell Network Communications to send the message
+ * every "period" milliseconds.
+ */
+void CANJaguar::sendMessage(uint32_t messageID, const uint8_t *data, uint8_t dataSize, int32_t period)
+{
+ int32_t localStatus = sendMessageHelper(messageID | m_deviceNumber, data, dataSize, period);
+
+ if(localStatus < 0)
+ {
+ wpi_setErrorWithContext(localStatus, "sendMessage");
+ }
+}
+
+/**
+ * Request a message from the Jaguar, but don't wait for it to arrive.
+ *
+ * @param messageID The message to request
+ * @param periodic If positive, tell Network Communications to send the message
+ * every "period" milliseconds.
+ */
+void CANJaguar::requestMessage(uint32_t messageID, int32_t period)
+{
+ sendMessageHelper(messageID | m_deviceNumber, NULL, 0, period);
+}
+
+/**
+ * Get a previously requested message.
+ *
+ * Jaguar always generates a message with the same message ID when replying.
+ *
+ * @param messageID The messageID to read from the CAN bus (device number is added internally)
+ * @param data The up to 8 bytes of data that was received with the message
+ * @param dataSize Indicates how much data was received
+ *
+ * @return true if the message was found. Otherwise, no new message is available.
+ */
+bool CANJaguar::getMessage(uint32_t messageID, uint32_t messageMask, uint8_t *data, uint8_t *dataSize)
+{
+ uint32_t targetedMessageID = messageID | m_deviceNumber;
+ int32_t status = 0;
+ uint32_t timeStamp;
+
+ // Caller may have set bit31 for remote frame transmission so clear invalid bits[31-29]
+ targetedMessageID &= CAN_MSGID_FULL_M;
+
+ // Get the data.
+ FRC_NetworkCommunication_CANSessionMux_receiveMessage(&targetedMessageID, messageMask, data, dataSize, &timeStamp, &status);
+
+ // Do we already have the most recent value?
+ if(status == ERR_CANSessionMux_MessageNotFound)
+ return false;
+ else
+ wpi_setErrorWithContext(status, "receiveMessage");
+
+ return true;
+}
+
+/**
+ * Enables periodic status updates from the Jaguar.
+ */
+void CANJaguar::setupPeriodicStatus() {
+ uint8_t data[8];
+ uint8_t dataSize;
+
+ // Message 0 returns bus voltage, output voltage, output current, and
+ // temperature.
+ static const uint8_t kMessage0Data[] = {
+ LM_PSTAT_VOLTBUS_B0, LM_PSTAT_VOLTBUS_B1,
+ LM_PSTAT_VOLTOUT_B0, LM_PSTAT_VOLTOUT_B1,
+ LM_PSTAT_CURRENT_B0, LM_PSTAT_CURRENT_B1,
+ LM_PSTAT_TEMP_B0, LM_PSTAT_TEMP_B1
+ };
+
+ // Message 1 returns position and speed
+ static const uint8_t kMessage1Data[] = {
+ LM_PSTAT_POS_B0, LM_PSTAT_POS_B1, LM_PSTAT_POS_B2, LM_PSTAT_POS_B3,
+ LM_PSTAT_SPD_B0, LM_PSTAT_SPD_B1, LM_PSTAT_SPD_B2, LM_PSTAT_SPD_B3
+ };
+
+ // Message 2 returns limits and faults
+ static const uint8_t kMessage2Data[] = {
+ LM_PSTAT_LIMIT_CLR,
+ LM_PSTAT_FAULT,
+ LM_PSTAT_END
+ };
+
+ dataSize = packint16_t(data, kSendMessagePeriod);
+ sendMessage(LM_API_PSTAT_PER_EN_S0, data, dataSize);
+ sendMessage(LM_API_PSTAT_PER_EN_S1, data, dataSize);
+ sendMessage(LM_API_PSTAT_PER_EN_S2, data, dataSize);
+
+ dataSize = 8;
+ sendMessage(LM_API_PSTAT_CFG_S0, kMessage0Data, dataSize);
+ sendMessage(LM_API_PSTAT_CFG_S1, kMessage1Data, dataSize);
+ sendMessage(LM_API_PSTAT_CFG_S2, kMessage2Data, dataSize);
+}
+
+/**
+ * Check for new periodic status updates and unpack them into local variables
+ */
+void CANJaguar::updatePeriodicStatus() {
+ uint8_t data[8];
+ uint8_t dataSize;
+
+ // Check if a new bus voltage/output voltage/current/temperature message
+ // has arrived and unpack the values into the cached member variables
+ if(getMessage(LM_API_PSTAT_DATA_S0, CAN_MSGID_FULL_M, data, &dataSize)) {
+ m_busVoltage = unpackFXP8_8(data);
+ m_outputVoltage = unpackPercentage(data + 2) * m_busVoltage;
+ m_outputCurrent = unpackFXP8_8(data + 4);
+ m_temperature = unpackFXP8_8(data + 6);
+
+ m_receivedStatusMessage0 = true;
+ }
+
+ // Check if a new position/speed message has arrived and do the same
+ if(getMessage(LM_API_PSTAT_DATA_S1, CAN_MSGID_FULL_M, data, &dataSize)) {
+ m_position = unpackFXP16_16(data);
+ m_speed = unpackFXP16_16(data + 4);
+
+ m_receivedStatusMessage1 = true;
+ }
+
+ // Check if a new limits/faults message has arrived and do the same
+ if(getMessage(LM_API_PSTAT_DATA_S2, CAN_MSGID_FULL_M, data, &dataSize)) {
+ m_limits = data[0];
+ m_faults = data[1];
+
+ m_receivedStatusMessage2 = true;
+ }
+}
+
+/**
+ * Check all unverified params and make sure they're equal to their local
+ * cached versions. If a value isn't available, it gets requested. If a value
+ * doesn't match up, it gets set again.
+ */
+void CANJaguar::verify()
+{
+ uint8_t dataBuffer[8];
+ uint8_t dataSize;
+
+ // If the Jaguar lost power, everything should be considered unverified.
+ if(getMessage(LM_API_STATUS_POWER, CAN_MSGID_FULL_M, dataBuffer, &dataSize))
+ {
+ bool powerCycled = (bool)dataBuffer[0];
+
+ if(powerCycled)
+ {
+ // Clear the power cycled bit
+ dataBuffer[0] = 1;
+ sendMessage(LM_API_STATUS_POWER, dataBuffer, sizeof(uint8_t));
+
+ // Mark everything as unverified
+ m_controlModeVerified = false;
+ m_speedRefVerified = false;
+ m_posRefVerified = false;
+ m_neutralModeVerified = false;
+ m_encoderCodesPerRevVerified = false;
+ m_potentiometerTurnsVerified = false;
+ m_forwardLimitVerified = false;
+ m_reverseLimitVerified = false;
+ m_limitModeVerified = false;
+ m_maxOutputVoltageVerified = false;
+ m_faultTimeVerified = false;
+
+ if(m_controlMode == kPercentVbus || m_controlMode == kVoltage)
+ {
+ m_voltageRampRateVerified = false;
+ }
+ else
+ {
+ m_pVerified = false;
+ m_iVerified = false;
+ m_dVerified = false;
+ }
+
+ // Verify periodic status messages again
+ m_receivedStatusMessage0 = false;
+ m_receivedStatusMessage1 = false;
+ m_receivedStatusMessage2 = false;
+
+ // Remove any old values from netcomms. Otherwise, parameters are
+ // incorrectly marked as verified based on stale messages.
+ getMessage(LM_API_SPD_REF, CAN_MSGID_FULL_M, dataBuffer, &dataSize);
+ getMessage(LM_API_POS_REF, CAN_MSGID_FULL_M, dataBuffer, &dataSize);
+ getMessage(LM_API_SPD_PC, CAN_MSGID_FULL_M, dataBuffer, &dataSize);
+ getMessage(LM_API_POS_PC, CAN_MSGID_FULL_M, dataBuffer, &dataSize);
+ getMessage(LM_API_ICTRL_PC, CAN_MSGID_FULL_M, dataBuffer, &dataSize);
+ getMessage(LM_API_SPD_IC, CAN_MSGID_FULL_M, dataBuffer, &dataSize);
+ getMessage(LM_API_POS_IC, CAN_MSGID_FULL_M, dataBuffer, &dataSize);
+ getMessage(LM_API_ICTRL_IC, CAN_MSGID_FULL_M, dataBuffer, &dataSize);
+ getMessage(LM_API_SPD_DC, CAN_MSGID_FULL_M, dataBuffer, &dataSize);
+ getMessage(LM_API_POS_DC, CAN_MSGID_FULL_M, dataBuffer, &dataSize);
+ getMessage(LM_API_ICTRL_DC, CAN_MSGID_FULL_M, dataBuffer, &dataSize);
+ getMessage(LM_API_CFG_BRAKE_COAST, CAN_MSGID_FULL_M, dataBuffer, &dataSize);
+ getMessage(LM_API_CFG_ENC_LINES, CAN_MSGID_FULL_M, dataBuffer, &dataSize);
+ getMessage(LM_API_CFG_POT_TURNS, CAN_MSGID_FULL_M, dataBuffer, &dataSize);
+ getMessage(LM_API_CFG_LIMIT_MODE, CAN_MSGID_FULL_M, dataBuffer, &dataSize);
+ getMessage(LM_API_CFG_LIMIT_FWD, CAN_MSGID_FULL_M, dataBuffer, &dataSize);
+ getMessage(LM_API_CFG_LIMIT_REV, CAN_MSGID_FULL_M, dataBuffer, &dataSize);
+ getMessage(LM_API_CFG_MAX_VOUT, CAN_MSGID_FULL_M, dataBuffer, &dataSize);
+ getMessage(LM_API_VOLT_SET_RAMP, CAN_MSGID_FULL_M, dataBuffer, &dataSize);
+ getMessage(LM_API_VCOMP_COMP_RAMP, CAN_MSGID_FULL_M, dataBuffer, &dataSize);
+ getMessage(LM_API_CFG_FAULT_TIME, CAN_MSGID_FULL_M, dataBuffer, &dataSize);
+ }
+ }
+ else
+ {
+ requestMessage(LM_API_STATUS_POWER);
+ }
+
+ // Verify that any recently set parameters are correct
+ if(!m_controlModeVerified && m_controlEnabled)
+ {
+ if(getMessage(LM_API_STATUS_CMODE, CAN_MSGID_FULL_M, dataBuffer, &dataSize))
+ {
+ ControlMode mode = (ControlMode)dataBuffer[0];
+
+ if(m_controlMode == mode)
+ m_controlModeVerified = true;
+ else
+ // Enable control again to resend the control mode
+ EnableControl();
+ }
+ else
+ {
+ // Verification is needed but not available - request it again.
+ requestMessage(LM_API_STATUS_CMODE);
+ }
+ }
+
+ if(!m_speedRefVerified)
+ {
+ if(getMessage(LM_API_SPD_REF, CAN_MSGID_FULL_M, dataBuffer, &dataSize))
+ {
+ uint8_t speedRef = dataBuffer[0];
+
+ if(m_speedReference == speedRef)
+ m_speedRefVerified = true;
+ else
+ // It's wrong - set it again
+ SetSpeedReference(m_speedReference);
+ }
+ else
+ {
+ // Verification is needed but not available - request it again.
+ requestMessage(LM_API_SPD_REF);
+ }
+ }
+
+ if(!m_posRefVerified)
+ {
+ if(getMessage(LM_API_POS_REF, CAN_MSGID_FULL_M, dataBuffer, &dataSize))
+ {
+ uint8_t posRef = dataBuffer[0];
+
+ if(m_positionReference == posRef)
+ m_posRefVerified = true;
+ else
+ // It's wrong - set it again
+ SetPositionReference(m_positionReference);
+ }
+ else
+ {
+ // Verification is needed but not available - request it again.
+ requestMessage(LM_API_POS_REF);
+ }
+ }
+
+ if(!m_pVerified)
+ {
+ uint32_t message;
+
+ if(m_controlMode == kSpeed)
+ message = LM_API_SPD_PC;
+ else if(m_controlMode == kPosition)
+ message = LM_API_POS_PC;
+ else if(m_controlMode == kCurrent)
+ message = LM_API_ICTRL_PC;
+
+ if(getMessage(message, CAN_MSGID_FULL_M, dataBuffer, &dataSize))
+ {
+ double p = unpackFXP16_16(dataBuffer);
+
+ if(FXP16_EQ(m_p, p))
+ m_pVerified = true;
+ else
+ // It's wrong - set it again
+ SetP(m_p);
+ }
+ else
+ {
+ // Verification is needed but not available - request it again.
+ requestMessage(message);
+ }
+ }
+
+ if(!m_iVerified)
+ {
+ uint32_t message;
+
+ if(m_controlMode == kSpeed)
+ message = LM_API_SPD_IC;
+ else if(m_controlMode == kPosition)
+ message = LM_API_POS_IC;
+ else if(m_controlMode == kCurrent)
+ message = LM_API_ICTRL_IC;
+
+ if(getMessage(message, CAN_MSGID_FULL_M, dataBuffer, &dataSize))
+ {
+ double i = unpackFXP16_16(dataBuffer);
+
+ if(FXP16_EQ(m_i, i))
+ m_iVerified = true;
+ else
+ // It's wrong - set it again
+ SetI(m_i);
+ }
+ else
+ {
+ // Verification is needed but not available - request it again.
+ requestMessage(message);
+ }
+ }
+
+ if(!m_dVerified)
+ {
+ uint32_t message;
+
+ if(m_controlMode == kSpeed)
+ message = LM_API_SPD_DC;
+ else if(m_controlMode == kPosition)
+ message = LM_API_POS_DC;
+ else if(m_controlMode == kCurrent)
+ message = LM_API_ICTRL_DC;
+
+ if(getMessage(message, CAN_MSGID_FULL_M, dataBuffer, &dataSize))
+ {
+ double d = unpackFXP16_16(dataBuffer);
+
+ if(FXP16_EQ(m_d, d))
+ m_dVerified = true;
+ else
+ // It's wrong - set it again
+ SetD(m_d);
+ }
+ else
+ {
+ // Verification is needed but not available - request it again.
+ requestMessage(message);
+ }
+ }
+
+ if(!m_neutralModeVerified)
+ {
+ if(getMessage(LM_API_CFG_BRAKE_COAST, CAN_MSGID_FULL_M, dataBuffer, &dataSize))
+ {
+ NeutralMode mode = (NeutralMode)dataBuffer[0];
+
+ if(mode == m_neutralMode)
+ m_neutralModeVerified = true;
+ else
+ // It's wrong - set it again
+ ConfigNeutralMode(m_neutralMode);
+ }
+ else
+ {
+ // Verification is needed but not available - request it again.
+ requestMessage(LM_API_CFG_BRAKE_COAST);
+ }
+ }
+
+ if(!m_encoderCodesPerRevVerified)
+ {
+ if(getMessage(LM_API_CFG_ENC_LINES, CAN_MSGID_FULL_M, dataBuffer, &dataSize))
+ {
+ uint16_t codes = unpackint16_t(dataBuffer);
+
+ if(codes == m_encoderCodesPerRev)
+ m_encoderCodesPerRevVerified = true;
+ else
+ // It's wrong - set it again
+ ConfigEncoderCodesPerRev(m_encoderCodesPerRev);
+ }
+ else
+ {
+ // Verification is needed but not available - request it again.
+ requestMessage(LM_API_CFG_ENC_LINES);
+ }
+ }
+
+ if(!m_potentiometerTurnsVerified)
+ {
+ if(getMessage(LM_API_CFG_POT_TURNS, CAN_MSGID_FULL_M, dataBuffer, &dataSize))
+ {
+ uint16_t turns = unpackint16_t(dataBuffer);
+
+ if(turns == m_potentiometerTurns)
+ m_potentiometerTurnsVerified = true;
+ else
+ // It's wrong - set it again
+ ConfigPotentiometerTurns(m_potentiometerTurns);
+ }
+ else
+ {
+ // Verification is needed but not available - request it again.
+ requestMessage(LM_API_CFG_POT_TURNS);
+ }
+ }
+
+ if(!m_limitModeVerified)
+ {
+ if(getMessage(LM_API_CFG_LIMIT_MODE, CAN_MSGID_FULL_M, dataBuffer, &dataSize))
+ {
+ LimitMode mode = (LimitMode)dataBuffer[0];
+
+ if(mode == m_limitMode)
+ m_limitModeVerified = true;
+ else
+ {
+ // It's wrong - set it again
+ ConfigLimitMode(m_limitMode);
+ }
+ }
+ else
+ {
+ // Verification is needed but not available - request it again.
+ requestMessage(LM_API_CFG_LIMIT_MODE);
+ }
+ }
+
+ if(!m_forwardLimitVerified)
+ {
+ if(getMessage(LM_API_CFG_LIMIT_FWD, CAN_MSGID_FULL_M, dataBuffer, &dataSize))
+ {
+ double limit = unpackFXP16_16(dataBuffer);
+
+ if(FXP16_EQ(limit, m_forwardLimit))
+ m_forwardLimitVerified = true;
+ else
+ {
+ // It's wrong - set it again
+ ConfigForwardLimit(m_forwardLimit);
+ }
+ }
+ else
+ {
+ // Verification is needed but not available - request it again.
+ requestMessage(LM_API_CFG_LIMIT_FWD);
+ }
+ }
+
+ if(!m_reverseLimitVerified)
+ {
+ if(getMessage(LM_API_CFG_LIMIT_REV, CAN_MSGID_FULL_M, dataBuffer, &dataSize))
+ {
+ double limit = unpackFXP16_16(dataBuffer);
+
+ if(FXP16_EQ(limit, m_reverseLimit))
+ m_reverseLimitVerified = true;
+ else
+ {
+ // It's wrong - set it again
+ ConfigReverseLimit(m_reverseLimit);
+ }
+ }
+ else
+ {
+ // Verification is needed but not available - request it again.
+ requestMessage(LM_API_CFG_LIMIT_REV);
+ }
+ }
+
+ if(!m_maxOutputVoltageVerified)
+ {
+ if(getMessage(LM_API_CFG_MAX_VOUT, CAN_MSGID_FULL_M, dataBuffer, &dataSize))
+ {
+ double voltage = unpackFXP8_8(dataBuffer);
+
+ // The returned max output voltage is sometimes slightly higher or
+ // lower than what was sent. This should not trigger resending
+ // the message.
+ if(std::abs(voltage - m_maxOutputVoltage) < 0.1)
+ m_maxOutputVoltageVerified = true;
+ else
+ {
+ // It's wrong - set it again
+ ConfigMaxOutputVoltage(m_maxOutputVoltage);
+ }
+ }
+ else
+ {
+ // Verification is needed but not available - request it again.
+ requestMessage(LM_API_CFG_MAX_VOUT);
+ }
+ }
+
+ if(!m_voltageRampRateVerified)
+ {
+ if(m_controlMode == kPercentVbus)
+ {
+ if(getMessage(LM_API_VOLT_SET_RAMP, CAN_MSGID_FULL_M, dataBuffer, &dataSize))
+ {
+ double rate = unpackPercentage(dataBuffer);
+
+ if(FXP16_EQ(rate, m_voltageRampRate))
+ m_voltageRampRateVerified = true;
+ else
+ {
+ // It's wrong - set it again
+ SetVoltageRampRate(m_voltageRampRate);
+ }
+ }
+ else
+ {
+ // Verification is needed but not available - request it again.
+ requestMessage(LM_API_VOLT_SET_RAMP);
+ }
+ }
+ else if(m_controlMode == kVoltage)
+ {
+ if(getMessage(LM_API_VCOMP_COMP_RAMP, CAN_MSGID_FULL_M, dataBuffer, &dataSize))
+ {
+ double rate = unpackFXP8_8(dataBuffer);
+
+ if(FXP8_EQ(rate, m_voltageRampRate))
+ m_voltageRampRateVerified = true;
+ else
+ {
+ // It's wrong - set it again
+ SetVoltageRampRate(m_voltageRampRate);
+ }
+ }
+ else
+ {
+ // Verification is needed but not available - request it again.
+ requestMessage(LM_API_VCOMP_COMP_RAMP);
+ }
+ }
+ }
+
+ if(!m_faultTimeVerified)
+ {
+ if(getMessage(LM_API_CFG_FAULT_TIME, CAN_MSGID_FULL_M, dataBuffer, &dataSize))
+ {
+ uint16_t faultTime = unpackint16_t(dataBuffer);
+
+ if((uint16_t)(m_faultTime * 1000.0) == faultTime)
+ m_faultTimeVerified = true;
+ else
+ {
+ // It's wrong - set it again
+ ConfigFaultTime(m_faultTime);
+ }
+ }
+ else
+ {
+ // Verification is needed but not available - request it again.
+ requestMessage(LM_API_CFG_FAULT_TIME);
+ }
+ }
+
+ if(!m_receivedStatusMessage0 ||
+ !m_receivedStatusMessage1 ||
+ !m_receivedStatusMessage2)
+ {
+ // If the periodic status messages haven't been verified as received,
+ // request periodic status messages again and attempt to unpack any
+ // available ones.
+ setupPeriodicStatus();
+ GetTemperature();
+ GetPosition();
+ GetFaults();
+ }
+}
+
+/**
+ * Set the reference source device for speed controller mode.
+ *
+ * Choose encoder as the source of speed feedback when in speed control mode.
+ *
+ * @param reference Specify a speed reference.
+ */
+void CANJaguar::SetSpeedReference(uint8_t reference)
+{
+ uint8_t dataBuffer[8];
+
+ // Send the speed reference parameter
+ dataBuffer[0] = reference;
+ sendMessage(LM_API_SPD_REF, dataBuffer, sizeof(uint8_t));
+
+ m_speedReference = reference;
+ m_speedRefVerified = false;
+}
+
+/**
+ * Get the reference source device for speed controller mode.
+ *
+ * @return A speed reference indicating the currently selected reference device
+ * for speed controller mode.
+ */
+uint8_t CANJaguar::GetSpeedReference()
+{
+ return m_speedReference;
+}
+
+/**
+ * Set the reference source device for position controller mode.
+ *
+ * Choose between using and encoder and using a potentiometer
+ * as the source of position feedback when in position control mode.
+ *
+ * @param reference Specify a PositionReference.
+ */
+void CANJaguar::SetPositionReference(uint8_t reference)
+{
+ uint8_t dataBuffer[8];
+
+ // Send the position reference parameter
+ dataBuffer[0] = reference;
+ sendMessage(LM_API_POS_REF, dataBuffer, sizeof(uint8_t));
+
+ m_positionReference = reference;
+ m_posRefVerified = false;
+}
+
+/**
+ * Get the reference source device for position controller mode.
+ *
+ * @return A PositionReference indicating the currently selected reference device for position controller mode.
+ */
+uint8_t CANJaguar::GetPositionReference()
+{
+ return m_positionReference;
+}
+
+/**
+ * Set the P, I, and D constants for the closed loop modes.
+ *
+ * @param p The proportional gain of the Jaguar's PID controller.
+ * @param i The integral gain of the Jaguar's PID controller.
+ * @param d The differential gain of the Jaguar's PID controller.
+ */
+void CANJaguar::SetPID(double p, double i, double d)
+{
+ SetP(p);
+ SetI(i);
+ SetD(d);
+}
+
+/**
+ * Set the P constant for the closed loop modes.
+ *
+ * @param p The proportional gain of the Jaguar's PID controller.
+ */
+void CANJaguar::SetP(double p)
+{
+ uint8_t dataBuffer[8];
+ uint8_t dataSize;
+
+ switch(m_controlMode)
+ {
+ case kPercentVbus:
+ case kVoltage:
+ case kFollower:
+ wpi_setWPIErrorWithContext(IncompatibleMode, "PID constants only apply in Speed, Position, and Current mode");
+ break;
+ case kSpeed:
+ dataSize = packFXP16_16(dataBuffer, p);
+ sendMessage(LM_API_SPD_PC, dataBuffer, dataSize);
+ break;
+ case kPosition:
+ dataSize = packFXP16_16(dataBuffer, p);
+ sendMessage(LM_API_POS_PC, dataBuffer, dataSize);
+ break;
+ case kCurrent:
+ dataSize = packFXP16_16(dataBuffer, p);
+ sendMessage(LM_API_ICTRL_PC, dataBuffer, dataSize);
+ break;
+ }
+
+ m_p = p;
+ m_pVerified = false;
+}
+
+/**
+ * Set the I constant for the closed loop modes.
+ *
+ * @param i The integral gain of the Jaguar's PID controller.
+ */
+void CANJaguar::SetI(double i)
+{
+ uint8_t dataBuffer[8];
+ uint8_t dataSize;
+
+ switch(m_controlMode)
+ {
+ case kPercentVbus:
+ case kVoltage:
+ case kFollower:
+ wpi_setWPIErrorWithContext(IncompatibleMode, "PID constants only apply in Speed, Position, and Current mode");
+ break;
+ case kSpeed:
+ dataSize = packFXP16_16(dataBuffer, i);
+ sendMessage(LM_API_SPD_IC, dataBuffer, dataSize);
+ break;
+ case kPosition:
+ dataSize = packFXP16_16(dataBuffer, i);
+ sendMessage(LM_API_POS_IC, dataBuffer, dataSize);
+ break;
+ case kCurrent:
+ dataSize = packFXP16_16(dataBuffer, i);
+ sendMessage(LM_API_ICTRL_IC, dataBuffer, dataSize);
+ break;
+ }
+
+ m_i = i;
+ m_iVerified = false;
+}
+
+/**
+ * Set the D constant for the closed loop modes.
+ *
+ * @param d The derivative gain of the Jaguar's PID controller.
+ */
+void CANJaguar::SetD(double d)
+{
+ uint8_t dataBuffer[8];
+ uint8_t dataSize;
+
+ switch(m_controlMode)
+ {
+ case kPercentVbus:
+ case kVoltage:
+ case kFollower:
+ wpi_setWPIErrorWithContext(IncompatibleMode, "PID constants only apply in Speed, Position, and Current mode");
+ break;
+ case kSpeed:
+ dataSize = packFXP16_16(dataBuffer, d);
+ sendMessage(LM_API_SPD_DC, dataBuffer, dataSize);
+ break;
+ case kPosition:
+ dataSize = packFXP16_16(dataBuffer, d);
+ sendMessage(LM_API_POS_DC, dataBuffer, dataSize);
+ break;
+ case kCurrent:
+ dataSize = packFXP16_16(dataBuffer, d);
+ sendMessage(LM_API_ICTRL_DC, dataBuffer, dataSize);
+ break;
+ }
+
+ m_d = d;
+ m_dVerified = false;
+}
+
+/**
+ * Get the Proportional gain of the controller.
+ *
+ * @return The proportional gain.
+ */
+double CANJaguar::GetP()
+{
+ if(m_controlMode == kPercentVbus || m_controlMode == kVoltage)
+ {
+ wpi_setWPIErrorWithContext(IncompatibleMode, "PID constants only apply in Speed, Position, and Current mode");
+ return 0.0;
+ }
+
+ return m_p;
+}
+
+/**
+ * Get the Intregral gain of the controller.
+ *
+ * @return The integral gain.
+ */
+double CANJaguar::GetI()
+{
+ if(m_controlMode == kPercentVbus || m_controlMode == kVoltage)
+ {
+ wpi_setWPIErrorWithContext(IncompatibleMode, "PID constants only apply in Speed, Position, and Current mode");
+ return 0.0;
+ }
+
+ return m_i;
+}
+
+/**
+ * Get the Differential gain of the controller.
+ *
+ * @return The differential gain.
+ */
+double CANJaguar::GetD()
+{
+ if(m_controlMode == kPercentVbus || m_controlMode == kVoltage)
+ {
+ wpi_setWPIErrorWithContext(IncompatibleMode, "PID constants only apply in Speed, Position, and Current mode");
+ return 0.0;
+ }
+
+ return m_d;
+}
+
+/**
+ * Enable the closed loop controller.
+ *
+ * Start actually controlling the output based on the feedback.
+ * If starting a position controller with an encoder reference,
+ * use the encoderInitialPosition parameter to initialize the
+ * encoder state.
+ *
+ * @param encoderInitialPosition Encoder position to set if position with encoder reference. Ignored otherwise.
+ */
+void CANJaguar::EnableControl(double encoderInitialPosition)
+{
+ uint8_t dataBuffer[8];
+ uint8_t dataSize = 0;
+
+ switch(m_controlMode)
+ {
+ case kPercentVbus:
+ sendMessage(LM_API_VOLT_T_EN, dataBuffer, dataSize);
+ break;
+ case kSpeed:
+ sendMessage(LM_API_SPD_T_EN, dataBuffer, dataSize);
+ break;
+ case kPosition:
+ dataSize = packFXP16_16(dataBuffer, encoderInitialPosition);
+ sendMessage(LM_API_POS_T_EN, dataBuffer, dataSize);
+ break;
+ case kCurrent:
+ sendMessage(LM_API_ICTRL_T_EN, dataBuffer, dataSize);
+ break;
+ case kVoltage:
+ sendMessage(LM_API_VCOMP_T_EN, dataBuffer, dataSize);
+ break;
+ default:
+ wpi_setWPIErrorWithContext(IncompatibleMode, "The Jaguar only supports Current, Voltage, Position, Speed, and Percent (Throttle) modes.");
+ return;
+ }
+
+ m_controlEnabled = true;
+ m_controlModeVerified = false;
+}
+
+/**
+ * Disable the closed loop controller.
+ *
+ * Stop driving the output based on the feedback.
+ */
+void CANJaguar::DisableControl()
+{
+ uint8_t dataBuffer[8];
+ uint8_t dataSize = 0;
+
+ // Disable all control
+ sendMessage(LM_API_VOLT_DIS, dataBuffer, dataSize);
+ sendMessage(LM_API_SPD_DIS, dataBuffer, dataSize);
+ sendMessage(LM_API_POS_DIS, dataBuffer, dataSize);
+ sendMessage(LM_API_ICTRL_DIS, dataBuffer, dataSize);
+ sendMessage(LM_API_VCOMP_DIS, dataBuffer, dataSize);
+
+ // Stop all periodic setpoints
+ sendMessage(LM_API_VOLT_T_SET, dataBuffer, dataSize, CAN_SEND_PERIOD_STOP_REPEATING);
+ sendMessage(LM_API_SPD_T_SET, dataBuffer, dataSize, CAN_SEND_PERIOD_STOP_REPEATING);
+ sendMessage(LM_API_POS_T_SET, dataBuffer, dataSize, CAN_SEND_PERIOD_STOP_REPEATING);
+ sendMessage(LM_API_ICTRL_T_SET, dataBuffer, dataSize, CAN_SEND_PERIOD_STOP_REPEATING);
+ sendMessage(LM_API_VCOMP_T_SET, dataBuffer, dataSize, CAN_SEND_PERIOD_STOP_REPEATING);
+
+ m_controlEnabled = false;
+}
+
+/**
+ * Enable controlling the motor voltage as a percentage of the bus voltage
+ * without any position or speed feedback.<br>
+ * After calling this you must call {@link CANJaguar#EnableControl()} or {@link CANJaguar#EnableControl(double)} to enable the device.
+ */
+void CANJaguar::SetPercentMode()
+{
+ SetControlMode(kPercentVbus);
+ SetPositionReference(LM_REF_NONE);
+ SetSpeedReference(LM_REF_NONE);
+}
+
+/**
+ * Enable controlling the motor voltage as a percentage of the bus voltage,
+ * and enable speed sensing from a non-quadrature encoder.<br>
+ * After calling this you must call {@link CANJaguar#EnableControl()} or {@link CANJaguar#EnableControl(double)} to enable the device.
+ *
+ * @param tag The constant CANJaguar::Encoder
+ * @param codesPerRev The counts per revolution on the encoder
+ */
+void CANJaguar::SetPercentMode(CANJaguar::EncoderStruct, uint16_t codesPerRev)
+{
+ SetControlMode(kPercentVbus);
+ SetPositionReference(LM_REF_NONE);
+ SetSpeedReference(LM_REF_ENCODER);
+ ConfigEncoderCodesPerRev(codesPerRev);
+}
+
+/**
+ * Enable controlling the motor voltage as a percentage of the bus voltage,
+ * and enable speed sensing from a non-quadrature encoder.<br>
+ * After calling this you must call {@link CANJaguar#EnableControl()} or {@link CANJaguar#EnableControl(double)} to enable the device.
+ *
+ * @param tag The constant CANJaguar::QuadEncoder
+ * @param codesPerRev The counts per revolution on the encoder
+ */
+void CANJaguar::SetPercentMode(CANJaguar::QuadEncoderStruct, uint16_t codesPerRev)
+{
+ SetControlMode(kPercentVbus);
+ SetPositionReference(LM_REF_ENCODER);
+ SetSpeedReference(LM_REF_QUAD_ENCODER);
+ ConfigEncoderCodesPerRev(codesPerRev);
+}
+
+/**
+* Enable controlling the motor voltage as a percentage of the bus voltage,
+* and enable position sensing from a potentiometer and no speed feedback.<br>
+* After calling this you must call {@link CANJaguar#EnableControl()} or {@link CANJaguar#EnableControl(double)} to enable the device.
+*
+* @param potentiometer The constant CANJaguar::Potentiometer
+*/
+void CANJaguar::SetPercentMode(CANJaguar::PotentiometerStruct)
+{
+ SetControlMode(kPercentVbus);
+ SetPositionReference(LM_REF_POT);
+ SetSpeedReference(LM_REF_NONE);
+ ConfigPotentiometerTurns(1);
+}
+
+/**
+ * Enable controlling the motor current with a PID loop.<br>
+ * After calling this you must call {@link CANJaguar#EnableControl()} or {@link CANJaguar#EnableControl(double)} to enable the device.
+ *
+ * @param p The proportional gain of the Jaguar's PID controller.
+ * @param i The integral gain of the Jaguar's PID controller.
+ * @param d The differential gain of the Jaguar's PID controller.
+ */
+void CANJaguar::SetCurrentMode(double p, double i, double d)
+{
+ SetControlMode(kCurrent);
+ SetPositionReference(LM_REF_NONE);
+ SetSpeedReference(LM_REF_NONE);
+ SetPID(p, i, d);
+}
+
+/**
+* Enable controlling the motor current with a PID loop, and enable speed
+* sensing from a non-quadrature encoder.<br>
+* After calling this you must call {@link CANJaguar#EnableControl()} or {@link CANJaguar#EnableControl(double)} to enable the device.
+*
+* @param encoder The constant CANJaguar::Encoder
+* @param p The proportional gain of the Jaguar's PID controller.
+* @param i The integral gain of the Jaguar's PID controller.
+* @param d The differential gain of the Jaguar's PID controller.
+*/
+void CANJaguar::SetCurrentMode(CANJaguar::EncoderStruct, uint16_t codesPerRev, double p, double i, double d)
+{
+ SetControlMode(kCurrent);
+ SetPositionReference(LM_REF_NONE);
+ SetSpeedReference(LM_REF_NONE);
+ ConfigEncoderCodesPerRev(codesPerRev);
+ SetPID(p, i, d);
+}
+
+/**
+* Enable controlling the motor current with a PID loop, and enable speed and
+* position sensing from a quadrature encoder.<br>
+* After calling this you must call {@link CANJaguar#EnableControl()} or {@link CANJaguar#EnableControl(double)} to enable the device.
+*
+* @param endoer The constant CANJaguar::QuadEncoder
+* @param p The proportional gain of the Jaguar's PID controller.
+* @param i The integral gain of the Jaguar's PID controller.
+* @param d The differential gain of the Jaguar's PID controller.
+*/
+void CANJaguar::SetCurrentMode(CANJaguar::QuadEncoderStruct, uint16_t codesPerRev, double p, double i, double d)
+{
+ SetControlMode(kCurrent);
+ SetPositionReference(LM_REF_ENCODER);
+ SetSpeedReference(LM_REF_QUAD_ENCODER);
+ ConfigEncoderCodesPerRev(codesPerRev);
+ SetPID(p, i, d);
+}
+
+/**
+* Enable controlling the motor current with a PID loop, and enable position
+* sensing from a potentiometer.<br>
+* After calling this you must call {@link CANJaguar#EnableControl()} or {@link CANJaguar#EnableControl(double)} to enable the device.
+*
+* @param potentiometer The constant CANJaguar::Potentiometer
+* @param p The proportional gain of the Jaguar's PID controller.
+* @param i The integral gain of the Jaguar's PID controller.
+* @param d The differential gain of the Jaguar's PID controller.
+*/
+void CANJaguar::SetCurrentMode(CANJaguar::PotentiometerStruct, double p, double i, double d)
+{
+ SetControlMode(kCurrent);
+ SetPositionReference(LM_REF_POT);
+ SetSpeedReference(LM_REF_NONE);
+ ConfigPotentiometerTurns(1);
+ SetPID(p, i, d);
+}
+
+/**
+ * Enable controlling the speed with a feedback loop from a non-quadrature
+ * encoder.<br>
+ * After calling this you must call {@link CANJaguar#EnableControl()} or {@link CANJaguar#EnableControl(double)} to enable the device.
+ *
+ * @param encoder The constant CANJaguar::Encoder
+ * @param codesPerRev The counts per revolution on the encoder.
+ * @param p The proportional gain of the Jaguar's PID controller.
+ * @param i The integral gain of the Jaguar's PID controller.
+ * @param d The differential gain of the Jaguar's PID controller.
+ */
+void CANJaguar::SetSpeedMode(CANJaguar::EncoderStruct, uint16_t codesPerRev, double p, double i, double d)
+{
+ SetControlMode(kSpeed);
+ SetPositionReference(LM_REF_NONE);
+ SetSpeedReference(LM_REF_ENCODER);
+ ConfigEncoderCodesPerRev(codesPerRev);
+ SetPID(p, i, d);
+}
+
+/**
+* Enable controlling the speed with a feedback loop from a quadrature encoder.<br>
+* After calling this you must call {@link CANJaguar#EnableControl()} or {@link CANJaguar#EnableControl(double)} to enable the device.
+*
+* @param encoder The constant CANJaguar::QuadEncoder
+* @param codesPerRev The counts per revolution on the encoder.
+* @param p The proportional gain of the Jaguar's PID controller.
+* @param i The integral gain of the Jaguar's PID controller.
+* @param d The differential gain of the Jaguar's PID controller.
+*/
+void CANJaguar::SetSpeedMode(CANJaguar::QuadEncoderStruct, uint16_t codesPerRev, double p, double i, double d)
+{
+ SetControlMode(kSpeed);
+ SetPositionReference(LM_REF_ENCODER);
+ SetSpeedReference(LM_REF_QUAD_ENCODER);
+ ConfigEncoderCodesPerRev(codesPerRev);
+ SetPID(p, i, d);
+}
+
+/**
+ * Enable controlling the position with a feedback loop using an encoder.<br>
+ * After calling this you must call {@link CANJaguar#EnableControl()} or {@link CANJaguar#EnableControl(double)} to enable the device.
+ *
+ * @param encoder The constant CANJaguar::QuadEncoder
+ * @param codesPerRev The counts per revolution on the encoder.
+ * @param p The proportional gain of the Jaguar's PID controller.
+ * @param i The integral gain of the Jaguar's PID controller.
+ * @param d The differential gain of the Jaguar's PID controller.
+ *
+ */
+void CANJaguar::SetPositionMode(CANJaguar::QuadEncoderStruct, uint16_t codesPerRev, double p, double i, double d)
+{
+ SetControlMode(kPosition);
+ SetPositionReference(LM_REF_ENCODER);
+ ConfigEncoderCodesPerRev(codesPerRev);
+ SetPID(p, i, d);
+}
+
+/**
+* Enable controlling the position with a feedback loop using a potentiometer.<br>
+* After calling this you must call {@link CANJaguar#EnableControl()} or {@link CANJaguar#EnableControl(double)} to enable the device.
+* @param p The proportional gain of the Jaguar's PID controller.
+* @param i The integral gain of the Jaguar's PID controller.
+* @param d The differential gain of the Jaguar's PID controller.
+*/
+void CANJaguar::SetPositionMode(CANJaguar::PotentiometerStruct, double p, double i, double d)
+{
+ SetControlMode(kPosition);
+ SetPositionReference(LM_REF_POT);
+ ConfigPotentiometerTurns(1);
+ SetPID(p, i, d);
+}
+
+/**
+* Enable controlling the motor voltage without any position or speed feedback.<br>
+* After calling this you must call {@link CANJaguar#EnableControl()} or {@link CANJaguar#EnableControl(double)} to enable the device.
+*/
+void CANJaguar::SetVoltageMode()
+{
+ SetControlMode(kVoltage);
+ SetPositionReference(LM_REF_NONE);
+ SetSpeedReference(LM_REF_NONE);
+}
+
+/**
+* Enable controlling the motor voltage with speed feedback from a
+* non-quadrature encoder and no position feedback.<br>
+* After calling this you must call {@link CANJaguar#EnableControl()} or {@link CANJaguar#EnableControl(double)} to enable the device.
+*
+* @param encoder The constant CANJaguar::Encoder
+* @param codesPerRev The counts per revolution on the encoder
+*/
+void CANJaguar::SetVoltageMode(CANJaguar::EncoderStruct, uint16_t codesPerRev)
+{
+ SetControlMode(kVoltage);
+ SetPositionReference(LM_REF_NONE);
+ SetSpeedReference(LM_REF_ENCODER);
+ ConfigEncoderCodesPerRev(codesPerRev);
+}
+
+/**
+* Enable controlling the motor voltage with position and speed feedback from a
+* quadrature encoder.<br>
+* After calling this you must call {@link CANJaguar#EnableControl()} or {@link CANJaguar#EnableControl(double)} to enable the device.
+*
+* @param encoder The constant CANJaguar::QuadEncoder
+* @param codesPerRev The counts per revolution on the encoder
+*/
+void CANJaguar::SetVoltageMode(CANJaguar::QuadEncoderStruct, uint16_t codesPerRev)
+{
+ SetControlMode(kVoltage);
+ SetPositionReference(LM_REF_ENCODER);
+ SetSpeedReference(LM_REF_QUAD_ENCODER);
+ ConfigEncoderCodesPerRev(codesPerRev);
+}
+
+/**
+* Enable controlling the motor voltage with position feedback from a
+* potentiometer and no speed feedback.<br>
+* After calling this you must call {@link CANJaguar#EnableControl()} or {@link CANJaguar#EnableControl(double)} to enable the device.
+*
+* @param potentiometer The constant CANJaguar::Potentiometer
+*/
+void CANJaguar::SetVoltageMode(CANJaguar::PotentiometerStruct)
+{
+ SetControlMode(kVoltage);
+ SetPositionReference(LM_REF_POT);
+ SetSpeedReference(LM_REF_NONE);
+ ConfigPotentiometerTurns(1);
+}
+
+/**
+ * Used internally. In order to set the control mode see the methods listed below.
+ * Change the control mode of this Jaguar object.
+ *
+ * After changing modes, configure any PID constants or other settings needed
+ * and then EnableControl() to actually change the mode on the Jaguar.
+ *
+ * @param controlMode The new mode.
+ */
+void CANJaguar::SetControlMode(ControlMode controlMode)
+{
+ // Disable the previous mode
+ DisableControl();
+
+ if (controlMode == kFollower)
+ wpi_setWPIErrorWithContext(IncompatibleMode, "The Jaguar only supports Current, Voltage, Position, Speed, and Percent (Throttle) modes.");
+
+ // Update the local mode
+ m_controlMode = controlMode;
+ m_controlModeVerified = false;
+
+ HALReport(HALUsageReporting::kResourceType_CANJaguar, m_deviceNumber, m_controlMode);
+}
+
+/**
+ * Get the active control mode from the Jaguar.
+ *
+ * Ask the Jag what mode it is in.
+ *
+ * @return ControlMode that the Jag is in.
+ */
+CANJaguar::ControlMode CANJaguar::GetControlMode()
+{
+ return m_controlMode;
+}
+
+/**
+ * Get the voltage at the battery input terminals of the Jaguar.
+ *
+ * @return The bus voltage in volts.
+ */
+float CANJaguar::GetBusVoltage()
+{
+ updatePeriodicStatus();
+
+ return m_busVoltage;
+}
+
+/**
+ * Get the voltage being output from the motor terminals of the Jaguar.
+ *
+ * @return The output voltage in volts.
+ */
+float CANJaguar::GetOutputVoltage()
+{
+ updatePeriodicStatus();
+
+ return m_outputVoltage;
+}
+
+/**
+ * Get the current through the motor terminals of the Jaguar.
+ *
+ * @return The output current in amps.
+ */
+float CANJaguar::GetOutputCurrent()
+{
+ updatePeriodicStatus();
+
+ return m_outputCurrent;
+}
+
+/**
+ * Get the internal temperature of the Jaguar.
+ *
+ * @return The temperature of the Jaguar in degrees Celsius.
+ */
+float CANJaguar::GetTemperature()
+{
+ updatePeriodicStatus();
+
+ return m_temperature;
+}
+
+/**
+ * Get the position of the encoder or potentiometer.
+ *
+ * @return The position of the motor in rotations based on the configured feedback.
+ * @see CANJaguar#ConfigPotentiometerTurns(int)
+ * @see CANJaguar#ConfigEncoderCodesPerRev(int)
+ */
+double CANJaguar::GetPosition()
+{
+ updatePeriodicStatus();
+
+ return m_position;
+}
+
+/**
+ * Get the speed of the encoder.
+ *
+ * @return The speed of the motor in RPM based on the configured feedback.
+ */
+double CANJaguar::GetSpeed()
+{
+ updatePeriodicStatus();
+
+ return m_speed;
+}
+
+/**
+ * Get the status of the forward limit switch.
+ *
+ * @return The motor is allowed to turn in the forward direction when true.
+ */
+bool CANJaguar::GetForwardLimitOK()
+{
+ updatePeriodicStatus();
+
+ return m_limits & kForwardLimit;
+}
+
+/**
+ * Get the status of the reverse limit switch.
+ *
+ * @return The motor is allowed to turn in the reverse direction when true.
+ */
+bool CANJaguar::GetReverseLimitOK()
+{
+ updatePeriodicStatus();
+
+ return m_limits & kReverseLimit;
+}
+
+/**
+ * Get the status of any faults the Jaguar has detected.
+ *
+ * @return A bit-mask of faults defined by the "Faults" enum.
+ * @see #kCurrentFault
+ * @see #kBusVoltageFault
+ * @see #kTemperatureFault
+ * @see #kGateDriverFault
+ */
+uint16_t CANJaguar::GetFaults()
+{
+ updatePeriodicStatus();
+
+ return m_faults;
+}
+
+/**
+ * Set the maximum voltage change rate.
+ *
+ * When in PercentVbus or Voltage output mode, the rate at which the voltage changes can
+ * be limited to reduce current spikes. Set this to 0.0 to disable rate limiting.
+ *
+ * @param rampRate The maximum rate of voltage change in Percent Voltage mode in V/s.
+ */
+void CANJaguar::SetVoltageRampRate(double rampRate)
+{
+ uint8_t dataBuffer[8];
+ uint8_t dataSize;
+ uint32_t message;
+
+ switch(m_controlMode)
+ {
+ case kPercentVbus:
+ dataSize = packPercentage(dataBuffer, rampRate / (m_maxOutputVoltage * kControllerRate));
+ message = LM_API_VOLT_SET_RAMP;
+ break;
+ case kVoltage:
+ dataSize = packFXP8_8(dataBuffer, rampRate / kControllerRate);
+ message = LM_API_VCOMP_COMP_RAMP;
+ break;
+ default:
+ wpi_setWPIErrorWithContext(IncompatibleMode, "SetVoltageRampRate only applies in Voltage and Percent mode");
+ return;
+ }
+
+ sendMessage(message, dataBuffer, dataSize);
+
+ m_voltageRampRate = rampRate;
+ m_voltageRampRateVerified = false;
+}
+
+/**
+ * Get the version of the firmware running on the Jaguar.
+ *
+ * @return The firmware version. 0 if the device did not respond.
+ */
+uint32_t CANJaguar::GetFirmwareVersion()
+{
+ return m_firmwareVersion;
+}
+
+/**
+ * Get the version of the Jaguar hardware.
+ *
+ * @return The hardware version. 1: Jaguar, 2: Black Jaguar
+ */
+uint8_t CANJaguar::GetHardwareVersion()
+{
+ return m_hardwareVersion;
+}
+
+/**
+ * Configure what the controller does to the H-Bridge when neutral (not driving the output).
+ *
+ * This allows you to override the jumper configuration for brake or coast.
+ *
+ * @param mode Select to use the jumper setting or to override it to coast or brake.
+ */
+void CANJaguar::ConfigNeutralMode(NeutralMode mode)
+{
+ uint8_t dataBuffer[8];
+
+ // Set the neutral mode
+ sendMessage(LM_API_CFG_BRAKE_COAST, dataBuffer, sizeof(uint8_t));
+
+ m_neutralMode = mode;
+ m_neutralModeVerified = false;
+}
+
+/**
+ * Configure how many codes per revolution are generated by your encoder.
+ *
+ * @param codesPerRev The number of counts per revolution in 1X mode.
+ */
+void CANJaguar::ConfigEncoderCodesPerRev(uint16_t codesPerRev)
+{
+ uint8_t dataBuffer[8];
+ uint8_t dataSize;
+
+ // Set the codes per revolution mode
+ dataSize = packint16_t(dataBuffer, codesPerRev);
+ sendMessage(LM_API_CFG_ENC_LINES, dataBuffer, dataSize);
+
+ m_encoderCodesPerRev = codesPerRev;
+ m_encoderCodesPerRevVerified = false;
+}
+
+/**
+ * Configure the number of turns on the potentiometer.
+ *
+ * There is no special support for continuous turn potentiometers.
+ * Only integer numbers of turns are supported.
+ *
+ * @param turns The number of turns of the potentiometer.
+ */
+void CANJaguar::ConfigPotentiometerTurns(uint16_t turns)
+{
+ uint8_t dataBuffer[8];
+ uint8_t dataSize;
+
+ // Set the pot turns
+ dataSize = packint16_t(dataBuffer, turns);
+ sendMessage(LM_API_CFG_POT_TURNS, dataBuffer, dataSize);
+
+ m_potentiometerTurns = turns;
+ m_potentiometerTurnsVerified = false;
+}
+
+/**
+ * Configure Soft Position Limits when in Position Controller mode.
+ *
+ * When controlling position, you can add additional limits on top of the limit switch inputs
+ * that are based on the position feedback. If the position limit is reached or the
+ * switch is opened, that direction will be disabled.
+ *
+
+ * @param forwardLimitPosition The position that if exceeded will disable the forward direction.
+ * @param reverseLimitPosition The position that if exceeded will disable the reverse direction.
+ */
+void CANJaguar::ConfigSoftPositionLimits(double forwardLimitPosition, double reverseLimitPosition)
+{
+ ConfigLimitMode(kLimitMode_SoftPositionLimits);
+ ConfigForwardLimit(forwardLimitPosition);
+ ConfigReverseLimit(reverseLimitPosition);
+}
+
+/**
+ * Disable Soft Position Limits if previously enabled.
+ *
+ * Soft Position Limits are disabled by default.
+ */
+void CANJaguar::DisableSoftPositionLimits()
+{
+ ConfigLimitMode(kLimitMode_SwitchInputsOnly);
+}
+
+/**
+ * Set the limit mode for position control mode.
+ *
+ * Use ConfigSoftPositionLimits or DisableSoftPositionLimits to set this
+ * automatically.
+ */
+void CANJaguar::ConfigLimitMode(LimitMode mode)
+{
+ uint8_t dataBuffer[8];
+
+ dataBuffer[0] = mode;
+ sendMessage(LM_API_CFG_LIMIT_MODE, dataBuffer, sizeof(uint8_t));
+
+ m_limitMode = mode;
+ m_limitModeVerified = false;
+}
+
+/**
+* Set the position that if exceeded will disable the forward direction.
+*
+* Use ConfigSoftPositionLimits to set this and the limit mode automatically.
+*/
+void CANJaguar::ConfigForwardLimit(double forwardLimitPosition)
+{
+ uint8_t dataBuffer[8];
+ uint8_t dataSize;
+
+ dataSize = packFXP16_16(dataBuffer, forwardLimitPosition);
+ dataBuffer[dataSize++] = 1;
+ sendMessage(LM_API_CFG_LIMIT_FWD, dataBuffer, dataSize);
+
+ m_forwardLimit = forwardLimitPosition;
+ m_forwardLimitVerified = false;
+}
+
+/**
+* Set the position that if exceeded will disable the reverse direction.
+*
+* Use ConfigSoftPositionLimits to set this and the limit mode automatically.
+*/
+void CANJaguar::ConfigReverseLimit(double reverseLimitPosition)
+{
+ uint8_t dataBuffer[8];
+ uint8_t dataSize;
+
+ dataSize = packFXP16_16(dataBuffer, reverseLimitPosition);
+ dataBuffer[dataSize++] = 0;
+ sendMessage(LM_API_CFG_LIMIT_REV, dataBuffer, dataSize);
+
+ m_reverseLimit = reverseLimitPosition;
+ m_reverseLimitVerified = false;
+}
+
+/**
+ * Configure the maximum voltage that the Jaguar will ever output.
+ *
+ * This can be used to limit the maximum output voltage in all modes so that
+ * motors which cannot withstand full bus voltage can be used safely.
+ *
+ * @param voltage The maximum voltage output by the Jaguar.
+ */
+void CANJaguar::ConfigMaxOutputVoltage(double voltage)
+{
+ uint8_t dataBuffer[8];
+ uint8_t dataSize;
+
+ dataSize = packFXP8_8(dataBuffer, voltage);
+ sendMessage(LM_API_CFG_MAX_VOUT, dataBuffer, dataSize);
+
+ m_maxOutputVoltage = voltage;
+ m_maxOutputVoltageVerified = false;
+}
+
+/**
+ * Configure how long the Jaguar waits in the case of a fault before resuming operation.
+ *
+ * Faults include over temerature, over current, and bus under voltage.
+ * The default is 3.0 seconds, but can be reduced to as low as 0.5 seconds.
+ *
+ * @param faultTime The time to wait before resuming operation, in seconds.
+ */
+void CANJaguar::ConfigFaultTime(float faultTime)
+{
+ uint8_t dataBuffer[8];
+ uint8_t dataSize;
+
+ if(faultTime < 0.5) faultTime = 0.5;
+ else if(faultTime > 3.0) faultTime = 3.0;
+
+ // Message takes ms
+ dataSize = packint16_t(dataBuffer, (int16_t)(faultTime * 1000.0));
+ sendMessage(LM_API_CFG_FAULT_TIME, dataBuffer, dataSize);
+
+ m_faultTime = faultTime;
+ m_faultTimeVerified = false;
+}
+
+/**
+ * Update all the motors that have pending sets in the syncGroup.
+ *
+ * @param syncGroup A bitmask of groups to generate synchronous output.
+ */
+void CANJaguar::UpdateSyncGroup(uint8_t syncGroup)
+{
+ sendMessageHelper(CAN_MSGID_API_SYNC, &syncGroup, sizeof(syncGroup), CAN_SEND_PERIOD_NO_REPEAT);
+}
+
+
+void CANJaguar::SetExpiration(float timeout)
+{
+ if (m_safetyHelper) m_safetyHelper->SetExpiration(timeout);
+}
+
+float CANJaguar::GetExpiration()
+{
+ if (!m_safetyHelper) return 0.0;
+ return m_safetyHelper->GetExpiration();
+}
+
+bool CANJaguar::IsAlive()
+{
+ if (!m_safetyHelper) return false;
+ return m_safetyHelper->IsAlive();
+}
+
+bool CANJaguar::IsSafetyEnabled()
+{
+ if (!m_safetyHelper) return false;
+ return m_safetyHelper->IsSafetyEnabled();
+}
+
+void CANJaguar::SetSafetyEnabled(bool enabled)
+{
+ if (m_safetyHelper) m_safetyHelper->SetSafetyEnabled(enabled);
+}
+
+void CANJaguar::GetDescription(char *desc)
+{
+ sprintf(desc, "CANJaguar ID %d", m_deviceNumber);
+}
+
+uint8_t CANJaguar::GetDeviceID()
+{
+ return m_deviceNumber;
+}
+
+/**
+ * Common interface for stopping the motor
+ * Part of the MotorSafety interface
+ *
+ * @deprecated Call DisableControl instead.
+ */
+void CANJaguar::StopMotor()
+{
+ DisableControl();
+}
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/CANTalon.cpp b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/CANTalon.cpp
new file mode 100644
index 0000000..1dd7b7e
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/CANTalon.cpp
@@ -0,0 +1,1259 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2014. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+
+#include "CANTalon.h"
+#include "WPIErrors.h"
+#include "ctre/CanTalonSRX.h"
+#include <unistd.h> // usleep
+/**
+ * Constructor for the CANTalon device.
+ * @param deviceNumber The CAN ID of the Talon SRX
+ */
+CANTalon::CANTalon(int deviceNumber)
+ : m_deviceNumber(deviceNumber)
+ , m_impl(new CanTalonSRX(deviceNumber))
+ , m_safetyHelper(new MotorSafetyHelper(this))
+ , m_profile(0)
+ , m_controlEnabled(true)
+ , m_controlMode(kPercentVbus)
+ , m_setPoint(0)
+{
+ ApplyControlMode(m_controlMode);
+ m_impl->SetProfileSlotSelect(m_profile);
+}
+/**
+ * Constructor for the CANTalon device.
+ * @param deviceNumber The CAN ID of the Talon SRX
+ * @param controlPeriodMs The period in ms to send the CAN control frame.
+ * Period is bounded to [1ms, 95ms].
+ */
+CANTalon::CANTalon(int deviceNumber,int controlPeriodMs)
+ : m_deviceNumber(deviceNumber)
+ , m_impl(new CanTalonSRX(deviceNumber,controlPeriodMs)) /* bounded underneath to be within [1 ms,95 ms] */
+ , m_safetyHelper(new MotorSafetyHelper(this))
+ , m_profile(0)
+ , m_controlEnabled(true)
+ , m_controlMode(kPercentVbus)
+ , m_setPoint(0)
+{
+ ApplyControlMode(m_controlMode);
+ m_impl->SetProfileSlotSelect(m_profile);
+}
+CANTalon::~CANTalon() {
+ delete m_impl;
+ delete m_safetyHelper;
+}
+
+/**
+* Write out the PID value as seen in the PIDOutput base object.
+*
+* @deprecated Call Set instead.
+*
+* @param output Write out the PercentVbus value as was computed by the PIDController
+*/
+void CANTalon::PIDWrite(float output)
+{
+ if (GetControlMode() == kPercentVbus)
+ {
+ Set(output);
+ }
+ else
+ {
+ wpi_setWPIErrorWithContext(IncompatibleMode, "PID only supported in PercentVbus mode");
+ }
+}
+
+ /**
+ * Gets the current status of the Talon (usually a sensor value).
+ *
+ * In Current mode: returns output current.
+ * In Speed mode: returns current speed.
+ * In Position mode: returns current sensor position.
+ * In PercentVbus and Follower modes: returns current applied throttle.
+ *
+ * @return The current sensor value of the Talon.
+ */
+float CANTalon::Get()
+{
+ int value;
+ switch(m_controlMode) {
+ case kVoltage:
+ return GetOutputVoltage();
+ case kCurrent:
+ return GetOutputCurrent();
+ case kSpeed:
+ m_impl->GetSensorVelocity(value);
+ return value;
+ case kPosition:
+ m_impl->GetSensorPosition(value);
+ return value;
+ case kPercentVbus:
+ default:
+ m_impl->GetAppliedThrottle(value);
+ return (float)value / 1023.0;
+ }
+}
+
+/**
+ * Sets the appropriate output on the talon, depending on the mode.
+ *
+ * In PercentVbus, the output is between -1.0 and 1.0, with 0.0 as stopped.
+ * In Voltage mode, output value is in volts.
+ * In Current mode, output value is in amperes.
+ * In Speed mode, output value is in position change / 10ms.
+ * In Position mode, output value is in encoder ticks or an analog value,
+ * depending on the sensor.
+ * In Follower mode, the output value is the integer device ID of the talon to duplicate.
+ *
+ * @param outputValue The setpoint value, as described above.
+ * @see SelectProfileSlot to choose between the two sets of gains.
+ */
+void CANTalon::Set(float value, uint8_t syncGroup)
+{
+ /* feed safety helper since caller just updated our output */
+ m_safetyHelper->Feed();
+ if(m_controlEnabled) {
+ m_setPoint = value;
+ CTR_Code status;
+ switch(m_controlMode) {
+ case CANSpeedController::kPercentVbus:
+ {
+ m_impl->Set(value);
+ status = CTR_OKAY;
+ }
+ break;
+ case CANSpeedController::kFollower:
+ {
+ status = m_impl->SetDemand((int)value);
+ }
+ break;
+ case CANSpeedController::kVoltage:
+ {
+ // Voltage is an 8.8 fixed point number.
+ int volts = int(value * 256);
+ status = m_impl->SetDemand(volts);
+ }
+ break;
+ case CANSpeedController::kSpeed:
+ status = m_impl->SetDemand(value);
+ break;
+ case CANSpeedController::kPosition:
+ status = m_impl->SetDemand(value);
+ break;
+ default:
+ break;
+ }
+ if (status != CTR_OKAY) {
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ }
+
+ status = m_impl->SetModeSelect(m_sendMode);
+
+ if (status != CTR_OKAY) {
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ }
+
+ }
+}
+
+/**
+ * TODO documentation (see CANJaguar.cpp)
+ */
+void CANTalon::Disable()
+{
+ // Until Modes other than throttle work, just disable by setting throttle to 0.0.
+ m_impl->SetModeSelect((int)CANTalon::kDisabled);
+ m_controlEnabled = false;
+}
+
+/**
+ * TODO documentation (see CANJaguar.cpp)
+ */
+void CANTalon::EnableControl() {
+ SetControlMode(m_controlMode);
+ m_controlEnabled = true;
+}
+
+/**
+ * @return Whether the Talon is currently enabled.
+ */
+bool CANTalon::IsControlEnabled() {
+ return m_controlEnabled;
+}
+
+/**
+ * @param p Proportional constant to use in PID loop.
+ * @see SelectProfileSlot to choose between the two sets of gains.
+ */
+void CANTalon::SetP(double p)
+{
+ CTR_Code status = m_impl->SetPgain(m_profile, p);
+ if(status != CTR_OKAY) {
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ }
+}
+
+ /**
+ * Set the integration constant of the currently selected profile.
+ *
+ * @param i Integration constant for the currently selected PID profile.
+ * @see SelectProfileSlot to choose between the two sets of gains.
+ */
+void CANTalon::SetI(double i)
+{
+ CTR_Code status = m_impl->SetIgain(m_profile, i);
+ if(status != CTR_OKAY) {
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ }
+}
+
+/**
+ * Set the derivative constant of the currently selected profile.
+ *
+ * @param d Derivative constant for the currently selected PID profile.
+ * @see SelectProfileSlot to choose between the two sets of gains.
+ */
+void CANTalon::SetD(double d)
+{
+ CTR_Code status = m_impl->SetDgain(m_profile, d);
+ if(status != CTR_OKAY) {
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ }
+}
+/**
+ * Set the feedforward value of the currently selected profile.
+ *
+ * @param f Feedforward constant for the currently selected PID profile.
+ * @see SelectProfileSlot to choose between the two sets of gains.
+ */
+void CANTalon::SetF(double f)
+{
+ CTR_Code status = m_impl->SetFgain(m_profile, f);
+ if(status != CTR_OKAY) {
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ }
+}
+/**
+ * Set the Izone to a nonzero value to auto clear the integral accumulator
+ * when the absolute value of CloseLoopError exceeds Izone.
+ *
+ * @see SelectProfileSlot to choose between the two sets of gains.
+ */
+void CANTalon::SetIzone(unsigned iz)
+{
+ CTR_Code status = m_impl->SetIzone(m_profile, iz);
+ if(status != CTR_OKAY) {
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ }
+}
+/**
+ * SRX has two available slots for PID.
+ * @param slotIdx one or zero depending on which slot caller wants.
+ */
+void CANTalon::SelectProfileSlot(int slotIdx)
+{
+ m_profile = (slotIdx == 0) ? 0 : 1; /* only get two slots for now */
+ CTR_Code status = m_impl->SetProfileSlotSelect(m_profile);
+ if(status != CTR_OKAY) {
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ }
+}
+/**
+ * Sets control values for closed loop control.
+ *
+ * @param p Proportional constant.
+ * @param i Integration constant.
+ * @param d Differential constant.
+ * This function does not modify F-gain. Considerable passing a zero for f using
+ * the four-parameter function.
+ */
+void CANTalon::SetPID(double p, double i, double d)
+{
+ SetP(p);
+ SetI(i);
+ SetD(d);
+}
+/**
+ * Sets control values for closed loop control.
+ *
+ * @param p Proportional constant.
+ * @param i Integration constant.
+ * @param d Differential constant.
+ * @param f Feedforward constant.
+ */
+void CANTalon::SetPID(double p, double i, double d, double f)
+{
+ SetP(p);
+ SetI(i);
+ SetD(d);
+ SetF(f);
+}
+/**
+ * Select the feedback device to use in closed-loop
+ */
+void CANTalon::SetFeedbackDevice(FeedbackDevice device)
+{
+ CTR_Code status = m_impl->SetFeedbackDeviceSelect((int)device);
+ if(status != CTR_OKAY) {
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ }
+}
+/**
+ * Select the feedback device to use in closed-loop
+ */
+void CANTalon::SetStatusFrameRateMs(StatusFrameRate stateFrame, int periodMs)
+{
+ CTR_Code status = m_impl->SetStatusFrameRate((int)stateFrame,periodMs);
+ if(status != CTR_OKAY) {
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ }
+}
+
+/**
+ * Get the current proportional constant.
+ *
+ * @return double proportional constant for current profile.
+ * @see SelectProfileSlot to choose between the two sets of gains.
+ */
+double CANTalon::GetP()
+{
+ CanTalonSRX::param_t param = m_profile ? CanTalonSRX::eProfileParamSlot1_P : CanTalonSRX::eProfileParamSlot0_P;
+ // Update the info in m_impl.
+ CTR_Code status = m_impl->RequestParam(param);
+ if(status != CTR_OKAY) {
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ }
+ usleep(kDelayForSolicitedSignalsUs); /* small yield for getting response */
+ double p;
+ status = m_impl->GetPgain(m_profile, p);
+ if(status != CTR_OKAY) {
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ }
+ return p;
+}
+
+/**
+ * TODO documentation (see CANJaguar.cpp)
+ * @see SelectProfileSlot to choose between the two sets of gains.
+ */
+double CANTalon::GetI()
+{
+ CanTalonSRX::param_t param = m_profile ? CanTalonSRX::eProfileParamSlot1_I : CanTalonSRX::eProfileParamSlot0_I;
+ // Update the info in m_impl.
+ CTR_Code status = m_impl->RequestParam(param);
+ if(status != CTR_OKAY) {
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ }
+ usleep(kDelayForSolicitedSignalsUs); /* small yield for getting response */
+
+ double i;
+ status = m_impl->GetIgain(m_profile, i);
+ if(status != CTR_OKAY) {
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ }
+ return i;
+}
+
+/**
+ * TODO documentation (see CANJaguar.cpp)
+ * @see SelectProfileSlot to choose between the two sets of gains.
+ */
+double CANTalon::GetD()
+{
+ CanTalonSRX::param_t param = m_profile ? CanTalonSRX::eProfileParamSlot1_D : CanTalonSRX::eProfileParamSlot0_D;
+ // Update the info in m_impl.
+ CTR_Code status = m_impl->RequestParam(param);
+ if(status != CTR_OKAY) {
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ }
+ usleep(kDelayForSolicitedSignalsUs); /* small yield for getting response */
+
+ double d;
+ status = m_impl->GetDgain(m_profile, d);
+ if(status != CTR_OKAY) {
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ }
+ return d;
+}
+/**
+ *
+ * @see SelectProfileSlot to choose between the two sets of gains.
+ */
+double CANTalon::GetF()
+{
+ CanTalonSRX::param_t param = m_profile ? CanTalonSRX::eProfileParamSlot1_F : CanTalonSRX::eProfileParamSlot0_F;
+ // Update the info in m_impl.
+ CTR_Code status = m_impl->RequestParam(param);
+ if(status != CTR_OKAY) {
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ }
+
+ usleep(kDelayForSolicitedSignalsUs); /* small yield for getting response */
+ double f;
+ status = m_impl->GetFgain(m_profile, f);
+ if(status != CTR_OKAY) {
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ }
+ return f;
+}
+/**
+ * @see SelectProfileSlot to choose between the two sets of gains.
+ */
+int CANTalon::GetIzone()
+{
+ CanTalonSRX::param_t param = m_profile ? CanTalonSRX::eProfileParamSlot1_IZone: CanTalonSRX::eProfileParamSlot0_IZone;
+ // Update the info in m_impl.
+ CTR_Code status = m_impl->RequestParam(param);
+ if(status != CTR_OKAY) {
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ }
+ usleep(kDelayForSolicitedSignalsUs);
+
+ int iz;
+ status = m_impl->GetIzone(m_profile, iz);
+ if(status != CTR_OKAY) {
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ }
+ return iz;
+}
+
+/**
+ * @return the current setpoint; ie, whatever was last passed to Set().
+ */
+double CANTalon::GetSetpoint() {
+ return m_setPoint;
+}
+
+/**
+ * Returns the voltage coming in from the battery.
+ *
+ * @return The input voltage in volts.
+ */
+float CANTalon::GetBusVoltage()
+{
+ double voltage;
+ CTR_Code status = m_impl->GetBatteryV(voltage);
+ if(status != CTR_OKAY) {
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ }
+ return voltage;
+}
+
+/**
+ * @return The voltage being output by the Talon, in Volts.
+ */
+float CANTalon::GetOutputVoltage()
+{
+ int throttle11;
+ CTR_Code status = m_impl->GetAppliedThrottle(throttle11);
+ float voltage = GetBusVoltage() * (float(throttle11) / 1023.0);
+ if(status != CTR_OKAY) {
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ }
+ return voltage;
+}
+
+
+/**
+ * Returns the current going through the Talon, in Amperes.
+ */
+float CANTalon::GetOutputCurrent()
+{
+ double current;
+
+ CTR_Code status = m_impl->GetCurrent(current);
+ if(status != CTR_OKAY) {
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ }
+
+ return current;
+}
+
+ /**
+ * Returns temperature of Talon, in degrees Celsius.
+ */
+float CANTalon::GetTemperature()
+{
+ double temp;
+
+ CTR_Code status = m_impl->GetTemp(temp);
+ if(temp != CTR_OKAY) {
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ }
+ return temp;
+}
+/**
+ * Set the position value of the selected sensor. This is useful for zero-ing quadrature encoders.
+ * Continuous sensors (like analog encoderes) can also partially be set (the portion of the postion based on overflows).
+ */
+void CANTalon::SetPosition(double pos)
+{
+ m_impl->SetSensorPosition(pos);
+}
+/**
+ * TODO documentation (see CANJaguar.cpp)
+ *
+ * @return The position of the sensor currently providing feedback.
+ * When using analog sensors, 0 units corresponds to 0V, 1023 units corresponds to 3.3V
+ * When using an analog encoder (wrapping around 1023 => 0 is possible) the units are still 3.3V per 1023 units.
+ * When using quadrature, each unit is a quadrature edge (4X) mode.
+ */
+double CANTalon::GetPosition()
+{
+ int postition;
+
+ CTR_Code status = m_impl->GetSensorPosition(postition);
+ if(status != CTR_OKAY) {
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ }
+ return (double)postition;
+}
+/**
+ * If sensor and motor are out of phase, sensor can be inverted
+ * (position and velocity multiplied by -1).
+ * @see GetPosition and @see GetSpeed.
+ */
+void CANTalon::SetSensorDirection(bool reverseSensor)
+{
+ CTR_Code status = m_impl->SetRevFeedbackSensor(reverseSensor ? 1 : 0);
+ if(status != CTR_OKAY) {
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ }
+}
+
+/**
+ * Returns the current error in the controller.
+ *
+ * @return the difference between the setpoint and the sensor value.
+ */
+int CANTalon::GetClosedLoopError() {
+ int error;
+ CTR_Code status = m_impl->GetCloseLoopErr(error);
+ if(status != CTR_OKAY) {
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ }
+ return error;
+}
+
+/**
+ * TODO documentation (see CANJaguar.cpp)
+ *
+ * @returns The speed of the sensor currently providing feedback.
+ *
+ * The speed units will be in the sensor's native ticks per 100ms.
+ *
+ * For analog sensors, 3.3V corresponds to 1023 units.
+ * So a speed of 200 equates to ~0.645 dV per 100ms or 6.451 dV per second.
+ * If this is an analog encoder, that likely means 1.9548 rotations per sec.
+ * For quadrature encoders, each unit corresponds a quadrature edge (4X).
+ * So a 250 count encoder will produce 1000 edge events per rotation.
+ * An example speed of 200 would then equate to 20% of a rotation per 100ms,
+ * or 10 rotations per second.
+ */
+double CANTalon::GetSpeed()
+{
+ int speed;
+ // TODO convert from int to appropriate units (or at least document it).
+
+ CTR_Code status = m_impl->GetSensorVelocity(speed);
+ if(status != CTR_OKAY) {
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ }
+ return (double)speed;
+}
+
+/**
+ * Get the position of whatever is in the analog pin of the Talon, regardless of
+ * whether it is actually being used for feedback.
+ *
+ * @returns The 24bit analog value. The bottom ten bits is the ADC (0 - 1023) on
+ * the analog pin of the Talon. The upper 14 bits
+ * tracks the overflows and underflows (continuous sensor).
+ */
+int CANTalon::GetAnalogIn()
+{
+ int position;
+ CTR_Code status = m_impl->GetAnalogInWithOv(position);
+ if(status != CTR_OKAY) {
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ }
+ return position;
+}
+/**
+ * Get the position of whatever is in the analog pin of the Talon, regardless of
+ * whether it is actually being used for feedback.
+ *
+ * @returns The ADC (0 - 1023) on analog pin of the Talon.
+ */
+int CANTalon::GetAnalogInRaw()
+{
+ return GetAnalogIn() & 0x3FF;
+}
+/**
+ * Get the position of whatever is in the analog pin of the Talon, regardless of
+ * whether it is actually being used for feedback.
+ *
+ * @returns The value (0 - 1023) on the analog pin of the Talon.
+ */
+int CANTalon::GetAnalogInVel()
+{
+ int vel;
+ CTR_Code status = m_impl->GetAnalogInVel(vel);
+ if(status != CTR_OKAY) {
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ }
+ return vel;
+}
+
+/**
+ * Get the position of whatever is in the analog pin of the Talon, regardless of
+ * whether it is actually being used for feedback.
+ *
+ * @returns The value (0 - 1023) on the analog pin of the Talon.
+ */
+int CANTalon::GetEncPosition()
+{
+ int position;
+ CTR_Code status = m_impl->GetEncPosition(position);
+ if(status != CTR_OKAY) {
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ }
+ return position;
+}
+
+/**
+ * Get the position of whatever is in the analog pin of the Talon, regardless of
+ * whether it is actually being used for feedback.
+ *
+ * @returns The value (0 - 1023) on the analog pin of the Talon.
+ */
+int CANTalon::GetEncVel()
+{
+ int vel;
+ CTR_Code status = m_impl->GetEncVel(vel);
+ if(status != CTR_OKAY) {
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ }
+ return vel;
+}
+/**
+ * @return IO level of QUADA pin.
+ */
+int CANTalon::GetPinStateQuadA()
+{
+ int retval;
+ CTR_Code status = m_impl->GetQuadApin(retval);
+ if(status != CTR_OKAY) {
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ }
+ return retval;
+}
+/**
+ * @return IO level of QUADB pin.
+ */
+int CANTalon::GetPinStateQuadB()
+{
+ int retval;
+ CTR_Code status = m_impl->GetQuadBpin(retval);
+ if(status != CTR_OKAY) {
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ }
+ return retval;
+}
+/**
+ * @return IO level of QUAD Index pin.
+ */
+int CANTalon::GetPinStateQuadIdx()
+{
+ int retval;
+ CTR_Code status = m_impl->GetQuadIdxpin(retval);
+ if(status != CTR_OKAY) {
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ }
+ return retval;
+}
+/**
+ * @return '1' iff forward limit switch is closed, 0 iff switch is open.
+ * This function works regardless if limit switch feature is enabled.
+ */
+int CANTalon::IsFwdLimitSwitchClosed()
+{
+ int retval;
+ CTR_Code status = m_impl->GetLimitSwitchClosedFor(retval); /* rename this func, '1' => open, '0' => closed */
+ if(status != CTR_OKAY) {
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ }
+ return retval ? 0 : 1;
+}
+/**
+ * @return '1' iff reverse limit switch is closed, 0 iff switch is open.
+ * This function works regardless if limit switch feature is enabled.
+ */
+int CANTalon::IsRevLimitSwitchClosed()
+{
+ int retval;
+ CTR_Code status = m_impl->GetLimitSwitchClosedRev(retval); /* rename this func, '1' => open, '0' => closed */
+ if(status != CTR_OKAY) {
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ }
+ return retval ? 0 : 1;
+}
+/*
+ * Simple accessor for tracked rise eventso index pin.
+ * @return number of rising edges on idx pin.
+ */
+int CANTalon::GetNumberOfQuadIdxRises()
+{
+ int rises;
+ CTR_Code status = m_impl->GetEncIndexRiseEvents(rises); /* rename this func, '1' => open, '0' => closed */
+ if(status != CTR_OKAY) {
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ }
+ return rises;
+}
+/*
+ * @param rises integral value to set into index-rises register. Great way to zero the index count.
+ */
+void CANTalon::SetNumberOfQuadIdxRises(int rises)
+{
+ CTR_Code status = m_impl->SetParam(CanTalonSRX::eEncIndexRiseEvents, rises); /* rename this func, '1' => open, '0' => closed */
+ if(status != CTR_OKAY) {
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ }
+}
+/**
+ * TODO documentation (see CANJaguar.cpp)
+ */
+bool CANTalon::GetForwardLimitOK()
+{
+ int limSwit=0;
+ int softLim=0;
+ CTR_Code status;
+ status = m_impl->GetFault_ForSoftLim(softLim);
+ if(status != CTR_OKAY) {
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ }
+ status = m_impl->GetFault_ForLim(limSwit);
+ if(status != CTR_OKAY) {
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ }
+ /* If either fault is asserted, signal caller we are disabled (with false?) */
+ return (softLim | limSwit) ? false : true;
+}
+
+/**
+ * TODO documentation (see CANJaguar.cpp)
+ */
+bool CANTalon::GetReverseLimitOK()
+{
+ int limSwit=0;
+ int softLim=0;
+ CTR_Code status;
+ status = m_impl->GetFault_RevSoftLim(softLim);
+ if(status != CTR_OKAY) {
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ }
+ status = m_impl->GetFault_RevLim(limSwit);
+ if(status != CTR_OKAY) {
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ }
+ /* If either fault is asserted, signal caller we are disabled (with false?) */
+ return (softLim | limSwit) ? false : true;
+}
+
+/**
+ * TODO documentation (see CANJaguar.cpp)
+ */
+uint16_t CANTalon::GetFaults()
+{
+ uint16_t retval = 0;
+ int val;
+ CTR_Code status;
+
+ /* temperature */
+ val = 0;
+ status = m_impl->GetFault_OverTemp(val);
+ if(status != CTR_OKAY)
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ retval |= (val) ? CANSpeedController::kTemperatureFault : 0;
+
+ /* voltage */
+ val = 0;
+ status = m_impl->GetFault_UnderVoltage(val);
+ if(status != CTR_OKAY)
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ retval |= (val) ? CANSpeedController::kBusVoltageFault : 0;
+
+ /* fwd-limit-switch */
+ val = 0;
+ status = m_impl->GetFault_ForLim(val);
+ if(status != CTR_OKAY)
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ retval |= (val) ? CANSpeedController::kFwdLimitSwitch : 0;
+
+ /* rev-limit-switch */
+ val = 0;
+ status = m_impl->GetFault_RevLim(val);
+ if(status != CTR_OKAY)
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ retval |= (val) ? CANSpeedController::kRevLimitSwitch : 0;
+
+ /* fwd-soft-limit */
+ val = 0;
+ status = m_impl->GetFault_ForSoftLim(val);
+ if(status != CTR_OKAY)
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ retval |= (val) ? CANSpeedController::kFwdSoftLimit : 0;
+
+ /* rev-soft-limit */
+ val = 0;
+ status = m_impl->GetFault_RevSoftLim(val);
+ if(status != CTR_OKAY)
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ retval |= (val) ? CANSpeedController::kRevSoftLimit : 0;
+
+ return retval;
+}
+uint16_t CANTalon::GetStickyFaults()
+{
+ uint16_t retval = 0;
+ int val;
+ CTR_Code status;
+
+ /* temperature */
+ val = 0;
+ status = m_impl->GetStckyFault_OverTemp(val);
+ if(status != CTR_OKAY)
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ retval |= (val) ? CANSpeedController::kTemperatureFault : 0;
+
+ /* voltage */
+ val = 0;
+ status = m_impl->GetStckyFault_UnderVoltage(val);
+ if(status != CTR_OKAY)
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ retval |= (val) ? CANSpeedController::kBusVoltageFault : 0;
+
+ /* fwd-limit-switch */
+ val = 0;
+ status = m_impl->GetStckyFault_ForLim(val);
+ if(status != CTR_OKAY)
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ retval |= (val) ? CANSpeedController::kFwdLimitSwitch : 0;
+
+ /* rev-limit-switch */
+ val = 0;
+ status = m_impl->GetStckyFault_RevLim(val);
+ if(status != CTR_OKAY)
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ retval |= (val) ? CANSpeedController::kRevLimitSwitch : 0;
+
+ /* fwd-soft-limit */
+ val = 0;
+ status = m_impl->GetStckyFault_ForSoftLim(val);
+ if(status != CTR_OKAY)
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ retval |= (val) ? CANSpeedController::kFwdSoftLimit : 0;
+
+ /* rev-soft-limit */
+ val = 0;
+ status = m_impl->GetStckyFault_RevSoftLim(val);
+ if(status != CTR_OKAY)
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ retval |= (val) ? CANSpeedController::kRevSoftLimit : 0;
+
+ return retval;
+}
+void CANTalon::ClearStickyFaults()
+{
+ CTR_Code status = m_impl->ClearStickyFaults();
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+}
+
+/**
+ * Set the maximum voltage change rate. This ramp rate is in affect regardless of which control mode
+ * the TALON is in.
+ *
+ * When in PercentVbus or Voltage output mode, the rate at which the voltage changes can
+ * be limited to reduce current spikes. Set this to 0.0 to disable rate limiting.
+ *
+ * @param rampRate The maximum rate of voltage change in Percent Voltage mode in V/s.
+ */
+void CANTalon::SetVoltageRampRate(double rampRate)
+{
+ /* Caller is expressing ramp as Voltage per sec, assuming 12V is full.
+ Talon's throttle ramp is in dThrot/d10ms. 1023 is full fwd, -1023 is full rev. */
+ double rampRatedThrotPer10ms = (rampRate*1023.0/12.0) / 100;
+ CTR_Code status = m_impl->SetRampThrottle((int)rampRatedThrotPer10ms);
+ if(status != CTR_OKAY) {
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ }
+}
+/**
+ * Sets a voltage change rate that applies only when a close loop contorl mode is enabled.
+ * This allows close loop specific ramp behavior.
+ *
+ * @param rampRate The maximum rate of voltage change in Percent Voltage mode in V/s.
+ */
+void CANTalon::SetCloseLoopRampRate(double rampRate)
+{
+ CTR_Code status = m_impl->SetCloseLoopRampRate(m_profile,rampRate * 1023.0 / 12.0 / 1000.0);
+ if(status != CTR_OKAY) {
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ }
+}
+
+/**
+ * @return The version of the firmware running on the Talon
+ */
+uint32_t CANTalon::GetFirmwareVersion()
+{
+ int firmwareVersion;
+ CTR_Code status = m_impl->RequestParam(CanTalonSRX::eFirmVers);
+ if(status != CTR_OKAY) {
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ }
+ usleep(kDelayForSolicitedSignalsUs);
+ status = m_impl->GetParamResponseInt32(CanTalonSRX::eFirmVers,firmwareVersion);
+ if(status != CTR_OKAY) {
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ }
+
+ /* only sent once on boot */
+ //CTR_Code status = m_impl->GetFirmVers(firmwareVersion);
+ //if(status != CTR_OKAY) {
+ // wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ //}
+
+ return firmwareVersion;
+}
+/**
+ * @return The accumulator for I gain.
+ */
+int CANTalon::GetIaccum()
+{
+ CTR_Code status = m_impl->RequestParam(CanTalonSRX::ePidIaccum);
+ if(status != CTR_OKAY) {
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ }
+ usleep(kDelayForSolicitedSignalsUs); /* small yield for getting response */
+ int iaccum;
+ status = m_impl->GetParamResponseInt32(CanTalonSRX::ePidIaccum,iaccum);
+ if(status != CTR_OKAY) {
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ }
+ return iaccum;
+}
+/**
+ * Clear the accumulator for I gain.
+ */
+void CANTalon::ClearIaccum()
+{
+ CTR_Code status = m_impl->SetParam(CanTalonSRX::ePidIaccum, 0);
+ if(status != CTR_OKAY) {
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ }
+}
+
+/**
+ * TODO documentation (see CANJaguar.cpp)
+ */
+void CANTalon::ConfigNeutralMode(NeutralMode mode)
+{
+ CTR_Code status;
+ switch(mode){
+ default:
+ case kNeutralMode_Jumper: /* use default setting in flash based on webdash/BrakeCal button selection */
+ status = m_impl->SetOverrideBrakeType(CanTalonSRX::kBrakeOverride_UseDefaultsFromFlash);
+ break;
+ case kNeutralMode_Brake:
+ status = m_impl->SetOverrideBrakeType(CanTalonSRX::kBrakeOverride_OverrideBrake);
+ break;
+ case kNeutralMode_Coast:
+ status = m_impl->SetOverrideBrakeType(CanTalonSRX::kBrakeOverride_OverrideCoast);
+ break;
+ }
+ if(status != CTR_OKAY) {
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ }
+}
+/**
+ * @return nonzero if brake is enabled during neutral. Zero if coast is enabled during neutral.
+ */
+int CANTalon::GetBrakeEnableDuringNeutral()
+{
+ int brakeEn = 0;
+ CTR_Code status = m_impl->GetBrakeIsEnabled(brakeEn);
+ if(status != CTR_OKAY) {
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ }
+ return brakeEn;
+}
+/**
+ * @deprecated not implemented
+ */
+void CANTalon::ConfigEncoderCodesPerRev(uint16_t codesPerRev)
+{
+ /* TALON SRX does not scale units, they are raw from the sensor. Unit scaling can be done in API or by caller */
+}
+
+/**
+ * @deprecated not implemented
+ */
+void CANTalon::ConfigPotentiometerTurns(uint16_t turns)
+{
+ /* TALON SRX does not scale units, they are raw from the sensor. Unit scaling can be done in API or by caller */
+}
+
+/**
+ * @deprecated not implemented
+ */
+void CANTalon::ConfigSoftPositionLimits(double forwardLimitPosition, double reverseLimitPosition)
+{
+ ConfigLimitMode(kLimitMode_SoftPositionLimits);
+ ConfigForwardLimit(forwardLimitPosition);
+ ConfigReverseLimit(reverseLimitPosition);
+}
+
+/**
+ * TODO documentation (see CANJaguar.cpp)
+ */
+void CANTalon::DisableSoftPositionLimits()
+{
+ ConfigLimitMode(kLimitMode_SwitchInputsOnly);
+}
+
+/**
+ * TODO documentation (see CANJaguar.cpp)
+ * Configures the soft limit enable (wear leveled persistent memory).
+ * Also sets the limit switch overrides.
+ */
+void CANTalon::ConfigLimitMode(LimitMode mode)
+{
+ CTR_Code status;
+ switch(mode){
+ case kLimitMode_SwitchInputsOnly: /** Only use switches for limits */
+ /* turn OFF both limits. SRX has individual enables and polarity for each limit switch.*/
+ status = m_impl->SetForwardSoftEnable(false);
+ if(status != CTR_OKAY) {
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ }
+ status = m_impl->SetReverseSoftEnable(false);
+ if(status != CTR_OKAY) {
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ }
+ /* override enable the limit switches, this circumvents the webdash */
+ status = m_impl->SetOverrideLimitSwitchEn(CanTalonSRX::kLimitSwitchOverride_EnableFwd_EnableRev);
+ if(status != CTR_OKAY) {
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ }
+ break;
+ case kLimitMode_SoftPositionLimits: /** Use both switches and soft limits */
+ /* turn on both limits. SRX has individual enables and polarity for each limit switch.*/
+ status = m_impl->SetForwardSoftEnable(true);
+ if(status != CTR_OKAY) {
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ }
+ status = m_impl->SetReverseSoftEnable(true);
+ if(status != CTR_OKAY) {
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ }
+ /* override enable the limit switches, this circumvents the webdash */
+ status = m_impl->SetOverrideLimitSwitchEn(CanTalonSRX::kLimitSwitchOverride_EnableFwd_EnableRev);
+ if(status != CTR_OKAY) {
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ }
+ break;
+
+ case kLimitMode_SrxDisableSwitchInputs: /** disable both limit switches and soft limits */
+ /* turn on both limits. SRX has individual enables and polarity for each limit switch.*/
+ status = m_impl->SetForwardSoftEnable(false);
+ if(status != CTR_OKAY) {
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ }
+ status = m_impl->SetReverseSoftEnable(false);
+ if(status != CTR_OKAY) {
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ }
+ /* override enable the limit switches, this circumvents the webdash */
+ status = m_impl->SetOverrideLimitSwitchEn(CanTalonSRX::kLimitSwitchOverride_DisableFwd_DisableRev);
+ if(status != CTR_OKAY) {
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ }
+ break;
+ }
+}
+
+/**
+ * TODO documentation (see CANJaguar.cpp)
+ */
+void CANTalon::ConfigForwardLimit(double forwardLimitPosition)
+{
+ CTR_Code status;
+ status = m_impl->SetForwardSoftLimit(forwardLimitPosition);
+ if(status != CTR_OKAY) {
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ }
+}
+/**
+ * Change the fwd limit switch setting to normally open or closed.
+ * Talon will disable momentarilly if the Talon's current setting
+ * is dissimilar to the caller's requested setting.
+ *
+ * Since Talon saves setting to flash this should only affect
+ * a given Talon initially during robot install.
+ *
+ * @param normallyOpen true for normally open. false for normally closed.
+ */
+void CANTalon::ConfigFwdLimitSwitchNormallyOpen(bool normallyOpen)
+{
+ CTR_Code status = m_impl->SetParam(CanTalonSRX::eOnBoot_LimitSwitch_Forward_NormallyClosed, normallyOpen ? 0 : 1);
+ if(status != CTR_OKAY) {
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ }
+}
+/**
+ * Change the rev limit switch setting to normally open or closed.
+ * Talon will disable momentarilly if the Talon's current setting
+ * is dissimilar to the caller's requested setting.
+ *
+ * Since Talon saves setting to flash this should only affect
+ * a given Talon initially during robot install.
+ *
+ * @param normallyOpen true for normally open. false for normally closed.
+ */
+void CANTalon::ConfigRevLimitSwitchNormallyOpen(bool normallyOpen)
+{
+ CTR_Code status = m_impl->SetParam(CanTalonSRX::eOnBoot_LimitSwitch_Reverse_NormallyClosed, normallyOpen ? 0 : 1);
+ if(status != CTR_OKAY) {
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ }
+}
+/**
+ * TODO documentation (see CANJaguar.cpp)
+ */
+void CANTalon::ConfigReverseLimit(double reverseLimitPosition)
+{
+ CTR_Code status;
+ status = m_impl->SetReverseSoftLimit(reverseLimitPosition);
+ if(status != CTR_OKAY) {
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ }
+}
+
+/**
+ * TODO documentation (see CANJaguar.cpp)
+ */
+void CANTalon::ConfigMaxOutputVoltage(double voltage)
+{
+ /* SRX does not support max output */
+ wpi_setWPIErrorWithContext(IncompatibleMode, "MaxOutputVoltage not supported.");
+}
+
+/**
+ * TODO documentation (see CANJaguar.cpp)
+ */
+void CANTalon::ConfigFaultTime(float faultTime)
+{
+ /* SRX does not have fault time. SRX motor drive is only disabled for soft limits and limit-switch faults. */
+ wpi_setWPIErrorWithContext(IncompatibleMode, "Fault Time not supported.");
+}
+
+/**
+ * Fixup the sendMode so Set() serializes the correct demand value.
+ * Also fills the modeSelecet in the control frame to disabled.
+ * @param mode Control mode to ultimately enter once user calls Set().
+ * @see Set()
+ */
+void CANTalon::ApplyControlMode(CANSpeedController::ControlMode mode)
+{
+ m_controlMode = mode;
+ switch (mode) {
+ case kPercentVbus:
+ m_sendMode = kThrottle;
+ break;
+ case kCurrent:
+ m_sendMode = kCurrentMode;
+ break;
+ case kSpeed:
+ m_sendMode = kSpeedMode;
+ break;
+ case kPosition:
+ m_sendMode = kPositionMode;
+ break;
+ case kVoltage:
+ m_sendMode = kVoltageMode;
+ break;
+ case kFollower:
+ m_sendMode = kFollowerMode;
+ break;
+ }
+ // Keep the talon disabled until Set() is called.
+ CTR_Code status = m_impl->SetModeSelect((int)kDisabled);
+ if(status != CTR_OKAY) {
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ }
+}
+/**
+ * TODO documentation (see CANJaguar.cpp)
+ */
+void CANTalon::SetControlMode(CANSpeedController::ControlMode mode)
+{
+ if(m_controlMode == mode){
+ /* we already are in this mode, don't perform disable workaround */
+ }else{
+ ApplyControlMode(mode);
+ }
+}
+
+/**
+ * TODO documentation (see CANJaguar.cpp)
+ */
+CANSpeedController::ControlMode CANTalon::GetControlMode()
+{
+ return m_controlMode;
+}
+
+void CANTalon::SetExpiration(float timeout)
+{
+ m_safetyHelper->SetExpiration(timeout);
+}
+
+float CANTalon::GetExpiration()
+{
+ return m_safetyHelper->GetExpiration();
+}
+
+bool CANTalon::IsAlive()
+{
+ return m_safetyHelper->IsAlive();
+}
+
+bool CANTalon::IsSafetyEnabled()
+{
+ return m_safetyHelper->IsSafetyEnabled();
+}
+
+void CANTalon::SetSafetyEnabled(bool enabled)
+{
+ m_safetyHelper->SetSafetyEnabled(enabled);
+}
+
+void CANTalon::GetDescription(char *desc)
+{
+ sprintf(desc, "CANTalon ID %d", m_deviceNumber);
+}
+
+/**
+ * Common interface for stopping the motor
+ * Part of the MotorSafety interface
+ *
+ * @deprecated Call Disable instead.
+*/
+void CANTalon::StopMotor()
+{
+ Disable();
+}
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/Compressor.cpp b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/Compressor.cpp
new file mode 100644
index 0000000..430eab1
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/Compressor.cpp
@@ -0,0 +1,261 @@
+/*
+ * Compressor.cpp
+ */
+
+#include "Compressor.h"
+#include "WPIErrors.h"
+
+void Compressor::InitCompressor(uint8_t pcmID) {
+ m_pcm_pointer = initializeCompressor(pcmID);
+
+ SetClosedLoopControl(true);
+}
+
+/**
+ * Constructor
+ *
+ * Uses the default PCM ID (0)
+ */
+Compressor::Compressor() {
+ InitCompressor(GetDefaultSolenoidModule());
+}
+
+/**
+ * Constructor
+ *
+ * @param module The PCM ID to use (0-62)
+ */
+Compressor::Compressor(uint8_t pcmID) {
+ InitCompressor(pcmID);
+}
+
+Compressor::~Compressor() {
+
+}
+
+/**
+ * Starts closed-loop control. Note that closed loop control is enabled by default.
+ */
+void Compressor::Start() {
+ SetClosedLoopControl(true);
+}
+
+/**
+ * Stops closed-loop control. Note that closed loop control is enabled by default.
+ */
+void Compressor::Stop() {
+ SetClosedLoopControl(false);
+}
+
+/**
+ * Check if compressor output is active
+ * @return true if the compressor is on
+ */
+bool Compressor::Enabled() {
+ int32_t status = 0;
+ bool value;
+
+ value = getCompressor(m_pcm_pointer, &status);
+
+ if(status) {
+ wpi_setWPIError(Timeout);
+ }
+
+ return value;
+}
+
+/**
+ * Check if the pressure switch is triggered
+ * @return true if pressure is low
+ */
+bool Compressor::GetPressureSwitchValue() {
+ int32_t status = 0;
+ bool value;
+
+ value = getPressureSwitch(m_pcm_pointer, &status);
+
+ if(status) {
+ wpi_setWPIError(Timeout);
+ }
+
+ return value;
+}
+
+
+/**
+ * Query how much current the compressor is drawing
+ * @return The current through the compressor, in amps
+ */
+float Compressor::GetCompressorCurrent() {
+ int32_t status = 0;
+ float value;
+
+ value = getCompressorCurrent(m_pcm_pointer, &status);
+
+ if(status) {
+ wpi_setWPIError(Timeout);
+ }
+
+ return value;
+}
+
+
+/**
+ * Enables or disables automatically turning the compressor on when the
+ * pressure is low.
+ * @param on Set to true to enable closed loop control of the compressor. False to disable.
+ */
+void Compressor::SetClosedLoopControl(bool on) {
+ int32_t status = 0;
+
+ setClosedLoopControl(m_pcm_pointer, on, &status);
+
+ if(status) {
+ wpi_setWPIError(Timeout);
+ }
+}
+
+/**
+ * Returns true if the compressor will automatically turn on when the
+ * pressure is low.
+ * @return True if closed loop control of the compressor is enabled. False if disabled.
+ */
+bool Compressor::GetClosedLoopControl() {
+ int32_t status = 0;
+ bool value;
+
+ value = getClosedLoopControl(m_pcm_pointer, &status);
+
+ if(status) {
+ wpi_setWPIError(Timeout);
+ }
+
+ return value;
+}
+
+/**
+ * Query if the compressor output has been disabled due to high current draw.
+ * @return true if PCM is in fault state : Compressor Drive is
+ * disabled due to compressor current being too high.
+ */
+bool Compressor::GetCompressorCurrentTooHighFault() {
+ int32_t status = 0;
+ bool value;
+
+ value = getCompressorCurrentTooHighFault(m_pcm_pointer, &status);
+
+ if(status) {
+ wpi_setWPIError(Timeout);
+ }
+
+ return value;
+}
+/**
+ * Query if the compressor output has been disabled due to high current draw (sticky).
+ * A sticky fault will not clear on device reboot, it must be cleared through code or the webdash.
+ * @return true if PCM sticky fault is set : Compressor Drive is
+ * disabled due to compressor current being too high.
+ */
+bool Compressor::GetCompressorCurrentTooHighStickyFault() {
+ int32_t status = 0;
+ bool value;
+
+ value = getCompressorCurrentTooHighStickyFault(m_pcm_pointer, &status);
+
+ if(status) {
+ wpi_setWPIError(Timeout);
+ }
+
+ return value;
+}
+/**
+ * Query if the compressor output has been disabled due to a short circuit (sticky).
+ * A sticky fault will not clear on device reboot, it must be cleared through code or the webdash.
+ * @return true if PCM sticky fault is set : Compressor output
+ * appears to be shorted.
+ */
+bool Compressor::GetCompressorShortedStickyFault() {
+ int32_t status = 0;
+ bool value;
+
+ value = getCompressorShortedStickyFault(m_pcm_pointer, &status);
+
+ if(status) {
+ wpi_setWPIError(Timeout);
+ }
+
+ return value;
+}
+/**
+ * Query if the compressor output has been disabled due to a short circuit.
+ * @return true if PCM is in fault state : Compressor output
+ * appears to be shorted.
+ */
+bool Compressor::GetCompressorShortedFault() {
+ int32_t status = 0;
+ bool value;
+
+ value = getCompressorShortedFault(m_pcm_pointer, &status);
+
+ if(status) {
+ wpi_setWPIError(Timeout);
+ }
+
+ return value;
+}
+/**
+ * Query if the compressor output does not appear to be wired (sticky).
+ * A sticky fault will not clear on device reboot, it must be cleared through code or the webdash.
+ * @return true if PCM sticky fault is set : Compressor does not
+ * appear to be wired, i.e. compressor is
+ * not drawing enough current.
+ */
+bool Compressor::GetCompressorNotConnectedStickyFault() {
+ int32_t status = 0;
+ bool value;
+
+ value = getCompressorNotConnectedStickyFault(m_pcm_pointer, &status);
+
+ if(status) {
+ wpi_setWPIError(Timeout);
+ }
+
+ return value;
+}
+/**
+ * Query if the compressor output does not appear to be wired.
+ * @return true if PCM is in fault state : Compressor does not
+ * appear to be wired, i.e. compressor is
+ * not drawing enough current.
+ */
+bool Compressor::GetCompressorNotConnectedFault() {
+ int32_t status = 0;
+ bool value;
+
+ value = getCompressorNotConnectedFault(m_pcm_pointer, &status);
+
+ if(status) {
+ wpi_setWPIError(Timeout);
+ }
+
+ return value;
+}
+/**
+ * Clear ALL sticky faults inside PCM that Compressor is wired to.
+ *
+ * If a sticky fault is set, then it will be persistently cleared. Compressor drive
+ * maybe momentarily disable while flags are being cleared. Care should be
+ * taken to not call this too frequently, otherwise normal compressor
+ * functionality may be prevented.
+ *
+ * If no sticky faults are set then this call will have no effect.
+ */
+void Compressor::ClearAllPCMStickyFaults() {
+ int32_t status = 0;
+
+ clearAllPCMStickyFaults(m_pcm_pointer, &status);
+
+ if(status) {
+ wpi_setWPIError(Timeout);
+ }
+}
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/ControllerPower.cpp b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/ControllerPower.cpp
new file mode 100644
index 0000000..157929b
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/ControllerPower.cpp
@@ -0,0 +1,170 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2011. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+
+#include "ControllerPower.h"
+
+#include <stdint.h>
+#include <HAL/Power.hpp>
+#include <HAL/HAL.hpp>
+#include "ErrorBase.h"
+
+/**
+ * Get the input voltage to the robot controller
+ * @return The controller input voltage value in Volts
+ */
+double ControllerPower::GetInputVoltage() {
+ int32_t status = 0;
+ double retVal = getVinVoltage(&status);
+ wpi_setGlobalErrorWithContext(status, getHALErrorMessage(status));
+ return retVal;
+}
+
+/**
+ * Get the input current to the robot controller
+ * @return The controller input current value in Amps
+ */
+double ControllerPower::GetInputCurrent() {
+ int32_t status = 0;
+ double retVal = getVinCurrent(&status);
+ wpi_setGlobalErrorWithContext(status, getHALErrorMessage(status));
+ return retVal;
+}
+
+/**
+ * Get the voltage of the 6V rail
+ * @return The controller 6V rail voltage value in Volts
+ */
+double ControllerPower::GetVoltage6V() {
+ int32_t status = 0;
+ double retVal = getUserVoltage6V(&status);
+ wpi_setGlobalErrorWithContext(status, getHALErrorMessage(status));
+ return retVal;
+}
+
+/**
+ * Get the current output of the 6V rail
+ * @return The controller 6V rail output current value in Amps
+ */
+double ControllerPower::GetCurrent6V() {
+ int32_t status = 0;
+ double retVal = getUserCurrent6V(&status);
+ wpi_setGlobalErrorWithContext(status, getHALErrorMessage(status));
+ return retVal;
+}
+
+/**
+ * Get the enabled state of the 6V rail. The rail may be disabled due to a controller
+ * brownout, a short circuit on the rail, or controller over-voltage
+ * @return The controller 6V rail enabled value. True for enabled.
+ */
+bool ControllerPower::GetEnabled6V() {
+ int32_t status = 0;
+ bool retVal = getUserActive6V(&status);
+ wpi_setGlobalErrorWithContext(status, getHALErrorMessage(status));
+ return retVal;
+}
+
+/**
+ * Get the count of the total current faults on the 6V rail since the controller has booted
+ * @return The number of faults.
+ */
+int ControllerPower::GetFaultCount6V() {
+ int32_t status = 0;
+ int retVal = getUserCurrentFaults6V(&status);
+ wpi_setGlobalErrorWithContext(status, getHALErrorMessage(status));
+ return retVal;
+}
+
+/**
+ * Get the voltage of the 5V rail
+ * @return The controller 5V rail voltage value in Volts
+ */
+double ControllerPower::GetVoltage5V() {
+ int32_t status = 0;
+ double retVal = getUserVoltage5V(&status);
+ wpi_setGlobalErrorWithContext(status, getHALErrorMessage(status));
+ return retVal;
+}
+
+/**
+ * Get the current output of the 5V rail
+ * @return The controller 5V rail output current value in Amps
+ */
+double ControllerPower::GetCurrent5V() {
+ int32_t status = 0;
+ double retVal = getUserCurrent5V(&status);
+ wpi_setGlobalErrorWithContext(status, getHALErrorMessage(status));
+ return retVal;
+}
+
+/**
+ * Get the enabled state of the 5V rail. The rail may be disabled due to a controller
+ * brownout, a short circuit on the rail, or controller over-voltage
+ * @return The controller 5V rail enabled value. True for enabled.
+ */
+bool ControllerPower::GetEnabled5V() {
+ int32_t status = 0;
+ bool retVal = getUserActive5V(&status);
+ wpi_setGlobalErrorWithContext(status, getHALErrorMessage(status));
+ return retVal;
+}
+
+/**
+ * Get the count of the total current faults on the 5V rail since the controller has booted
+ * @return The number of faults
+ */
+int ControllerPower::GetFaultCount5V() {
+ int32_t status = 0;
+ int retVal = getUserCurrentFaults5V(&status);
+ wpi_setGlobalErrorWithContext(status, getHALErrorMessage(status));
+ return retVal;
+}
+
+/**
+ * Get the voltage of the 3.3V rail
+ * @return The controller 3.3V rail voltage value in Volts
+ */
+double ControllerPower::GetVoltage3V3() {
+ int32_t status = 0;
+ double retVal = getUserVoltage3V3(&status);
+ wpi_setGlobalErrorWithContext(status, getHALErrorMessage(status));
+ return retVal;
+}
+
+/**
+ * Get the current output of the 3.3V rail
+ * @return The controller 3.3V rail output current value in Amps
+ */
+double ControllerPower::GetCurrent3V3() {
+ int32_t status = 0;
+ double retVal = getUserCurrent3V3(&status);
+ wpi_setGlobalErrorWithContext(status, getHALErrorMessage(status));
+ return retVal;
+}
+
+
+/**
+ * Get the enabled state of the 3.3V rail. The rail may be disabled due to a controller
+ * brownout, a short circuit on the rail, or controller over-voltage
+ * @return The controller 3.3V rail enabled value. True for enabled.
+ */
+bool ControllerPower::GetEnabled3V3() {
+ int32_t status = 0;
+ bool retVal = getUserActive3V3(&status);
+ wpi_setGlobalErrorWithContext(status, getHALErrorMessage(status));
+ return retVal;
+}
+
+/**
+ * Get the count of the total current faults on the 3.3V rail since the controller has booted
+ * @return The number of faults
+ */
+int ControllerPower::GetFaultCount3V3() {
+ int32_t status = 0;
+ int retVal = getUserCurrentFaults3V3(&status);
+ wpi_setGlobalErrorWithContext(status, getHALErrorMessage(status));
+ return retVal;
+}
\ No newline at end of file
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/Counter.cpp b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/Counter.cpp
new file mode 100644
index 0000000..964b774
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/Counter.cpp
@@ -0,0 +1,619 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+
+#include "Counter.h"
+#include "AnalogTrigger.h"
+#include "DigitalInput.h"
+//#include "NetworkCommunication/UsageReporting.h"
+#include "Resource.h"
+#include "WPIErrors.h"
+
+/**
+ * Create an instance of a counter object.
+ * This creates a ChipObject counter and initializes status variables appropriately
+ *
+ * The counter will start counting immediately.
+ * @param mode The counter mode
+ */
+void Counter::InitCounter(Mode mode)
+{
+ int32_t status = 0;
+ m_index = 0;
+ m_counter = initializeCounter(mode, &m_index, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+
+ m_upSource = NULL;
+ m_downSource = NULL;
+ m_allocatedUpSource = false;
+ m_allocatedDownSource = false;
+
+ SetMaxPeriod(.5);
+
+ HALReport(HALUsageReporting::kResourceType_Counter, m_index, mode);
+}
+
+/**
+ * Create an instance of a counter where no sources are selected.
+ * They all must be selected by calling functions to specify the upsource and the downsource
+ * independently.
+ *
+ * The counter will start counting immediately.
+ */
+Counter::Counter() :
+ m_upSource(NULL),
+ m_downSource(NULL),
+ m_counter(NULL)
+{
+ InitCounter();
+}
+
+/**
+ * Create an instance of a counter from a Digital Source (such as a Digital Input).
+ * This is used if an existing digital input is to be shared by multiple other objects such
+ * as encoders or if the Digital Source is not a Digital Input channel (such as an Analog Trigger).
+ *
+ * The counter will start counting immediately.
+ * @param source A pointer to the existing DigitalSource object. It will be set as the Up Source.
+ */
+Counter::Counter(DigitalSource *source) :
+ m_upSource(NULL),
+ m_downSource(NULL),
+ m_counter(NULL)
+{
+ InitCounter();
+ SetUpSource(source);
+ ClearDownSource();
+}
+
+/**
+ * Create an instance of a counter from a Digital Source (such as a Digital Input).
+ * This is used if an existing digital input is to be shared by multiple other objects such
+ * as encoders or if the Digital Source is not a Digital Input channel (such as an Analog Trigger).
+ *
+ * The counter will start counting immediately.
+ * @param source A reference to the existing DigitalSource object. It will be set as the Up Source.
+ */
+Counter::Counter(DigitalSource &source) :
+ m_upSource(NULL),
+ m_downSource(NULL),
+ m_counter(NULL)
+{
+ InitCounter();
+ SetUpSource(&source);
+ ClearDownSource();
+}
+
+/**
+ * Create an instance of a Counter object.
+ * Create an up-Counter instance given a channel.
+ *
+ * The counter will start counting immediately.
+ * @param channel The DIO channel to use as the up source. 0-9 are on-board, 10-25 are on the MXP
+ */
+Counter::Counter(int32_t channel) :
+ m_upSource(NULL),
+ m_downSource(NULL),
+ m_counter(NULL)
+{
+ InitCounter();
+ SetUpSource(channel);
+ ClearDownSource();
+}
+
+/**
+ * Create an instance of a Counter object.
+ * Create an instance of a simple up-Counter given an analog trigger.
+ * Use the trigger state output from the analog trigger.
+ *
+ * The counter will start counting immediately.
+ * @param trigger The pointer to the existing AnalogTrigger object.
+ */
+Counter::Counter(AnalogTrigger *trigger) :
+ m_upSource(NULL),
+ m_downSource(NULL),
+ m_counter(NULL)
+{
+ InitCounter();
+ SetUpSource(trigger->CreateOutput(kState));
+ ClearDownSource();
+ m_allocatedUpSource = true;
+}
+
+/**
+ * Create an instance of a Counter object.
+ * Create an instance of a simple up-Counter given an analog trigger.
+ * Use the trigger state output from the analog trigger.
+ *
+ * The counter will start counting immediately.
+ * @param trigger The reference to the existing AnalogTrigger object.
+ */
+Counter::Counter(AnalogTrigger &trigger) :
+ m_upSource(NULL),
+ m_downSource(NULL),
+ m_counter(NULL)
+{
+ InitCounter();
+ SetUpSource(trigger.CreateOutput(kState));
+ ClearDownSource();
+ m_allocatedUpSource = true;
+}
+
+/**
+ * Create an instance of a Counter object.
+ * Creates a full up-down counter given two Digital Sources
+ * @param encodingType The quadrature decoding mode (1x or 2x)
+ * @param upSource The pointer to the DigitalSource to set as the up source
+ * @param downSource The pointer to the DigitalSource to set as the down source
+ * @param inverted True to invert the output (reverse the direction)
+ */
+
+Counter::Counter(EncodingType encodingType, DigitalSource *upSource, DigitalSource *downSource, bool inverted) :
+ m_upSource(NULL),
+ m_downSource(NULL),
+ m_counter(NULL)
+{
+ if (encodingType != k1X && encodingType != k2X)
+ {
+ wpi_setWPIErrorWithContext(ParameterOutOfRange, "Counter only supports 1X and 2X quadrature decoding.");
+ return;
+ }
+ InitCounter(kExternalDirection);
+ SetUpSource(upSource);
+ SetDownSource(downSource);
+ int32_t status = 0;
+
+ if (encodingType == k1X)
+ {
+ SetUpSourceEdge(true, false);
+ setCounterAverageSize(m_counter, 1, &status);
+ }
+ else
+ {
+ SetUpSourceEdge(true, true);
+ setCounterAverageSize(m_counter, 2, &status);
+ }
+
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ SetDownSourceEdge(inverted, true);
+}
+
+/**
+ * Delete the Counter object.
+ */
+Counter::~Counter()
+{
+ SetUpdateWhenEmpty(true);
+ if (m_allocatedUpSource)
+ {
+ delete m_upSource;
+ m_upSource = NULL;
+ }
+ if (m_allocatedDownSource)
+ {
+ delete m_downSource;
+ m_downSource = NULL;
+ }
+
+ int32_t status = 0;
+ freeCounter(m_counter, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ m_counter = NULL;
+}
+
+/**
+ * Set the upsource for the counter as a digital input channel.
+ * @param channel The DIO channel to use as the up source. 0-9 are on-board, 10-25 are on the MXP
+ */
+void Counter::SetUpSource(int32_t channel)
+{
+ if (StatusIsFatal()) return;
+ SetUpSource(new DigitalInput(channel));
+ m_allocatedUpSource = true;
+}
+
+/**
+ * Set the up counting source to be an analog trigger.
+ * @param analogTrigger The analog trigger object that is used for the Up Source
+ * @param triggerType The analog trigger output that will trigger the counter.
+ */
+void Counter::SetUpSource(AnalogTrigger *analogTrigger, AnalogTriggerType triggerType)
+{
+ if (StatusIsFatal()) return;
+ SetUpSource(analogTrigger->CreateOutput(triggerType));
+ m_allocatedUpSource = true;
+}
+
+/**
+ * Set the up counting source to be an analog trigger.
+ * @param analogTrigger The analog trigger object that is used for the Up Source
+ * @param triggerType The analog trigger output that will trigger the counter.
+ */
+void Counter::SetUpSource(AnalogTrigger &analogTrigger, AnalogTriggerType triggerType)
+{
+ SetUpSource(&analogTrigger, triggerType);
+}
+
+/**
+ * Set the source object that causes the counter to count up.
+ * Set the up counting DigitalSource.
+ * @param source Pointer to the DigitalSource object to set as the up source
+ */
+void Counter::SetUpSource(DigitalSource *source)
+{
+ if (StatusIsFatal()) return;
+ if (m_allocatedUpSource)
+ {
+ delete m_upSource;
+ m_upSource = NULL;
+ m_allocatedUpSource = false;
+ }
+ m_upSource = source;
+ if (m_upSource->StatusIsFatal())
+ {
+ CloneError(m_upSource);
+ }
+ else
+ {
+ int32_t status = 0;
+ setCounterUpSource(m_counter, source->GetChannelForRouting(),
+ source->GetAnalogTriggerForRouting(), &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ }
+}
+
+/**
+ * Set the source object that causes the counter to count up.
+ * Set the up counting DigitalSource.
+ * @param source Reference to the DigitalSource object to set as the up source
+ */
+void Counter::SetUpSource(DigitalSource &source)
+{
+ SetUpSource(&source);
+}
+
+/**
+ * Set the edge sensitivity on an up counting source.
+ * Set the up source to either detect rising edges or falling edges or both.
+ * @param risingEdge True to trigger on rising edges
+ * @param fallingEdge True to trigger on falling edges
+ */
+void Counter::SetUpSourceEdge(bool risingEdge, bool fallingEdge)
+{
+ if (StatusIsFatal()) return;
+ if (m_upSource == NULL)
+ {
+ wpi_setWPIErrorWithContext(NullParameter, "Must set non-NULL UpSource before setting UpSourceEdge");
+ }
+ int32_t status = 0;
+ setCounterUpSourceEdge(m_counter, risingEdge, fallingEdge, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+}
+
+/**
+ * Disable the up counting source to the counter.
+ */
+void Counter::ClearUpSource()
+{
+ if (StatusIsFatal()) return;
+ if (m_allocatedUpSource)
+ {
+ delete m_upSource;
+ m_upSource = NULL;
+ m_allocatedUpSource = false;
+ }
+ int32_t status = 0;
+ clearCounterUpSource(m_counter, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+}
+
+/**
+ * Set the down counting source to be a digital input channel.
+ * @param channel The DIO channel to use as the up source. 0-9 are on-board, 10-25 are on the MXP
+ */
+void Counter::SetDownSource(int32_t channel)
+{
+ if (StatusIsFatal()) return;
+ SetDownSource(new DigitalInput(channel));
+ m_allocatedDownSource = true;
+}
+
+/**
+ * Set the down counting source to be an analog trigger.
+ * @param analogTrigger The analog trigger object that is used for the Down Source
+ * @param triggerType The analog trigger output that will trigger the counter.
+ */
+void Counter::SetDownSource(AnalogTrigger *analogTrigger, AnalogTriggerType triggerType)
+{
+ if (StatusIsFatal()) return;
+ SetDownSource(analogTrigger->CreateOutput(triggerType));
+ m_allocatedDownSource = true;
+}
+
+/**
+ * Set the down counting source to be an analog trigger.
+ * @param analogTrigger The analog trigger object that is used for the Down Source
+ * @param triggerType The analog trigger output that will trigger the counter.
+ */
+void Counter::SetDownSource(AnalogTrigger &analogTrigger, AnalogTriggerType triggerType)
+{
+ SetDownSource(&analogTrigger, triggerType);
+}
+
+/**
+ * Set the source object that causes the counter to count down.
+ * Set the down counting DigitalSource.
+ * @param source Pointer to the DigitalSource object to set as the down source
+ */
+void Counter::SetDownSource(DigitalSource *source)
+{
+ if (StatusIsFatal()) return;
+ if (m_allocatedDownSource)
+ {
+ delete m_downSource;
+ m_downSource = NULL;
+ m_allocatedDownSource = false;
+ }
+ m_downSource = source;
+ if (m_downSource->StatusIsFatal())
+ {
+ CloneError(m_downSource);
+ }
+ else
+ {
+ int32_t status = 0;
+ setCounterDownSource(m_counter, source->GetChannelForRouting(),
+ source->GetAnalogTriggerForRouting(), &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ }
+}
+
+/**
+ * Set the source object that causes the counter to count down.
+ * Set the down counting DigitalSource.
+ * @param source Reference to the DigitalSource object to set as the down source
+ */
+void Counter::SetDownSource(DigitalSource &source)
+{
+ SetDownSource(&source);
+}
+
+/**
+ * Set the edge sensitivity on a down counting source.
+ * Set the down source to either detect rising edges or falling edges.
+ * @param risingEdge True to trigger on rising edges
+ * @param fallingEdge True to trigger on falling edges
+ */
+void Counter::SetDownSourceEdge(bool risingEdge, bool fallingEdge)
+{
+ if (StatusIsFatal()) return;
+ if (m_downSource == NULL)
+ {
+ wpi_setWPIErrorWithContext(NullParameter, "Must set non-NULL DownSource before setting DownSourceEdge");
+ }
+ int32_t status = 0;
+ setCounterDownSourceEdge(m_counter, risingEdge, fallingEdge, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+}
+
+/**
+ * Disable the down counting source to the counter.
+ */
+void Counter::ClearDownSource()
+{
+ if (StatusIsFatal()) return;
+ if (m_allocatedDownSource)
+ {
+ delete m_downSource;
+ m_downSource = NULL;
+ m_allocatedDownSource = false;
+ }
+ int32_t status = 0;
+ clearCounterDownSource(m_counter, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+}
+
+/**
+ * Set standard up / down counting mode on this counter.
+ * Up and down counts are sourced independently from two inputs.
+ */
+void Counter::SetUpDownCounterMode()
+{
+ if (StatusIsFatal()) return;
+ int32_t status = 0;
+ setCounterUpDownMode(m_counter, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+}
+
+/**
+ * Set external direction mode on this counter.
+ * Counts are sourced on the Up counter input.
+ * The Down counter input represents the direction to count.
+ */
+void Counter::SetExternalDirectionMode()
+{
+ if (StatusIsFatal()) return;
+ int32_t status = 0;
+ setCounterExternalDirectionMode(m_counter, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+}
+
+/**
+ * Set Semi-period mode on this counter.
+ * Counts up on both rising and falling edges.
+ */
+void Counter::SetSemiPeriodMode(bool highSemiPeriod)
+{
+ if (StatusIsFatal()) return;
+ int32_t status = 0;
+ setCounterSemiPeriodMode(m_counter, highSemiPeriod, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+}
+
+/**
+ * Configure the counter to count in up or down based on the length of the input pulse.
+ * This mode is most useful for direction sensitive gear tooth sensors.
+ * @param threshold The pulse length beyond which the counter counts the opposite direction. Units are seconds.
+ */
+void Counter::SetPulseLengthMode(float threshold)
+{
+ if (StatusIsFatal()) return;
+ int32_t status = 0;
+ setCounterPulseLengthMode(m_counter, threshold, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+}
+
+
+/**
+ * Get the Samples to Average which specifies the number of samples of the timer to
+ * average when calculating the period. Perform averaging to account for
+ * mechanical imperfections or as oversampling to increase resolution.
+ * @return SamplesToAverage The number of samples being averaged (from 1 to 127)
+ */
+int Counter::GetSamplesToAverage()
+{
+ int32_t status = 0;
+ int32_t samples = getCounterSamplesToAverage(m_counter, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ return samples;
+}
+
+/**
+ * Set the Samples to Average which specifies the number of samples of the timer to
+ * average when calculating the period. Perform averaging to account for
+ * mechanical imperfections or as oversampling to increase resolution.
+ * @param samplesToAverage The number of samples to average from 1 to 127.
+ */
+void Counter::SetSamplesToAverage (int samplesToAverage) {
+ if (samplesToAverage < 1 || samplesToAverage > 127)
+ {
+ wpi_setWPIErrorWithContext(ParameterOutOfRange, "Average counter values must be between 1 and 127");
+ }
+ int32_t status = 0;
+ setCounterSamplesToAverage(m_counter, samplesToAverage, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+}
+
+/**
+ * Read the current counter value.
+ * Read the value at this instant. It may still be running, so it reflects the current value. Next
+ * time it is read, it might have a different value.
+ */
+int32_t Counter::Get()
+{
+ if (StatusIsFatal()) return 0;
+ int32_t status = 0;
+ int32_t value = getCounter(m_counter, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ return value;
+}
+
+/**
+ * Reset the Counter to zero.
+ * Set the counter value to zero. This doesn't effect the running state of the counter, just sets
+ * the current value to zero.
+ */
+void Counter::Reset()
+{
+ if (StatusIsFatal()) return;
+ int32_t status = 0;
+ resetCounter(m_counter, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+}
+
+/**
+ * Get the Period of the most recent count.
+ * Returns the time interval of the most recent count. This can be used for velocity calculations
+ * to determine shaft speed.
+ * @returns The period between the last two pulses in units of seconds.
+ */
+double Counter::GetPeriod()
+{
+ if (StatusIsFatal()) return 0.0;
+ int32_t status = 0;
+ double value = getCounterPeriod(m_counter, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ return value;
+}
+
+/**
+ * Set the maximum period where the device is still considered "moving".
+ * Sets the maximum period where the device is considered moving. This value is used to determine
+ * the "stopped" state of the counter using the GetStopped method.
+ * @param maxPeriod The maximum period where the counted device is considered moving in
+ * seconds.
+ */
+void Counter::SetMaxPeriod(double maxPeriod)
+{
+ if (StatusIsFatal()) return;
+ int32_t status = 0;
+ setCounterMaxPeriod(m_counter, maxPeriod, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+}
+
+/**
+ * Select whether you want to continue updating the event timer output when there are no samples captured.
+ * The output of the event timer has a buffer of periods that are averaged and posted to
+ * a register on the FPGA. When the timer detects that the event source has stopped
+ * (based on the MaxPeriod) the buffer of samples to be averaged is emptied. If you
+ * enable the update when empty, you will be notified of the stopped source and the event
+ * time will report 0 samples. If you disable update when empty, the most recent average
+ * will remain on the output until a new sample is acquired. You will never see 0 samples
+ * output (except when there have been no events since an FPGA reset) and you will likely not
+ * see the stopped bit become true (since it is updated at the end of an average and there are
+ * no samples to average).
+ * @param enabled True to enable update when empty
+ */
+void Counter::SetUpdateWhenEmpty(bool enabled)
+{
+ if (StatusIsFatal()) return;
+ int32_t status = 0;
+ setCounterUpdateWhenEmpty(m_counter, enabled, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+}
+
+/**
+ * Determine if the clock is stopped.
+ * Determine if the clocked input is stopped based on the MaxPeriod value set using the
+ * SetMaxPeriod method. If the clock exceeds the MaxPeriod, then the device (and counter) are
+ * assumed to be stopped and it returns true.
+ * @return Returns true if the most recent counter period exceeds the MaxPeriod value set by
+ * SetMaxPeriod.
+ */
+bool Counter::GetStopped()
+{
+ if (StatusIsFatal()) return false;
+ int32_t status = 0;
+ bool value = getCounterStopped(m_counter, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ return value;
+}
+
+/**
+ * The last direction the counter value changed.
+ * @return The last direction the counter value changed.
+ */
+bool Counter::GetDirection()
+{
+ if (StatusIsFatal()) return false;
+ int32_t status = 0;
+ bool value = getCounterDirection(m_counter, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ return value;
+}
+
+/**
+ * Set the Counter to return reversed sensing on the direction.
+ * This allows counters to change the direction they are counting in the case of 1X and 2X
+ * quadrature encoding only. Any other counter mode isn't supported.
+ * @param reverseDirection true if the value counted should be negated.
+ */
+void Counter::SetReverseDirection(bool reverseDirection)
+{
+ if (StatusIsFatal()) return;
+ int32_t status = 0;
+ setCounterReverseDirection(m_counter, reverseDirection, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+}
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/DigitalInput.cpp b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/DigitalInput.cpp
new file mode 100644
index 0000000..4ce8e72
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/DigitalInput.cpp
@@ -0,0 +1,227 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+
+#include <algorithm>
+#include <array>
+
+#include "DigitalInput.h"
+//#include "NetworkCommunication/UsageReporting.h"
+#include "Resource.h"
+#include "WPIErrors.h"
+#include "Encoder.h"
+#include "Counter.h"
+
+::std::array<int, 3> DigitalGlitchFilter::filter_allocated_ = {
+ {false, false, false}};
+Mutex DigitalGlitchFilter::mutex_;
+
+/**
+ * Create an instance of a DigitalInput.
+ * Creates a digital input given a channel. Common creation routine for all
+ * constructors.
+ */
+void DigitalInput::InitDigitalInput(uint32_t channel)
+{
+ char buf[64];
+
+ if (!CheckDigitalChannel(channel))
+ {
+ snprintf(buf, 64, "Digital Channel %d", channel);
+ wpi_setWPIErrorWithContext(ChannelIndexOutOfRange, buf);
+ return;
+ }
+ m_channel = channel;
+
+ int32_t status = 0;
+ allocateDIO(m_digital_ports[channel], true, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+
+ HALReport(HALUsageReporting::kResourceType_DigitalInput, channel);
+}
+
+/**
+ * Create an instance of a Digital Input class.
+ * Creates a digital input given a channel.
+ *
+ * @param channel The DIO channel 0-9 are on-board, 10-25 are on the MXP port
+ */
+DigitalInput::DigitalInput(uint32_t channel)
+{
+ InitDigitalInput(channel);
+}
+
+/**
+ * Free resources associated with the Digital Input class.
+ */
+DigitalInput::~DigitalInput()
+{
+ if (StatusIsFatal()) return;
+ if (m_interrupt != NULL)
+ {
+ int32_t status = 0;
+ cleanInterrupts(m_interrupt, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ m_interrupt = NULL;
+ m_interrupts->Free(m_interruptIndex);
+ }
+
+ int32_t status = 0;
+ freeDIO(m_digital_ports[m_channel], &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+}
+
+/**
+ * Get the value from a digital input channel.
+ * Retrieve the value of a single digital input channel from the FPGA.
+ */
+bool DigitalInput::Get()
+{
+ int32_t status = 0;
+ bool value = getDIO(m_digital_ports[m_channel], &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ return value;
+}
+
+/**
+ * @return The GPIO channel number that this object represents.
+ */
+uint32_t DigitalInput::GetChannel()
+{
+ return m_channel;
+}
+
+/**
+ * @return The value to be written to the channel field of a routing mux.
+ */
+uint32_t DigitalInput::GetChannelForRouting()
+{
+ return GetChannel();
+}
+
+/**
+ * @return The value to be written to the module field of a routing mux.
+ */
+uint32_t DigitalInput::GetModuleForRouting()
+{
+ return 0;
+}
+
+/**
+ * @return The value to be written to the analog trigger field of a routing mux.
+ */
+bool DigitalInput::GetAnalogTriggerForRouting()
+{
+ return false;
+}
+
+DigitalGlitchFilter::DigitalGlitchFilter() : channel_index_(-1) {
+ ::std::unique_lock<Mutex> sync(DigitalGlitchFilter::mutex_);
+ auto index =
+ ::std::find(filter_allocated_.begin(), filter_allocated_.end(), false);
+ if (index != filter_allocated_.end()) {
+ channel_index_ = index - filter_allocated_.begin();
+ *index = true;
+ }
+}
+
+DigitalGlitchFilter::~DigitalGlitchFilter() {
+ ::std::unique_lock<Mutex> sync(DigitalGlitchFilter::mutex_);
+ filter_allocated_[channel_index_] = false;
+}
+
+void DigitalGlitchFilter::Add(DigitalSource *input) {
+ int32_t status = 0;
+ setFilterSelect(input->m_digital_ports[input->GetChannelForRouting()],
+ channel_index_ + 1, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+
+ HALReport(HALUsageReporting::kResourceType_DigitalInput,
+ input->GetChannelForRouting());
+}
+
+void DigitalGlitchFilter::Remove(DigitalSource *input) {
+ int32_t status = 0;
+ setFilterSelect(input->m_digital_ports[input->GetChannelForRouting()], 0, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+
+ HALReport(HALUsageReporting::kResourceType_DigitalInput, input->GetChannelForRouting());
+}
+
+void DigitalGlitchFilter::SetPeriodCycles(uint32_t fpga_cycles) {
+ int32_t status = 0;
+ setFilterPeriod(channel_index_, fpga_cycles, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+
+ HALReport(HALUsageReporting::kResourceType_DigitalGlitchFilter,
+ channel_index_);
+}
+
+void DigitalGlitchFilter::SetPeriodNanoSeconds(uint64_t nanoseconds) {
+ int32_t status = 0;
+ uint32_t fpga_cycles =
+ nanoseconds * kSystemClockTicksPerMicrosecond / 4 / 1000;
+ setFilterPeriod(channel_index_, fpga_cycles, &status);
+
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+
+ HALReport(HALUsageReporting::kResourceType_DigitalGlitchFilter,
+ channel_index_);
+}
+
+uint32_t DigitalGlitchFilter::GetPeriodCycles() {
+ int32_t status = 0;
+ uint32_t fpga_cycles = getFilterPeriod(channel_index_, &status);
+
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+
+ HALReport(HALUsageReporting::kResourceType_DigitalGlitchFilter,
+ channel_index_);
+ return fpga_cycles;
+}
+
+uint64_t DigitalGlitchFilter::GetPeriodNanoSeconds() {
+ int32_t status = 0;
+ uint32_t fpga_cycles = getFilterPeriod(channel_index_, &status);
+
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+
+ HALReport(HALUsageReporting::kResourceType_DigitalGlitchFilter,
+ channel_index_);
+ return static_cast<uint64_t>(fpga_cycles) * 1000l /
+ static_cast<uint64_t>(kSystemClockTicksPerMicrosecond / 4);
+}
+
+void DigitalGlitchFilter::Add(Encoder *input) {
+ Add(input->m_aSource);
+ if (StatusIsFatal()) {
+ return;
+ }
+ Add(input->m_bSource);
+}
+
+void DigitalGlitchFilter::Add(Counter *input) {
+ Add(input->m_upSource);
+ if (StatusIsFatal()) {
+ return;
+ }
+ Add(input->m_downSource);
+}
+
+void DigitalGlitchFilter::Remove(Encoder *input) {
+ Remove(input->m_aSource);
+ if (StatusIsFatal()) {
+ return;
+ }
+ Remove(input->m_bSource);
+}
+
+void DigitalGlitchFilter::Remove(Counter *input) {
+ Remove(input->m_upSource);
+ if (StatusIsFatal()) {
+ return;
+ }
+ Remove(input->m_downSource);
+}
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/DigitalOutput.cpp b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/DigitalOutput.cpp
new file mode 100644
index 0000000..aeeab8a
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/DigitalOutput.cpp
@@ -0,0 +1,235 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+
+#include "DigitalOutput.h"
+//#include "NetworkCommunication/UsageReporting.h"
+#include "Resource.h"
+#include "WPIErrors.h"
+
+/**
+ * Create an instance of a DigitalOutput.
+ * Creates a digital output given a channel. Common creation routine for all
+ * constructors.
+ */
+void DigitalOutput::InitDigitalOutput(uint32_t channel)
+{
+ char buf[64];
+
+ if (!CheckDigitalChannel(channel))
+ {
+ snprintf(buf, 64, "Digital Channel %d", channel);
+ wpi_setWPIErrorWithContext(ChannelIndexOutOfRange, buf);
+ return;
+ }
+ m_channel = channel;
+ m_pwmGenerator = (void *)~0ul;
+
+ int32_t status = 0;
+ allocateDIO(m_digital_ports[channel], false, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+
+ HALReport(HALUsageReporting::kResourceType_DigitalOutput, channel);
+}
+
+/**
+ * Create an instance of a digital output.
+ * Create a digital output given a channel.
+ *
+ * @param channel The digital channel 0-9 are on-board, 10-25 are on the MXP port
+ */
+DigitalOutput::DigitalOutput(uint32_t channel)
+{
+ InitDigitalOutput(channel);
+}
+
+/**
+ * Free the resources associated with a digital output.
+ */
+DigitalOutput::~DigitalOutput()
+{
+ if (StatusIsFatal()) return;
+ // Disable the PWM in case it was running.
+ DisablePWM();
+
+ int32_t status = 0;
+ freeDIO(m_digital_ports[m_channel], &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+}
+
+/**
+ * Set the value of a digital output.
+ * Set the value of a digital output to either one (true) or zero (false).
+ * @param value 1 (true) for high, 0 (false) for disabled
+ */
+void DigitalOutput::Set(uint32_t value)
+{
+ if (StatusIsFatal()) return;
+
+ int32_t status = 0;
+ setDIO(m_digital_ports[m_channel], value, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+}
+
+/**
+ * Get the value from a digital output channel.
+ * Retrieve the value of a single digital output channel from the FPGA.
+ */
+bool DigitalOutput::Get()
+{
+ int32_t status = 0;
+ bool value = getDIO(m_digital_ports[m_channel], &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ return value;
+}
+
+/**
+ * @return The GPIO channel number that this object represents.
+ */
+uint32_t DigitalOutput::GetChannel()
+{
+ return m_channel;
+}
+
+/**
+ * Output a single pulse on the digital output line.
+ * Send a single pulse on the digital output line where the pulse duration is specified in seconds.
+ * Maximum pulse length is 0.0016 seconds.
+ * @param length The pulselength in seconds
+ */
+void DigitalOutput::Pulse(float length)
+{
+ if (StatusIsFatal()) return;
+
+ int32_t status = 0;
+ pulse(m_digital_ports[m_channel], length, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+}
+
+/**
+ * Determine if the pulse is still going.
+ * Determine if a previously started pulse is still going.
+ */
+bool DigitalOutput::IsPulsing()
+{
+ if (StatusIsFatal()) return false;
+
+ int32_t status = 0;
+ bool value = isPulsing(m_digital_ports[m_channel], &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ return value;
+}
+
+/**
+ * Change the PWM frequency of the PWM output on a Digital Output line.
+ *
+ * The valid range is from 0.6 Hz to 19 kHz. The frequency resolution is logarithmic.
+ *
+ * There is only one PWM frequency for all digital channels.
+ *
+ * @param rate The frequency to output all digital output PWM signals.
+ */
+void DigitalOutput::SetPWMRate(float rate)
+{
+ if (StatusIsFatal()) return;
+
+ int32_t status = 0;
+ setPWMRate(rate, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+}
+
+/**
+ * Enable a PWM Output on this line.
+ *
+ * Allocate one of the 6 DO PWM generator resources from this module.
+ *
+ * Supply the initial duty-cycle to output so as to avoid a glitch when first starting.
+ *
+ * The resolution of the duty cycle is 8-bit for low frequencies (1kHz or less)
+ * but is reduced the higher the frequency of the PWM signal is.
+ *
+ * @param initialDutyCycle The duty-cycle to start generating. [0..1]
+ */
+void DigitalOutput::EnablePWM(float initialDutyCycle)
+{
+ if(m_pwmGenerator != (void *)~0ul) return;
+
+ int32_t status = 0;
+
+ if(StatusIsFatal()) return;
+ m_pwmGenerator = allocatePWM(&status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+
+ if(StatusIsFatal()) return;
+ setPWMDutyCycle(m_pwmGenerator, initialDutyCycle, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+
+ if(StatusIsFatal()) return;
+ setPWMOutputChannel(m_pwmGenerator, m_channel, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+}
+
+/**
+ * Change this line from a PWM output back to a static Digital Output line.
+ *
+ * Free up one of the 6 DO PWM generator resources that were in use.
+ */
+void DigitalOutput::DisablePWM()
+{
+ if (StatusIsFatal()) return;
+ if(m_pwmGenerator == (void *)~0ul) return;
+
+ int32_t status = 0;
+
+ // Disable the output by routing to a dead bit.
+ setPWMOutputChannel(m_pwmGenerator, kDigitalChannels, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+
+ freePWM(m_pwmGenerator, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+
+ m_pwmGenerator = (void *)~0ul;
+}
+
+/**
+ * Change the duty-cycle that is being generated on the line.
+ *
+ * The resolution of the duty cycle is 8-bit for low frequencies (1kHz or less)
+ * but is reduced the higher the frequency of the PWM signal is.
+ *
+ * @param dutyCycle The duty-cycle to change to. [0..1]
+ */
+void DigitalOutput::UpdateDutyCycle(float dutyCycle)
+{
+ if (StatusIsFatal()) return;
+
+ int32_t status = 0;
+ setPWMDutyCycle(m_pwmGenerator, dutyCycle, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+}
+
+/**
+ * @return The value to be written to the channel field of a routing mux.
+ */
+uint32_t DigitalOutput::GetChannelForRouting()
+{
+ return GetChannel();
+}
+
+/**
+ * @return The value to be written to the module field of a routing mux.
+ */
+uint32_t DigitalOutput::GetModuleForRouting()
+{
+ return 0;
+}
+
+/**
+ * @return The value to be written to the analog trigger field of a routing mux.
+ */
+bool DigitalOutput::GetAnalogTriggerForRouting()
+{
+ return false;
+}
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/DigitalSource.cpp b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/DigitalSource.cpp
new file mode 100644
index 0000000..40cc75c
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/DigitalSource.cpp
@@ -0,0 +1,14 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+
+#include "DigitalSource.h"
+
+/**
+ * DigitalSource destructor.
+ */
+DigitalSource::~DigitalSource()
+{
+}
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/DoubleSolenoid.cpp b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/DoubleSolenoid.cpp
new file mode 100644
index 0000000..091bae4
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/DoubleSolenoid.cpp
@@ -0,0 +1,163 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+
+#include "DoubleSolenoid.h"
+//#include "NetworkCommunication/UsageReporting.h"
+#include "WPIErrors.h"
+
+/**
+ * Common function to implement constructor behaviour.
+ */
+void DoubleSolenoid::InitSolenoid()
+{
+ char buf[64];
+ if (!CheckSolenoidModule(m_moduleNumber))
+ {
+ snprintf(buf, 64, "Solenoid Module %d", m_moduleNumber);
+ wpi_setWPIErrorWithContext(ModuleIndexOutOfRange, buf);
+ return;
+ }
+ if (!CheckSolenoidChannel(m_forwardChannel))
+ {
+ snprintf(buf, 64, "Solenoid Channel %d", m_forwardChannel);
+ wpi_setWPIErrorWithContext(ChannelIndexOutOfRange, buf);
+ return;
+ }
+ if (!CheckSolenoidChannel(m_reverseChannel))
+ {
+ snprintf(buf, 64, "Solenoid Channel %d", m_reverseChannel);
+ wpi_setWPIErrorWithContext(ChannelIndexOutOfRange, buf);
+ return;
+ }
+ Resource::CreateResourceObject(&m_allocated, solenoid_kNumDO7_0Elements * kSolenoidChannels);
+
+ snprintf(buf, 64, "Solenoid %d (Module: %d)", m_forwardChannel, m_moduleNumber);
+ if (m_allocated->Allocate(m_moduleNumber * kSolenoidChannels + m_forwardChannel, buf) == ~0ul)
+ {
+ CloneError(m_allocated);
+ return;
+ }
+
+ snprintf(buf, 64, "Solenoid %d (Module: %d)", m_reverseChannel, m_moduleNumber);
+ if (m_allocated->Allocate(m_moduleNumber * kSolenoidChannels + m_reverseChannel, buf) == ~0ul)
+ {
+ CloneError(m_allocated);
+ return;
+ }
+
+ m_forwardMask = 1 << m_forwardChannel;
+ m_reverseMask = 1 << m_reverseChannel;
+ HALReport(HALUsageReporting::kResourceType_Solenoid, m_forwardChannel, m_moduleNumber);
+ HALReport(HALUsageReporting::kResourceType_Solenoid, m_reverseChannel, m_moduleNumber);
+}
+
+/**
+ * Constructor.
+ * Uses the default PCM ID of 0
+ * @param forwardChannel The forward channel number on the PCM (0..7).
+ * @param reverseChannel The reverse channel number on the PCM (0..7).
+ */
+DoubleSolenoid::DoubleSolenoid(uint32_t forwardChannel, uint32_t reverseChannel)
+ : SolenoidBase (GetDefaultSolenoidModule())
+ , m_forwardChannel (forwardChannel)
+ , m_reverseChannel (reverseChannel)
+{
+ InitSolenoid();
+}
+
+/**
+ * Constructor.
+ *
+ * @param moduleNumber The CAN ID of the PCM.
+ * @param forwardChannel The forward channel on the PCM to control (0..7).
+ * @param reverseChannel The reverse channel on the PCM to control (0..7).
+ */
+DoubleSolenoid::DoubleSolenoid(uint8_t moduleNumber, uint32_t forwardChannel, uint32_t reverseChannel)
+ : SolenoidBase (moduleNumber)
+ , m_forwardChannel (forwardChannel)
+ , m_reverseChannel (reverseChannel)
+{
+ InitSolenoid();
+}
+
+/**
+ * Destructor.
+ */
+DoubleSolenoid::~DoubleSolenoid()
+{
+ if (CheckSolenoidModule(m_moduleNumber))
+ {
+ m_allocated->Free(m_moduleNumber * kSolenoidChannels + m_forwardChannel);
+ m_allocated->Free(m_moduleNumber * kSolenoidChannels + m_reverseChannel);
+ }
+}
+
+/**
+ * Set the value of a solenoid.
+ *
+ * @param value The value to set (Off, Forward or Reverse)
+ */
+void DoubleSolenoid::Set(Value value)
+{
+ if (StatusIsFatal()) return;
+ uint8_t rawValue = 0x00;
+
+ switch(value)
+ {
+ case kOff:
+ rawValue = 0x00;
+ break;
+ case kForward:
+ rawValue = m_forwardMask;
+ break;
+ case kReverse:
+ rawValue = m_reverseMask;
+ break;
+ }
+
+ SolenoidBase::Set(rawValue, m_forwardMask | m_reverseMask);
+}
+
+/**
+ * Read the current value of the solenoid.
+ *
+ * @return The current value of the solenoid.
+ */
+DoubleSolenoid::Value DoubleSolenoid::Get()
+{
+ if (StatusIsFatal()) return kOff;
+ uint8_t value = GetAll();
+
+ if (value & m_forwardMask) return kForward;
+ if (value & m_reverseMask) return kReverse;
+ return kOff;
+}
+/**
+ * Check if the forward solenoid is blacklisted.
+ * If a solenoid is shorted, it is added to the blacklist and
+ * disabled until power cycle, or until faults are cleared.
+ * @see ClearAllPCMStickyFaults()
+ *
+ * @return If solenoid is disabled due to short.
+ */
+bool DoubleSolenoid::IsFwdSolenoidBlackListed()
+{
+ int blackList = GetPCMSolenoidBlackList();
+ return (blackList & m_forwardMask) ? 1 : 0;
+}
+/**
+ * Check if the reverse solenoid is blacklisted.
+ * If a solenoid is shorted, it is added to the blacklist and
+ * disabled until power cycle, or until faults are cleared.
+ * @see ClearAllPCMStickyFaults()
+ *
+ * @return If solenoid is disabled due to short.
+ */
+bool DoubleSolenoid::IsRevSolenoidBlackListed()
+{
+ int blackList = GetPCMSolenoidBlackList();
+ return (blackList & m_reverseMask) ? 1 : 0;
+}
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/DriverStation.cpp b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/DriverStation.cpp
new file mode 100644
index 0000000..bcd3063
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/DriverStation.cpp
@@ -0,0 +1,534 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+
+#include "DriverStation.h"
+#include "AnalogInput.h"
+#include "HAL/cpp/Synchronized.hpp"
+#include "Timer.h"
+#include "NetworkCommunication/FRCComm.h"
+#include "NetworkCommunication/UsageReporting.h"
+#include "MotorSafetyHelper.h"
+#include "Utility.h"
+#include "WPIErrors.h"
+#include <string.h>
+#include "Log.hpp"
+
+// set the logging level
+TLogLevel dsLogLevel = logDEBUG;
+const double JOYSTICK_UNPLUGGED_MESSAGE_INTERVAL = 1.0;
+
+#define DS_LOG(level) \
+ if (level > dsLogLevel) ; \
+ else Log().Get(level)
+
+const uint32_t DriverStation::kJoystickPorts;
+DriverStation* DriverStation::m_instance = NULL;
+
+/**
+ * DriverStation constructor.
+ *
+ * This is only called once the first time GetInstance() is called
+ */
+DriverStation::DriverStation()
+ : m_task ("DriverStation", (FUNCPTR)DriverStation::InitTask)
+ , m_newControlData(0)
+ , m_packetDataAvailableMultiWait(0)
+ , m_waitForDataSem(0)
+ , m_userInDisabled(false)
+ , m_userInAutonomous(false)
+ , m_userInTeleop(false)
+ , m_userInTest(false)
+ , m_nextMessageTime(0)
+{
+ // All joysticks should default to having zero axes, povs and buttons, so
+ // uninitialized memory doesn't get sent to speed controllers.
+ for(unsigned int i = 0; i < kJoystickPorts; i++) {
+ m_joystickAxes[i].count = 0;
+ m_joystickPOVs[i].count = 0;
+ m_joystickButtons[i].count = 0;
+ }
+ // Create a new semaphore
+ m_packetDataAvailableMultiWait = initializeMultiWait();
+ m_newControlData = initializeSemaphore(SEMAPHORE_EMPTY);
+
+ m_waitForDataSem = initializeMultiWait();
+ m_waitForDataMutex = initializeMutexNormal();
+
+ m_packetDataAvailableMultiWait = initializeMultiWait();
+ m_packetDataAvailableMutex = initializeMutexNormal();
+
+ // Register that semaphore with the network communications task.
+ // It will signal when new packet data is available.
+ HALSetNewDataSem(m_packetDataAvailableMultiWait);
+
+ AddToSingletonList();
+
+ if (!m_task.Start((int32_t)this))
+ {
+ wpi_setWPIError(DriverStationTaskError);
+ }
+}
+
+DriverStation::~DriverStation()
+{
+ m_task.Stop();
+ m_instance = NULL;
+ deleteMultiWait(m_waitForDataSem);
+ // Unregister our semaphore.
+ HALSetNewDataSem(NULL);
+ deleteMultiWait(m_packetDataAvailableMultiWait);
+ deleteMutex(m_packetDataAvailableMutex);
+ deleteMutex(m_waitForDataMutex);
+}
+
+void DriverStation::InitTask(DriverStation *ds)
+{
+ ds->Run();
+}
+
+void DriverStation::Run()
+{
+ int period = 0;
+ while (true)
+ {
+ takeMultiWait(m_packetDataAvailableMultiWait, m_packetDataAvailableMutex, 0);
+ GetData();
+ giveMultiWait(m_waitForDataSem);
+
+ if (++period >= 4)
+ {
+ MotorSafetyHelper::CheckMotors();
+ period = 0;
+ }
+ if (m_userInDisabled)
+ HALNetworkCommunicationObserveUserProgramDisabled();
+ if (m_userInAutonomous)
+ HALNetworkCommunicationObserveUserProgramAutonomous();
+ if (m_userInTeleop)
+ HALNetworkCommunicationObserveUserProgramTeleop();
+ if (m_userInTest)
+ HALNetworkCommunicationObserveUserProgramTest();
+ }
+}
+
+/**
+ * Return a pointer to the singleton DriverStation.
+ * @return Pointer to the DS instance
+ */
+DriverStation* DriverStation::GetInstance()
+{
+ if (m_instance == NULL)
+ {
+ m_instance = new DriverStation();
+ }
+ return m_instance;
+}
+
+/**
+ * Copy data from the DS task for the user.
+ * If no new data exists, it will just be returned, otherwise
+ * the data will be copied from the DS polling loop.
+ */
+void DriverStation::GetData()
+{
+
+ // Get the status of all of the joysticks
+ for(uint8_t stick = 0; stick < 4; stick++) {
+ HALGetJoystickAxes(stick, &m_joystickAxes[stick]);
+ HALGetJoystickButtons(stick, &m_joystickButtons[stick]);
+ HALGetJoystickPOVs(stick, &m_joystickPOVs[stick]);
+ }
+ giveSemaphore(m_newControlData);
+}
+
+/**
+ * Read the battery voltage.
+ *
+ * @return The battery voltage in Volts.
+ */
+float DriverStation::GetBatteryVoltage()
+{
+ int32_t status = 0;
+ float voltage = getVinVoltage(&status);
+ wpi_setErrorWithContext(status, "getVinVoltage");
+
+ return voltage;
+}
+
+/**
+ * Reports errors related to unplugged joysticks
+ * Throttles the errors so that they don't overwhelm the DS
+ */
+void DriverStation::ReportJoystickUnpluggedError(std::string message) {
+ double currentTime = Timer::GetFPGATimestamp();
+ if (currentTime > m_nextMessageTime) {
+ ReportError(message);
+ m_nextMessageTime = currentTime + JOYSTICK_UNPLUGGED_MESSAGE_INTERVAL;
+ }
+}
+
+/**
+ * Returns the number of axes on a given joystick port
+ *
+ * @param stick The joystick port number
+ * @return The number of axes on the indicated joystick
+ */
+int DriverStation::GetStickAxisCount(uint32_t stick)
+{
+ if (stick >= kJoystickPorts)
+ {
+ wpi_setWPIError(BadJoystickIndex);
+ return 0;
+ }
+ HALJoystickAxes joystickAxes;
+ HALGetJoystickAxes(stick, &joystickAxes);
+ return joystickAxes.count;
+}
+
+/**
+ * Returns the number of POVs on a given joystick port
+ *
+ * @param stick The joystick port number
+ * @return The number of POVs on the indicated joystick
+ */
+int DriverStation::GetStickPOVCount(uint32_t stick)
+{
+ if (stick >= kJoystickPorts)
+ {
+ wpi_setWPIError(BadJoystickIndex);
+ return 0;
+ }
+ HALJoystickPOVs joystickPOVs;
+ HALGetJoystickPOVs(stick, &joystickPOVs);
+ return joystickPOVs.count;
+}
+
+/**
+ * Returns the number of buttons on a given joystick port
+ *
+ * @param stick The joystick port number
+ * @return The number of buttons on the indicated joystick
+ */
+int DriverStation::GetStickButtonCount(uint32_t stick)
+{
+ if (stick >= kJoystickPorts)
+ {
+ wpi_setWPIError(BadJoystickIndex);
+ return 0;
+ }
+ HALJoystickButtons joystickButtons;
+ HALGetJoystickButtons(stick, &joystickButtons);
+ return joystickButtons.count;
+}
+
+/**
+ * Get the value of the axis on a joystick.
+ * This depends on the mapping of the joystick connected to the specified port.
+ *
+ * @param stick The joystick to read.
+ * @param axis The analog axis value to read from the joystick.
+ * @return The value of the axis on the joystick.
+ */
+float DriverStation::GetStickAxis(uint32_t stick, uint32_t axis)
+{
+ if (stick >= kJoystickPorts)
+ {
+ wpi_setWPIError(BadJoystickIndex);
+ return 0;
+ }
+
+ if (axis >= m_joystickAxes[stick].count)
+ {
+ if (axis >= kMaxJoystickAxes)
+ wpi_setWPIError(BadJoystickAxis);
+ //else
+ //ReportJoystickUnpluggedError("WARNING: Joystick Axis missing, check if all controllers are plugged in\n");
+ return 0.0f;
+ }
+
+ int8_t value = m_joystickAxes[stick].axes[axis];
+
+ if(value < 0)
+ {
+ return value / 128.0f;
+ }
+ else
+ {
+ return value / 127.0f;
+ }
+}
+
+/**
+ * Get the state of a POV on the joystick.
+ *
+ * @return the angle of the POV in degrees, or -1 if the POV is not pressed.
+ */
+int DriverStation::GetStickPOV(uint32_t stick, uint32_t pov) {
+ if (stick >= kJoystickPorts)
+ {
+ wpi_setWPIError(BadJoystickIndex);
+ return 0;
+ }
+
+ if (pov >= m_joystickPOVs[stick].count)
+ {
+ if (pov >= kMaxJoystickPOVs)
+ wpi_setWPIError(BadJoystickAxis);
+ //else
+ //ReportJoystickUnpluggedError("WARNING: Joystick POV missing, check if all controllers are plugged in\n");
+ return 0;
+ }
+
+ return m_joystickPOVs[stick].povs[pov];
+}
+
+/**
+ * The state of the buttons on the joystick.
+ *
+ * @param stick The joystick to read.
+ * @return The state of the buttons on the joystick.
+ */
+uint32_t DriverStation::GetStickButtons(uint32_t stick)
+{
+ if (stick >= kJoystickPorts)
+ {
+ wpi_setWPIError(BadJoystickIndex);
+ return 0;
+ }
+
+ return m_joystickButtons[stick].buttons;
+}
+
+/**
+ * The state of one joystick button. Button indexes begin at 1.
+ *
+ * @param stick The joystick to read.
+ * @param button The button index, beginning at 1.
+ * @return The state of the joystick button.
+ */
+bool DriverStation::GetStickButton(uint32_t stick, uint8_t button)
+{
+ if (stick >= kJoystickPorts)
+ {
+ wpi_setWPIError(BadJoystickIndex);
+ return false;
+ }
+
+ if(button > m_joystickButtons[stick].count)
+ {
+ //ReportJoystickUnpluggedError("WARNING: Joystick Button missing, check if all controllers are plugged in\n");
+ return false;
+ }
+ if(button == 0)
+ {
+ //ReportJoystickUnpluggedError("ERROR: Button indexes begin at 1 in WPILib for C++ and Java");
+ return false;
+ }
+ return ((0x1 << (button-1)) & m_joystickButtons[stick].buttons) !=0;
+}
+
+/**
+ * Check if the DS has enabled the robot
+ * @return True if the robot is enabled and the DS is connected
+ */
+bool DriverStation::IsEnabled()
+{
+ HALControlWord controlWord;
+ memset(&controlWord, 0, sizeof(controlWord));
+ HALGetControlWord(&controlWord);
+ return controlWord.enabled && controlWord.dsAttached;
+}
+
+/**
+ * Check if the robot is disabled
+ * @return True if the robot is explicitly disabled or the DS is not connected
+ */
+bool DriverStation::IsDisabled()
+{
+ HALControlWord controlWord;
+ memset(&controlWord, 0, sizeof(controlWord));
+ HALGetControlWord(&controlWord);
+ return !(controlWord.enabled && controlWord.dsAttached);
+}
+
+/**
+ * Check if the DS is commanding autonomous mode
+ * @return True if the robot is being commanded to be in autonomous mode
+ */
+bool DriverStation::IsAutonomous()
+{
+ HALControlWord controlWord;
+ memset(&controlWord, 0, sizeof(controlWord));
+ HALGetControlWord(&controlWord);
+ return controlWord.autonomous;
+}
+
+/**
+ * Check if the DS is commanding teleop mode
+ * @return True if the robot is being commanded to be in teleop mode
+ */
+bool DriverStation::IsOperatorControl()
+{
+ HALControlWord controlWord;
+ memset(&controlWord, 0, sizeof(controlWord));
+ HALGetControlWord(&controlWord);
+ return !(controlWord.autonomous || controlWord.test);
+}
+
+/**
+ * Check if the DS is commanding test mode
+ * @return True if the robot is being commanded to be in test mode
+ */
+bool DriverStation::IsTest()
+{
+ HALControlWord controlWord;
+ HALGetControlWord(&controlWord);
+ return controlWord.test;
+}
+
+/**
+ * Check if the DS is attached
+ * @return True if the DS is connected to the robot
+ */
+bool DriverStation::IsDSAttached()
+{
+ HALControlWord controlWord;
+ memset(&controlWord, 0, sizeof(controlWord));
+ HALGetControlWord(&controlWord);
+ return controlWord.dsAttached;
+}
+
+/**
+ * Check if the FPGA outputs are enabled. The outputs may be disabled if the robot is disabled
+ * or e-stopped, the watchdog has expired, or if the roboRIO browns out.
+ * @return True if the FPGA outputs are enabled.
+ */
+bool DriverStation::IsSysActive()
+{
+ int32_t status = 0;
+ bool retVal = HALGetSystemActive(&status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ return retVal;
+}
+
+/**
+ * Check if the system is browned out.
+ * @return True if the system is browned out
+ */
+bool DriverStation::IsSysBrownedOut()
+{
+ int32_t status = 0;
+ bool retVal = HALGetBrownedOut(&status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ return retVal;
+}
+
+/**
+ * Has a new control packet from the driver station arrived since the last time this function was called?
+ * Warning: If you call this function from more than one place at the same time,
+ * you will not get the get the intended behaviour.
+ * @return True if the control data has been updated since the last call.
+ */
+bool DriverStation::IsNewControlData()
+{
+ return tryTakeSemaphore(m_newControlData) == 0;
+}
+
+/**
+ * Is the driver station attached to a Field Management System?
+ * @return True if the robot is competing on a field being controlled by a Field Management System
+ */
+bool DriverStation::IsFMSAttached()
+{
+ HALControlWord controlWord;
+ HALGetControlWord(&controlWord);
+ return controlWord.fmsAttached;
+}
+
+/**
+ * Return the alliance that the driver station says it is on.
+ * This could return kRed or kBlue
+ * @return The Alliance enum (kRed, kBlue or kInvalid)
+ */
+DriverStation::Alliance DriverStation::GetAlliance()
+{
+ HALAllianceStationID allianceStationID;
+ HALGetAllianceStation(&allianceStationID);
+ switch(allianceStationID)
+ {
+ case kHALAllianceStationID_red1:
+ case kHALAllianceStationID_red2:
+ case kHALAllianceStationID_red3:
+ return kRed;
+ case kHALAllianceStationID_blue1:
+ case kHALAllianceStationID_blue2:
+ case kHALAllianceStationID_blue3:
+ return kBlue;
+ default:
+ return kInvalid;
+ }
+}
+
+/**
+ * Return the driver station location on the field
+ * This could return 1, 2, or 3
+ * @return The location of the driver station (1-3, 0 for invalid)
+ */
+uint32_t DriverStation::GetLocation()
+{
+ HALAllianceStationID allianceStationID;
+ HALGetAllianceStation(&allianceStationID);
+ switch(allianceStationID)
+ {
+ case kHALAllianceStationID_red1:
+ case kHALAllianceStationID_blue1:
+ return 1;
+ case kHALAllianceStationID_red2:
+ case kHALAllianceStationID_blue2:
+ return 2;
+ case kHALAllianceStationID_red3:
+ case kHALAllianceStationID_blue3:
+ return 3;
+ default:
+ return 0;
+ }
+}
+
+/**
+ * Wait until a new packet comes from the driver station
+ * This blocks on a semaphore, so the waiting is efficient.
+ * This is a good way to delay processing until there is new driver station data to act on
+ */
+void DriverStation::WaitForData()
+{
+ takeMultiWait(m_waitForDataSem, m_waitForDataMutex, SEMAPHORE_WAIT_FOREVER);
+}
+
+/**
+ * Return the approximate match time
+ * The FMS does not send an official match time to the robots, but does send an approximate match time.
+ * The value will count down the time remaining in the current period (auto or teleop).
+ * Warning: This is not an official time (so it cannot be used to dispute ref calls or guarantee that a function
+ * will trigger before the match ends)
+ * The Practice Match function of the DS approximates the behaviour seen on the field.
+ * @return Time remaining in current match period (auto or teleop)
+ */
+double DriverStation::GetMatchTime()
+{
+ float matchTime;
+ HALGetMatchTime(&matchTime);
+ return (double)matchTime;
+}
+
+/**
+ * Report an error to the DriverStation messages window.
+ * The error is also printed to the program console.
+ */
+void DriverStation::ReportError(std::string error)
+{
+ std::cout << error << std::endl;
+ HALSetErrorData(error.c_str(), error.size(), 0);
+}
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/Encoder.cpp b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/Encoder.cpp
new file mode 100644
index 0000000..4a2afb1
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/Encoder.cpp
@@ -0,0 +1,546 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+
+#include "Encoder.h"
+#include "DigitalInput.h"
+//#include "NetworkCommunication/UsageReporting.h"
+#include "Resource.h"
+#include "WPIErrors.h"
+
+/**
+ * Common initialization code for Encoders.
+ * This code allocates resources for Encoders and is common to all constructors.
+ *
+ * The counter will start counting immediately.
+ *
+ * @param reverseDirection If true, counts down instead of up (this is all relative)
+ * @param encodingType either k1X, k2X, or k4X to indicate 1X, 2X or 4X decoding. If 4X is
+ * selected, then an encoder FPGA object is used and the returned counts will be 4x the encoder
+ * spec'd value since all rising and falling edges are counted. If 1X or 2X are selected then
+ * a counter object will be used and the returned value will either exactly match the spec'd count
+ * or be double (2x) the spec'd count.
+ */
+void Encoder::InitEncoder(bool reverseDirection, EncodingType encodingType)
+{
+ m_encodingType = encodingType;
+ m_index = 0;
+ switch (encodingType)
+ {
+ case k4X:
+ {
+ m_encodingScale = 4;
+ if (m_aSource->StatusIsFatal())
+ {
+ CloneError(m_aSource);
+ return;
+ }
+ if (m_bSource->StatusIsFatal())
+ {
+ CloneError(m_bSource);
+ return;
+ }
+ int32_t status = 0;
+ m_encoder = initializeEncoder(m_aSource->GetModuleForRouting(), m_aSource->GetChannelForRouting(),
+ m_aSource->GetAnalogTriggerForRouting(),
+ m_bSource->GetModuleForRouting(), m_bSource->GetChannelForRouting(),
+ m_bSource->GetAnalogTriggerForRouting(),
+ reverseDirection, &m_index, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ m_counter = NULL;
+ SetMaxPeriod(.5);
+ break;
+ }
+ case k1X:
+ case k2X:
+ {
+ m_encodingScale = encodingType == k1X ? 1 : 2;
+ m_counter = new Counter(m_encodingType, m_aSource, m_bSource, reverseDirection);
+ m_index = m_counter->GetFPGAIndex();
+ break;
+ }
+ default:
+ wpi_setErrorWithContext(-1, "Invalid encodingType argument");
+ break;
+ }
+ m_distancePerPulse = 1.0;
+ m_pidSource = kDistance;
+
+ HALReport(HALUsageReporting::kResourceType_Encoder, m_index, encodingType);
+}
+
+/**
+ * Encoder constructor.
+ * Construct a Encoder given a and b channels.
+ *
+ * The counter will start counting immediately.
+ *
+ * @param aChannel The a channel DIO channel. 0-9 are on-board, 10-25 are on the MXP port
+ * @param bChannel The b channel DIO channel. 0-9 are on-board, 10-25 are on the MXP port
+ * @param reverseDirection represents the orientation of the encoder and inverts the output values
+ * if necessary so forward represents positive values.
+ * @param encodingType either k1X, k2X, or k4X to indicate 1X, 2X or 4X decoding. If 4X is
+ * selected, then an encoder FPGA object is used and the returned counts will be 4x the encoder
+ * spec'd value since all rising and falling edges are counted. If 1X or 2X are selected then
+ * a counter object will be used and the returned value will either exactly match the spec'd count
+ * or be double (2x) the spec'd count.
+ */
+Encoder::Encoder(uint32_t aChannel, uint32_t bChannel, bool reverseDirection, EncodingType encodingType) :
+ m_encoder(NULL),
+ m_counter(NULL)
+{
+ m_aSource = new DigitalInput(aChannel);
+ m_bSource = new DigitalInput(bChannel);
+ InitEncoder(reverseDirection, encodingType);
+ m_allocatedASource = true;
+ m_allocatedBSource = true;
+}
+
+/**
+ * Encoder constructor.
+ * Construct a Encoder given a and b channels as digital inputs. This is used in the case
+ * where the digital inputs are shared. The Encoder class will not allocate the digital inputs
+ * and assume that they already are counted.
+ *
+ * The counter will start counting immediately.
+ *
+ * @param aSource The source that should be used for the a channel.
+ * @param bSource the source that should be used for the b channel.
+ * @param reverseDirection represents the orientation of the encoder and inverts the output values
+ * if necessary so forward represents positive values.
+ * @param encodingType either k1X, k2X, or k4X to indicate 1X, 2X or 4X decoding. If 4X is
+ * selected, then an encoder FPGA object is used and the returned counts will be 4x the encoder
+ * spec'd value since all rising and falling edges are counted. If 1X or 2X are selected then
+ * a counter object will be used and the returned value will either exactly match the spec'd count
+ * or be double (2x) the spec'd count.
+ */
+Encoder::Encoder(DigitalSource *aSource, DigitalSource *bSource, bool reverseDirection, EncodingType encodingType) :
+ m_encoder(NULL),
+ m_counter(NULL)
+{
+ m_aSource = aSource;
+ m_bSource = bSource;
+ m_allocatedASource = false;
+ m_allocatedBSource = false;
+ if (m_aSource == NULL || m_bSource == NULL)
+ wpi_setWPIError(NullParameter);
+ else
+ InitEncoder(reverseDirection, encodingType);
+}
+
+/**
+ * Encoder constructor.
+ * Construct a Encoder given a and b channels as digital inputs. This is used in the case
+ * where the digital inputs are shared. The Encoder class will not allocate the digital inputs
+ * and assume that they already are counted.
+ *
+ * The counter will start counting immediately.
+ *
+ * @param aSource The source that should be used for the a channel.
+ * @param bSource the source that should be used for the b channel.
+ * @param reverseDirection represents the orientation of the encoder and inverts the output values
+ * if necessary so forward represents positive values.
+ * @param encodingType either k1X, k2X, or k4X to indicate 1X, 2X or 4X decoding. If 4X is
+ * selected, then an encoder FPGA object is used and the returned counts will be 4x the encoder
+ * spec'd value since all rising and falling edges are counted. If 1X or 2X are selected then
+ * a counter object will be used and the returned value will either exactly match the spec'd count
+ * or be double (2x) the spec'd count.
+ */
+Encoder::Encoder(DigitalSource &aSource, DigitalSource &bSource, bool reverseDirection, EncodingType encodingType) :
+ m_encoder(NULL),
+ m_counter(NULL)
+{
+ m_aSource = &aSource;
+ m_bSource = &bSource;
+ m_allocatedASource = false;
+ m_allocatedBSource = false;
+ InitEncoder(reverseDirection, encodingType);
+}
+
+/**
+ * Free the resources for an Encoder.
+ * Frees the FPGA resources associated with an Encoder.
+ */
+Encoder::~Encoder()
+{
+ if (m_allocatedASource) delete m_aSource;
+ if (m_allocatedBSource) delete m_bSource;
+ if (m_counter)
+ {
+ delete m_counter;
+ }
+ else
+ {
+ int32_t status = 0;
+ freeEncoder(m_encoder, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ }
+}
+
+/**
+ * The encoding scale factor 1x, 2x, or 4x, per the requested encodingType.
+ * Used to divide raw edge counts down to spec'd counts.
+ */
+int32_t Encoder::GetEncodingScale() { return m_encodingScale; }
+
+/**
+ * Gets the raw value from the encoder.
+ * The raw value is the actual count unscaled by the 1x, 2x, or 4x scale
+ * factor.
+ * @return Current raw count from the encoder
+ */
+int32_t Encoder::GetRaw()
+{
+ if (StatusIsFatal()) return 0;
+ int32_t value;
+ if (m_counter)
+ value = m_counter->Get();
+ else
+ {
+ int32_t status = 0;
+ value = getEncoder(m_encoder, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ }
+ return value;
+}
+
+/**
+ * Gets the current count.
+ * Returns the current count on the Encoder.
+ * This method compensates for the decoding type.
+ *
+ * @return Current count from the Encoder adjusted for the 1x, 2x, or 4x scale factor.
+ */
+int32_t Encoder::Get()
+{
+ if (StatusIsFatal()) return 0;
+ return (int32_t) (GetRaw() * DecodingScaleFactor());
+}
+
+/**
+ * Reset the Encoder distance to zero.
+ * Resets the current count to zero on the encoder.
+ */
+void Encoder::Reset()
+{
+ if (StatusIsFatal()) return;
+ if (m_counter)
+ m_counter->Reset();
+ else
+ {
+ int32_t status = 0;
+ resetEncoder(m_encoder, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ }
+}
+
+/**
+ * Returns the period of the most recent pulse.
+ * Returns the period of the most recent Encoder pulse in seconds.
+ * This method compensates for the decoding type.
+ *
+ * @deprecated Use GetRate() in favor of this method. This returns unscaled periods and GetRate() scales using value from SetDistancePerPulse().
+ *
+ * @return Period in seconds of the most recent pulse.
+ */
+double Encoder::GetPeriod()
+{
+ if (StatusIsFatal()) return 0.0;
+ if (m_counter)
+ {
+ return m_counter->GetPeriod() / DecodingScaleFactor();
+ }
+ else
+ {
+ int32_t status = 0;
+ double period = getEncoderPeriod(m_encoder, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ return period;
+ }
+
+}
+
+/**
+ * Sets the maximum period for stopped detection.
+ * Sets the value that represents the maximum period of the Encoder before it will assume
+ * that the attached device is stopped. This timeout allows users to determine if the wheels or
+ * other shaft has stopped rotating.
+ * This method compensates for the decoding type.
+ *
+ * @deprecated Use SetMinRate() in favor of this method. This takes unscaled periods and SetMinRate() scales using value from SetDistancePerPulse().
+ *
+ * @param maxPeriod The maximum time between rising and falling edges before the FPGA will
+ * report the device stopped. This is expressed in seconds.
+ */
+void Encoder::SetMaxPeriod(double maxPeriod)
+{
+ if (StatusIsFatal()) return;
+ if (m_counter)
+ {
+ m_counter->SetMaxPeriod(maxPeriod * DecodingScaleFactor());
+ }
+ else
+ {
+ int32_t status = 0;
+ setEncoderMaxPeriod(m_encoder, maxPeriod, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ }
+}
+
+/**
+ * Determine if the encoder is stopped.
+ * Using the MaxPeriod value, a boolean is returned that is true if the encoder is considered
+ * stopped and false if it is still moving. A stopped encoder is one where the most recent pulse
+ * width exceeds the MaxPeriod.
+ * @return True if the encoder is considered stopped.
+ */
+bool Encoder::GetStopped()
+{
+ if (StatusIsFatal()) return true;
+ if (m_counter)
+ {
+ return m_counter->GetStopped();
+ }
+ else
+ {
+ int32_t status = 0;
+ bool value = getEncoderStopped(m_encoder, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ return value;
+ }
+}
+
+/**
+ * The last direction the encoder value changed.
+ * @return The last direction the encoder value changed.
+ */
+bool Encoder::GetDirection()
+{
+ if (StatusIsFatal()) return false;
+ if (m_counter)
+ {
+ return m_counter->GetDirection();
+ }
+ else
+ {
+ int32_t status = 0;
+ bool value = getEncoderDirection(m_encoder, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ return value;
+ }
+}
+
+/**
+ * The scale needed to convert a raw counter value into a number of encoder pulses.
+ */
+double Encoder::DecodingScaleFactor()
+{
+ if (StatusIsFatal()) return 0.0;
+ switch (m_encodingType)
+ {
+ case k1X:
+ return 1.0;
+ case k2X:
+ return 0.5;
+ case k4X:
+ return 0.25;
+ default:
+ return 0.0;
+ }
+}
+
+/**
+ * Get the distance the robot has driven since the last reset.
+ *
+ * @return The distance driven since the last reset as scaled by the value from SetDistancePerPulse().
+ */
+double Encoder::GetDistance()
+{
+ if (StatusIsFatal()) return 0.0;
+ return GetRaw() * DecodingScaleFactor() * m_distancePerPulse;
+}
+
+/**
+ * Get the current rate of the encoder.
+ * Units are distance per second as scaled by the value from SetDistancePerPulse().
+ *
+ * @return The current rate of the encoder.
+ */
+double Encoder::GetRate()
+{
+ if (StatusIsFatal()) return 0.0;
+ return (m_distancePerPulse / GetPeriod());
+}
+
+/**
+ * Set the minimum rate of the device before the hardware reports it stopped.
+ *
+ * @param minRate The minimum rate. The units are in distance per second as scaled by the value from SetDistancePerPulse().
+ */
+void Encoder::SetMinRate(double minRate)
+{
+ if (StatusIsFatal()) return;
+ SetMaxPeriod(m_distancePerPulse / minRate);
+}
+
+/**
+ * Set the distance per pulse for this encoder.
+ * This sets the multiplier used to determine the distance driven based on the count value
+ * from the encoder.
+ * Do not include the decoding type in this scale. The library already compensates for the decoding type.
+ * Set this value based on the encoder's rated Pulses per Revolution and
+ * factor in gearing reductions following the encoder shaft.
+ * This distance can be in any units you like, linear or angular.
+ *
+ * @param distancePerPulse The scale factor that will be used to convert pulses to useful units.
+ */
+void Encoder::SetDistancePerPulse(double distancePerPulse)
+{
+ if (StatusIsFatal()) return;
+ m_distancePerPulse = distancePerPulse;
+}
+
+/**
+ * Set the direction sensing for this encoder.
+ * This sets the direction sensing on the encoder so that it could count in the correct
+ * software direction regardless of the mounting.
+ * @param reverseDirection true if the encoder direction should be reversed
+ */
+void Encoder::SetReverseDirection(bool reverseDirection)
+{
+ if (StatusIsFatal()) return;
+ if (m_counter)
+ {
+ m_counter->SetReverseDirection(reverseDirection);
+ }
+ else
+ {
+ int32_t status = 0;
+ setEncoderReverseDirection(m_encoder, reverseDirection, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ }
+}
+
+
+/**
+ * Set the Samples to Average which specifies the number of samples of the timer to
+ * average when calculating the period. Perform averaging to account for
+ * mechanical imperfections or as oversampling to increase resolution.
+ * @param samplesToAverage The number of samples to average from 1 to 127.
+ */
+void Encoder::SetSamplesToAverage(int samplesToAverage)
+{
+ if (samplesToAverage < 1 || samplesToAverage > 127)
+ {
+ wpi_setWPIErrorWithContext(ParameterOutOfRange, "Average counter values must be between 1 and 127");
+ }
+ int32_t status = 0;
+ switch (m_encodingType) {
+ case k4X:
+ setEncoderSamplesToAverage(m_encoder, samplesToAverage, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ break;
+ case k1X:
+ case k2X:
+ m_counter->SetSamplesToAverage(samplesToAverage);
+ break;
+ }
+}
+
+/**
+ * Get the Samples to Average which specifies the number of samples of the timer to
+ * average when calculating the period. Perform averaging to account for
+ * mechanical imperfections or as oversampling to increase resolution.
+ * @return SamplesToAverage The number of samples being averaged (from 1 to 127)
+ */
+int Encoder::GetSamplesToAverage()
+{
+ int result = 1;
+ int32_t status = 0;
+ switch (m_encodingType) {
+ case k4X:
+ result = getEncoderSamplesToAverage(m_encoder, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ break;
+ case k1X:
+ case k2X:
+ result = m_counter->GetSamplesToAverage();
+ break;
+ }
+ return result;
+}
+
+
+
+/**
+ * Set which parameter of the encoder you are using as a process control variable.
+ *
+ * @param pidSource An enum to select the parameter.
+ */
+void Encoder::SetPIDSourceParameter(PIDSourceParameter pidSource)
+{
+ if (StatusIsFatal()) return;
+ m_pidSource = pidSource;
+}
+
+/**
+ * Implement the PIDSource interface.
+ *
+ * @return The current value of the selected source parameter.
+ */
+double Encoder::PIDGet()
+{
+ if (StatusIsFatal()) return 0.0;
+ switch (m_pidSource)
+ {
+ case kDistance:
+ return GetDistance();
+ case kRate:
+ return GetRate();
+ default:
+ return 0.0;
+ }
+}
+
+/**
+ * Set the index source for the encoder. When this source is activated, the encoder count automatically resets.
+ *
+ * @param channel A DIO channel to set as the encoder index
+ * @param type The state that will cause the encoder to reset
+ */
+void Encoder::SetIndexSource(uint32_t channel, Encoder::IndexingType type) {
+ int32_t status = 0;
+ bool activeHigh = (type == kResetWhileHigh) || (type == kResetOnRisingEdge);
+ bool edgeSensitive = (type == kResetOnFallingEdge) || (type == kResetOnRisingEdge);
+
+ setEncoderIndexSource(m_encoder, channel, false, activeHigh, edgeSensitive, &status);
+ wpi_setGlobalErrorWithContext(status, getHALErrorMessage(status));
+}
+
+/**
+ * Set the index source for the encoder. When this source is activated, the encoder count automatically resets.
+ *
+ * @param channel A digital source to set as the encoder index
+ * @param type The state that will cause the encoder to reset
+ */
+void Encoder::SetIndexSource(DigitalSource *source, Encoder::IndexingType type) {
+ int32_t status = 0;
+ bool activeHigh = (type == kResetWhileHigh) || (type == kResetOnRisingEdge);
+ bool edgeSensitive = (type == kResetOnFallingEdge) || (type == kResetOnRisingEdge);
+
+ setEncoderIndexSource(m_encoder, source->GetChannelForRouting(), source->GetAnalogTriggerForRouting(), activeHigh,
+ edgeSensitive, &status);
+ wpi_setGlobalErrorWithContext(status, getHALErrorMessage(status));
+}
+
+/**
+ * Set the index source for the encoder. When this source is activated, the encoder count automatically resets.
+ *
+ * @param channel A digital source to set as the encoder index
+ * @param type The state that will cause the encoder to reset
+ */
+void Encoder::SetIndexSource(DigitalSource &source, Encoder::IndexingType type) {
+ SetIndexSource(&source, type);
+}
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/GearTooth.cpp b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/GearTooth.cpp
new file mode 100644
index 0000000..8e8f25d
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/GearTooth.cpp
@@ -0,0 +1,64 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+
+#include "GearTooth.h"
+
+constexpr double GearTooth::kGearToothThreshold;
+
+/**
+ * Common code called by the constructors.
+ */
+void GearTooth::EnableDirectionSensing(bool directionSensitive)
+{
+ if (directionSensitive)
+ {
+ SetPulseLengthMode(kGearToothThreshold);
+ }
+}
+
+/**
+ * Construct a GearTooth sensor given a channel.
+ *
+ * @param channel The DIO channel that the sensor is connected to. 0-9 are on-board, 10-25 are on the MXP.
+ * @param directionSensitive True to enable the pulse length decoding in hardware to specify count direction.
+ */
+GearTooth::GearTooth(uint32_t channel, bool directionSensitive)
+ : Counter(channel)
+{
+ EnableDirectionSensing(directionSensitive);
+}
+
+/**
+ * Construct a GearTooth sensor given a digital input.
+ * This should be used when sharing digital inputs.
+ *
+ * @param source A pointer to the existing DigitalSource object (such as a DigitalInput)
+ * @param directionSensitive True to enable the pulse length decoding in hardware to specify count direction.
+ */
+GearTooth::GearTooth(DigitalSource *source, bool directionSensitive)
+ : Counter(source)
+{
+ EnableDirectionSensing(directionSensitive);
+}
+
+/**
+ * Construct a GearTooth sensor given a digital input.
+ * This should be used when sharing digital inputs.
+ *
+ * @param source A reference to the existing DigitalSource object (such as a DigitalInput)
+ * @param directionSensitive True to enable the pulse length decoding in hardware to specify count direction.
+ */
+GearTooth::GearTooth(DigitalSource &source, bool directionSensitive): Counter(source)
+{
+ EnableDirectionSensing(directionSensitive);
+}
+
+/**
+ * Free the resources associated with a gear tooth sensor.
+ */
+GearTooth::~GearTooth()
+{
+}
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/Gyro.cpp b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/Gyro.cpp
new file mode 100644
index 0000000..b1650de
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/Gyro.cpp
@@ -0,0 +1,230 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+
+#include "Gyro.h"
+#include "AnalogInput.h"
+//#include "NetworkCommunication/UsageReporting.h"
+#include "Timer.h"
+#include "WPIErrors.h"
+#include <climits>
+const uint32_t Gyro::kOversampleBits;
+const uint32_t Gyro::kAverageBits;
+constexpr float Gyro::kSamplesPerSecond;
+constexpr float Gyro::kCalibrationSampleTime;
+constexpr float Gyro::kDefaultVoltsPerDegreePerSecond;
+
+/**
+ * Initialize the gyro.
+ * Calibrate the gyro by running for a number of samples and computing the center value.
+ * Then use the center value as the Accumulator center value for subsequent measurements.
+ * It's important to make sure that the robot is not moving while the centering calculations are
+ * in progress, this is typically done when the robot is first turned on while it's sitting at
+ * rest before the competition starts.
+ */
+void Gyro::InitGyro()
+{
+ if (!m_analog->IsAccumulatorChannel())
+ {
+ wpi_setWPIErrorWithContext(ParameterOutOfRange,
+ " channel (must be accumulator channel)");
+ if (m_channelAllocated)
+ {
+ delete m_analog;
+ m_analog = NULL;
+ }
+ return;
+ }
+
+ m_voltsPerDegreePerSecond = kDefaultVoltsPerDegreePerSecond;
+ m_analog->SetAverageBits(kAverageBits);
+ m_analog->SetOversampleBits(kOversampleBits);
+ float sampleRate = kSamplesPerSecond *
+ (1 << (kAverageBits + kOversampleBits));
+ m_analog->SetSampleRate(sampleRate);
+ Wait(1.0);
+
+ m_analog->InitAccumulator();
+
+ Wait(kCalibrationSampleTime);
+
+ int64_t value;
+ uint32_t count;
+ m_analog->GetAccumulatorOutput(&value, &count);
+
+ m_center = (uint32_t)((float)value / (float)count + .5);
+
+ m_offset = ((float)value / (float)count) - (float)m_center;
+ m_analog->SetAccumulatorCenter(m_center);
+ m_analog->ResetAccumulator();
+
+ SetDeadband(0.0f);
+
+ SetPIDSourceParameter(kAngle);
+
+ HALReport(HALUsageReporting::kResourceType_Gyro, m_analog->GetChannel());
+}
+
+/**
+ * Gyro constructor using the Analog Input channel number.
+ *
+ * @param channel The analog channel the gyro is connected to. Gyros
+ can only be used on on-board Analog Inputs 0-1.
+ */
+Gyro::Gyro(int32_t channel)
+{
+ m_analog = new AnalogInput(channel);
+ m_channelAllocated = true;
+ InitGyro();
+}
+
+/**
+ * Gyro constructor with a precreated AnalogInput object.
+ * Use this constructor when the analog channel needs to be shared.
+ * This object will not clean up the AnalogInput object when using this constructor.
+ * Gyros can only be used on on-board channels 0-1.
+ * @param channel A pointer to the AnalogInput object that the gyro is connected to.
+ */
+Gyro::Gyro(AnalogInput *channel)
+{
+ m_analog = channel;
+ m_channelAllocated = false;
+ if (channel == NULL)
+ {
+ wpi_setWPIError(NullParameter);
+ }
+ else
+ {
+ InitGyro();
+ }
+}
+
+/**
+ * Gyro constructor with a precreated AnalogInput object.
+ * Use this constructor when the analog channel needs to be shared.
+ * This object will not clean up the AnalogInput object when using this constructor
+ * @param channel A reference to the AnalogInput object that the gyro is connected to.
+ */
+Gyro::Gyro(AnalogInput &channel)
+{
+ m_analog = &channel;
+ m_channelAllocated = false;
+ InitGyro();
+}
+
+/**
+ * Reset the gyro.
+ * Resets the gyro to a heading of zero. This can be used if there is significant
+ * drift in the gyro and it needs to be recalibrated after it has been running.
+ */
+void Gyro::Reset()
+{
+ m_analog->ResetAccumulator();
+}
+
+/**
+ * Delete (free) the accumulator and the analog components used for the gyro.
+ */
+Gyro::~Gyro()
+{
+ if (m_channelAllocated)
+ delete m_analog;
+}
+
+/**
+ * Return the actual angle in degrees that the robot is currently facing.
+ *
+ * The angle is based on the current accumulator value corrected by the oversampling rate, the
+ * gyro type and the A/D calibration values.
+ * The angle is continuous, that is it will continue from 360->361 degrees. This allows algorithms that wouldn't
+ * want to see a discontinuity in the gyro output as it sweeps from 360 to 0 on the second time around.
+ *
+ * @return the current heading of the robot in degrees. This heading is based on integration
+ * of the returned rate from the gyro.
+ */
+float Gyro::GetAngle( void )
+{
+ int64_t rawValue;
+ uint32_t count;
+ m_analog->GetAccumulatorOutput(&rawValue, &count);
+
+ int64_t value = rawValue - (int64_t)((float)count * m_offset);
+
+ double scaledValue = value * 1e-9 * (double)m_analog->GetLSBWeight() * (double)(1 << m_analog->GetAverageBits()) /
+ (m_analog->GetSampleRate() * m_voltsPerDegreePerSecond);
+
+ return (float)scaledValue;
+}
+
+
+/**
+ * Return the rate of rotation of the gyro
+ *
+ * The rate is based on the most recent reading of the gyro analog value
+ *
+ * @return the current rate in degrees per second
+ */
+double Gyro::GetRate( void )
+{
+ return (m_analog->GetAverageValue() - ((double)m_center + m_offset)) * 1e-9 * m_analog->GetLSBWeight()
+ / ((1 << m_analog->GetOversampleBits()) * m_voltsPerDegreePerSecond);
+}
+
+
+/**
+ * Set the gyro sensitivity.
+ * This takes the number of volts/degree/second sensitivity of the gyro and uses it in subsequent
+ * calculations to allow the code to work with multiple gyros. This value is typically found in
+ * the gyro datasheet.
+ *
+ * @param voltsPerDegreePerSecond The sensitivity in Volts/degree/second
+ */
+void Gyro::SetSensitivity( float voltsPerDegreePerSecond )
+{
+ m_voltsPerDegreePerSecond = voltsPerDegreePerSecond;
+}
+
+/**
+ * Set the size of the neutral zone. Any voltage from the gyro less than this
+ * amount from the center is considered stationary. Setting a deadband will
+ * decrease the amount of drift when the gyro isn't rotating, but will make it
+ * less accurate.
+ *
+ * @param volts The size of the deadband in volts
+ */
+void Gyro::SetDeadband( float volts ) {
+ int32_t deadband = volts * 1e9 / m_analog->GetLSBWeight() * (1 << m_analog->GetOversampleBits());
+ m_analog->SetAccumulatorDeadband(deadband);
+}
+
+
+/**
+ * Sets the type of output to use with the WPILib PID class
+ * The gyro supports using either rate or angle for PID calculations
+ */
+void Gyro::SetPIDSourceParameter(PIDSourceParameter pidSource)
+{
+ if(pidSource == 0 || pidSource > 2)
+ wpi_setWPIErrorWithContext(ParameterOutOfRange, "Gyro pidSource");
+ m_pidSource = pidSource;
+}
+
+/**
+ * Get the PIDOutput for the PIDSource base object. Can be set to return
+ * angle or rate using SetPIDSourceParameter(). Defaults to angle.
+ *
+ * @return The PIDOutput (angle or rate, defaults to angle)
+ */
+double Gyro::PIDGet()
+{
+ switch(m_pidSource){
+ case kRate:
+ return GetRate();
+ case kAngle:
+ return GetAngle();
+ default:
+ return 0;
+ }
+}
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/I2C.cpp b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/I2C.cpp
new file mode 100644
index 0000000..19e3f4f
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/I2C.cpp
@@ -0,0 +1,221 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+
+#include "I2C.h"
+#include "HAL/HAL.hpp"
+#include "HAL/Digital.hpp"
+#include "WPIErrors.h"
+
+/**
+ * Constructor.
+ *
+ * @param port The I2C port to which the device is connected.
+ * @param deviceAddress The address of the device on the I2C bus.
+ */
+I2C::I2C(Port port, uint8_t deviceAddress) :
+ m_port (port)
+ , m_deviceAddress (deviceAddress)
+{
+ int32_t status = 0;
+ i2CInitialize(m_port, &status);
+ //wpi_setErrorWithContext(status, getHALErrorMessage(status));
+
+ HALReport(HALUsageReporting::kResourceType_I2C, deviceAddress);
+}
+
+/**
+ * Destructor.
+ */
+I2C::~I2C()
+{
+ i2CClose(m_port);
+}
+
+/**
+ * Generic transaction.
+ *
+ * This is a lower-level interface to the I2C hardware giving you more control over each transaction.
+ *
+ * @param dataToSend Buffer of data to send as part of the transaction.
+ * @param sendSize Number of bytes to send as part of the transaction. [0..6]
+ * @param dataReceived Buffer to read data into.
+ * @param receiveSize Number of bytes to read from the device. [0..7]
+ * @return Transfer Aborted... false for success, true for aborted.
+ */
+bool I2C::Transaction(uint8_t *dataToSend, uint8_t sendSize, uint8_t *dataReceived, uint8_t receiveSize)
+{
+ if (sendSize > 6) // Optional, provides better error message.
+ {
+ wpi_setWPIErrorWithContext(ParameterOutOfRange, "sendSize");
+ return true;
+ }
+ if (receiveSize > 7) // Optional, provides better error message.
+ {
+ wpi_setWPIErrorWithContext(ParameterOutOfRange, "receiveSize");
+ return true;
+ }
+
+ int32_t status = 0;
+ status = i2CTransaction(m_port, m_deviceAddress,
+ dataToSend, sendSize, dataReceived, receiveSize);
+ //wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ return status < 0;
+}
+
+/**
+ * Attempt to address a device on the I2C bus.
+ *
+ * This allows you to figure out if there is a device on the I2C bus that
+ * responds to the address specified in the constructor.
+ *
+ * @return Transfer Aborted... false for success, true for aborted.
+ */
+bool I2C::AddressOnly()
+{
+ int32_t status = 0;
+ status = Transaction(NULL, 0, NULL, 0);
+ return status < 0;
+}
+
+/**
+ * Execute a write transaction with the device.
+ *
+ * Write a single byte to a register on a device and wait until the
+ * transaction is complete.
+ *
+ * @param registerAddress The address of the register on the device to be written.
+ * @param data The byte to write to the register on the device.
+ * @return Transfer Aborted... false for success, true for aborted.
+ */
+bool I2C::Write(uint8_t registerAddress, uint8_t data)
+{
+ uint8_t buffer[2];
+ buffer[0] = registerAddress;
+ buffer[1] = data;
+ int32_t status = 0;
+ status = i2CWrite(m_port, m_deviceAddress, buffer, sizeof(buffer));
+ return status < 0;
+}
+
+/**
+ * Execute a bulk write transaction with the device.
+ *
+ * Write multiple bytes to a device and wait until the
+ * transaction is complete.
+ *
+ * @param data The data to write to the register on the device.
+ * @param count The number of bytes to be written.
+ * @return Transfer Aborted... false for success, true for aborted.
+ */
+bool I2C::WriteBulk(uint8_t* data, uint8_t count)
+{
+ int32_t status = 0;
+ status = i2CWrite(m_port, m_deviceAddress, data, count);
+ return status < 0;
+}
+
+/**
+ * Execute a read transaction with the device.
+ *
+ * Read 1 to 7 bytes from a device.
+ * Most I2C devices will auto-increment the register pointer internally
+ * allowing you to read up to 7 consecutive registers on a device in a
+ * single transaction.
+ *
+ * @param registerAddress The register to read first in the transaction.
+ * @param count The number of bytes to read in the transaction. [1..7]
+ * @param buffer A pointer to the array of bytes to store the data read from the device.
+ * @return Transfer Aborted... false for success, true for aborted.
+ */
+bool I2C::Read(uint8_t registerAddress, uint8_t count, uint8_t *buffer)
+{
+ if (count < 1 || count > 7)
+ {
+ wpi_setWPIErrorWithContext(ParameterOutOfRange, "count");
+ return true;
+ }
+ if (buffer == NULL)
+ {
+ wpi_setWPIErrorWithContext(NullParameter, "buffer");
+ return true;
+ }
+ int32_t status = 0;
+ status = Transaction(®isterAddress, sizeof(registerAddress), buffer, count);
+ return status < 0;
+}
+
+/**
+ * Execute a read only transaction with the device.
+ *
+ * Read 1 to 7 bytes from a device. This method does not write any data to prompt
+ * the device.
+ *
+ * @param buffer
+ * A pointer to the array of bytes to store the data read from
+ * the device.
+ * @param count
+ * The number of bytes to read in the transaction. [1..7]
+ * @return Transfer Aborted... false for success, true for aborted.
+ */
+bool I2C::ReadOnly(uint8_t count, uint8_t *buffer)
+{
+ if (count < 1 || count > 7)
+ {
+ wpi_setWPIErrorWithContext(ParameterOutOfRange, "count");
+ return true;
+ }
+ if (buffer == NULL)
+ {
+ wpi_setWPIErrorWithContext(NullParameter, "buffer");
+ return true;
+ }
+ int32_t status = 0;
+ status = i2CRead(m_port, m_deviceAddress, buffer, count);
+ return status < 0;
+}
+
+/**
+ * Send a broadcast write to all devices on the I2C bus.
+ *
+ * This is not currently implemented!
+ *
+ * @param registerAddress The register to write on all devices on the bus.
+ * @param data The value to write to the devices.
+ */
+void I2C::Broadcast(uint8_t registerAddress, uint8_t data)
+{
+}
+
+/**
+ * Verify that a device's registers contain expected values.
+ *
+ * Most devices will have a set of registers that contain a known value that
+ * can be used to identify them. This allows an I2C device driver to easily
+ * verify that the device contains the expected value.
+ *
+ * @pre The device must support and be configured to use register auto-increment.
+ *
+ * @param registerAddress The base register to start reading from the device.
+ * @param count The size of the field to be verified.
+ * @param expected A buffer containing the values expected from the device.
+ */
+bool I2C::VerifySensor(uint8_t registerAddress, uint8_t count, const uint8_t *expected)
+{
+ // TODO: Make use of all 7 read bytes
+ uint8_t deviceData[4];
+ for (uint8_t i=0, curRegisterAddress = registerAddress; i < count; i+=4, curRegisterAddress+=4)
+ {
+ uint8_t toRead = count - i < 4 ? count - i : 4;
+ // Read the chunk of data. Return false if the sensor does not respond.
+ if (Read(curRegisterAddress, toRead, deviceData)) return false;
+
+ for (uint8_t j=0; j<toRead; j++)
+ {
+ if(deviceData[j] != expected[i + j]) return false;
+ }
+ }
+ return true;
+}
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/Internal/HardwareHLReporting.cpp b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/Internal/HardwareHLReporting.cpp
new file mode 100644
index 0000000..9c21b7a
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/Internal/HardwareHLReporting.cpp
@@ -0,0 +1,12 @@
+
+#include "Internal/HardwareHLReporting.h"
+#include "HAL/HAL.hpp"
+
+void HardwareHLReporting::ReportScheduler() {
+ HALReport(HALUsageReporting::kResourceType_Command,
+ HALUsageReporting::kCommand_Scheduler);
+}
+
+void HardwareHLReporting::ReportSmartDashboard() {
+ HALReport(HALUsageReporting::kResourceType_SmartDashboard, 0);
+}
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/InterruptableSensorBase.cpp b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/InterruptableSensorBase.cpp
new file mode 100644
index 0000000..512c567
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/InterruptableSensorBase.cpp
@@ -0,0 +1,207 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+
+#include "InterruptableSensorBase.h"
+#include "Utility.h"
+#include "WPIErrors.h"
+
+Resource *InterruptableSensorBase::m_interrupts = NULL;
+
+InterruptableSensorBase::InterruptableSensorBase()
+{
+ m_interrupt = NULL;
+ Resource::CreateResourceObject(&m_interrupts, interrupt_kNumSystems);
+}
+
+InterruptableSensorBase::~InterruptableSensorBase()
+{
+
+}
+
+/**
+* Request one of the 8 interrupts asynchronously on this digital input.
+* Request interrupts in asynchronous mode where the user's interrupt handler will be
+* called when the interrupt fires. Users that want control over the thread priority
+* should use the synchronous method with their own spawned thread.
+* The default is interrupt on rising edges only.
+*/
+void InterruptableSensorBase::RequestInterrupts(InterruptHandlerFunction handler, void *param)
+{
+ if (StatusIsFatal()) return;
+ uint32_t index = m_interrupts->Allocate("Async Interrupt");
+ if (index == ~0ul)
+ {
+ CloneError(m_interrupts);
+ return;
+ }
+ m_interruptIndex = index;
+
+ // Creates a manager too
+ AllocateInterrupts(false);
+
+ int32_t status = 0;
+ requestInterrupts(m_interrupt, GetModuleForRouting(), GetChannelForRouting(),
+ GetAnalogTriggerForRouting(), &status);
+ SetUpSourceEdge(true, false);
+ attachInterruptHandler(m_interrupt, handler, param, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+}
+
+/**
+* Request one of the 8 interrupts synchronously on this digital input.
+* Request interrupts in synchronous mode where the user program will have to explicitly
+* wait for the interrupt to occur using WaitForInterrupt.
+* The default is interrupt on rising edges only.
+*/
+void InterruptableSensorBase::RequestInterrupts()
+{
+ if (StatusIsFatal()) return;
+ uint32_t index = m_interrupts->Allocate("Sync Interrupt");
+ if (index == ~0ul)
+ {
+ CloneError(m_interrupts);
+ return;
+ }
+ m_interruptIndex = index;
+
+ AllocateInterrupts(true);
+
+ int32_t status = 0;
+ requestInterrupts(m_interrupt, GetModuleForRouting(), GetChannelForRouting(),
+ GetAnalogTriggerForRouting(), &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ SetUpSourceEdge(true, false);
+}
+
+void InterruptableSensorBase::AllocateInterrupts(bool watcher)
+{
+ wpi_assert(m_interrupt == NULL);
+ // Expects the calling leaf class to allocate an interrupt index.
+ int32_t status = 0;
+ m_interrupt = initializeInterrupts(m_interruptIndex, watcher, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+}
+
+/**
+ * Cancel interrupts on this device.
+ * This deallocates all the chipobject structures and disables any interrupts.
+ */
+void InterruptableSensorBase::CancelInterrupts()
+{
+ if (StatusIsFatal()) return;
+ wpi_assert(m_interrupt != NULL);
+ int32_t status = 0;
+ cleanInterrupts(m_interrupt, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ m_interrupt = NULL;
+ m_interrupts->Free(m_interruptIndex);
+}
+
+/**
+ * In synchronous mode, wait for the defined interrupt to occur. You should <b>NOT</b> attempt to read the
+ * sensor from another thread while waiting for an interrupt. This is not threadsafe, and can cause
+ * memory corruption
+ * @param timeout Timeout in seconds
+ * @param ignorePrevious If true, ignore interrupts that happened before
+ * WaitForInterrupt was called.
+ * @return What interrupts fired
+ */
+InterruptableSensorBase::WaitResult InterruptableSensorBase::WaitForInterrupt(float timeout, bool ignorePrevious)
+{
+ if (StatusIsFatal()) return InterruptableSensorBase::kTimeout;
+ wpi_assert(m_interrupt != NULL);
+ int32_t status = 0;
+ uint32_t result;
+
+ result = waitForInterrupt(m_interrupt, timeout, ignorePrevious, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+
+ return static_cast<WaitResult>(result);
+}
+
+/**
+ * Enable interrupts to occur on this input.
+ * Interrupts are disabled when the RequestInterrupt call is made. This gives time to do the
+ * setup of the other options before starting to field interrupts.
+ */
+void InterruptableSensorBase::EnableInterrupts()
+{
+ if (StatusIsFatal()) return;
+ wpi_assert(m_interrupt != NULL);
+ int32_t status = 0;
+ enableInterrupts(m_interrupt, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+}
+
+/**
+ * Disable Interrupts without without deallocating structures.
+ */
+void InterruptableSensorBase::DisableInterrupts()
+{
+ if (StatusIsFatal()) return;
+ wpi_assert(m_interrupt != NULL);
+ int32_t status = 0;
+ disableInterrupts(m_interrupt, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+}
+
+/**
+ * Return the timestamp for the rising interrupt that occurred most recently.
+ * This is in the same time domain as GetClock().
+ * The rising-edge interrupt should be enabled with
+ * {@link #DigitalInput.SetUpSourceEdge}
+ * @return Timestamp in seconds since boot.
+ */
+double InterruptableSensorBase::ReadRisingTimestamp()
+{
+ if (StatusIsFatal()) return 0.0;
+ wpi_assert(m_interrupt != NULL);
+ int32_t status = 0;
+ double timestamp = readRisingTimestamp(m_interrupt, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ return timestamp;
+}
+
+/**
+ * Return the timestamp for the falling interrupt that occurred most recently.
+ * This is in the same time domain as GetClock().
+ * The falling-edge interrupt should be enabled with
+ * {@link #DigitalInput.SetUpSourceEdge}
+ * @return Timestamp in seconds since boot.
+*/
+double InterruptableSensorBase::ReadFallingTimestamp()
+{
+ if (StatusIsFatal()) return 0.0;
+ wpi_assert(m_interrupt != NULL);
+ int32_t status = 0;
+ double timestamp = readFallingTimestamp(m_interrupt, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ return timestamp;
+}
+
+/**
+ * Set which edge to trigger interrupts on
+ *
+ * @param risingEdge
+ * true to interrupt on rising edge
+ * @param fallingEdge
+ * true to interrupt on falling edge
+ */
+void InterruptableSensorBase::SetUpSourceEdge(bool risingEdge, bool fallingEdge)
+{
+ if (StatusIsFatal()) return;
+ if (m_interrupt == NULL)
+ {
+ wpi_setWPIErrorWithContext(NullParameter, "You must call RequestInterrupts before SetUpSourceEdge");
+ return;
+ }
+ if (m_interrupt != NULL)
+ {
+ int32_t status = 0;
+ setInterruptUpSourceEdge(m_interrupt, risingEdge, fallingEdge, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ }
+}
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/Jaguar.cpp b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/Jaguar.cpp
new file mode 100644
index 0000000..ebc9ae2
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/Jaguar.cpp
@@ -0,0 +1,86 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+
+
+#include "Jaguar.h"
+//#include "NetworkCommunication/UsageReporting.h"
+
+/**
+ * Common initialization code called by all constructors.
+ */
+void Jaguar::InitJaguar()
+{
+ /**
+ * Input profile defined by Luminary Micro.
+ *
+ * Full reverse ranges from 0.671325ms to 0.6972211ms
+ * Proportional reverse ranges from 0.6972211ms to 1.4482078ms
+ * Neutral ranges from 1.4482078ms to 1.5517922ms
+ * Proportional forward ranges from 1.5517922ms to 2.3027789ms
+ * Full forward ranges from 2.3027789ms to 2.328675ms
+ */
+ SetBounds(2.31, 1.55, 1.507, 1.454, .697);
+ SetPeriodMultiplier(kPeriodMultiplier_1X);
+ SetRaw(m_centerPwm);
+ SetZeroLatch();
+
+ HALReport(HALUsageReporting::kResourceType_Jaguar, GetChannel());
+}
+
+/**
+ * Constructor for a Jaguar connected via PWM
+ * @param channel The PWM channel that the Jaguar is attached to. 0-9 are on-board, 10-19 are on the MXP port
+ */
+Jaguar::Jaguar(uint32_t channel) : SafePWM(channel)
+{
+ InitJaguar();
+}
+
+Jaguar::~Jaguar()
+{
+}
+
+/**
+ * Set the PWM value.
+ *
+ * The PWM value is set using a range of -1.0 to 1.0, appropriately
+ * scaling the value for the FPGA.
+ *
+ * @param speed The speed value between -1.0 and 1.0 to set.
+ * @param syncGroup Unused interface.
+ */
+void Jaguar::Set(float speed, uint8_t syncGroup)
+{
+ SetSpeed(speed);
+}
+
+/**
+ * Get the recently set value of the PWM.
+ *
+ * @return The most recently set value for the PWM between -1.0 and 1.0.
+ */
+float Jaguar::Get()
+{
+ return GetSpeed();
+}
+
+/**
+ * Common interface for disabling a motor.
+ */
+void Jaguar::Disable()
+{
+ SetRaw(kPwmDisabled);
+}
+
+/**
+ * Write out the PID value as seen in the PIDOutput base object.
+ *
+ * @param output Write out the PWM value as was found in the PIDController
+ */
+void Jaguar::PIDWrite(float output)
+{
+ Set(output);
+}
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/Joystick.cpp b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/Joystick.cpp
new file mode 100644
index 0000000..790f0d7
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/Joystick.cpp
@@ -0,0 +1,389 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+
+#include "Joystick.h"
+#include "DriverStation.h"
+//#include "NetworkCommunication/UsageReporting.h"
+#include "WPIErrors.h"
+#include <math.h>
+
+const uint32_t Joystick::kDefaultXAxis;
+const uint32_t Joystick::kDefaultYAxis;
+const uint32_t Joystick::kDefaultZAxis;
+const uint32_t Joystick::kDefaultTwistAxis;
+const uint32_t Joystick::kDefaultThrottleAxis;
+const uint32_t Joystick::kDefaultTriggerButton;
+const uint32_t Joystick::kDefaultTopButton;
+static Joystick *joysticks[DriverStation::kJoystickPorts];
+static bool joySticksInitialized = false;
+
+/**
+ * Construct an instance of a joystick.
+ * The joystick index is the usb port on the drivers station.
+ *
+ * @param port The port on the driver station that the joystick is plugged into (0-5).
+ */
+Joystick::Joystick(uint32_t port)
+ : m_ds (NULL)
+ , m_port (port)
+ , m_axes (NULL)
+ , m_buttons (NULL)
+ , m_outputs (0)
+ , m_leftRumble (0)
+ , m_rightRumble (0)
+{
+ InitJoystick(kNumAxisTypes, kNumButtonTypes);
+
+ m_axes[kXAxis] = kDefaultXAxis;
+ m_axes[kYAxis] = kDefaultYAxis;
+ m_axes[kZAxis] = kDefaultZAxis;
+ m_axes[kTwistAxis] = kDefaultTwistAxis;
+ m_axes[kThrottleAxis] = kDefaultThrottleAxis;
+
+ m_buttons[kTriggerButton] = kDefaultTriggerButton;
+ m_buttons[kTopButton] = kDefaultTopButton;
+
+ HALReport(HALUsageReporting::kResourceType_Joystick, port);
+}
+
+/**
+ * Version of the constructor to be called by sub-classes.
+ *
+ * This constructor allows the subclass to configure the number of constants
+ * for axes and buttons.
+ *
+ * @param port The port on the driver station that the joystick is plugged into.
+ * @param numAxisTypes The number of axis types in the enum.
+ * @param numButtonTypes The number of button types in the enum.
+ */
+Joystick::Joystick(uint32_t port, uint32_t numAxisTypes, uint32_t numButtonTypes)
+ : m_ds (NULL)
+ , m_port (port)
+ , m_axes (NULL)
+ , m_buttons (NULL)
+{
+ InitJoystick(numAxisTypes, numButtonTypes);
+}
+
+void Joystick::InitJoystick(uint32_t numAxisTypes, uint32_t numButtonTypes)
+{
+ if ( !joySticksInitialized )
+ {
+ for (unsigned i = 0; i < DriverStation::kJoystickPorts; i++)
+ joysticks[i] = NULL;
+ joySticksInitialized = true;
+ }
+ if (m_port >= DriverStation::kJoystickPorts) {
+ wpi_setWPIError(BadJoystickIndex);
+ } else {
+ joysticks[m_port] = this;
+ }
+
+ m_ds = DriverStation::GetInstance();
+ m_axes = new uint32_t[numAxisTypes];
+ m_buttons = new uint32_t[numButtonTypes];
+}
+
+Joystick * Joystick::GetStickForPort(uint32_t port)
+{
+ Joystick *stick = joysticks[port];
+ if (stick == NULL)
+ {
+ stick = new Joystick(port);
+ joysticks[port] = stick;
+ }
+ return stick;
+}
+
+Joystick::~Joystick()
+{
+ delete [] m_buttons;
+ delete [] m_axes;
+}
+
+/**
+ * Get the X value of the joystick.
+ * This depends on the mapping of the joystick connected to the current port.
+ */
+float Joystick::GetX(JoystickHand hand)
+{
+ return GetRawAxis(m_axes[kXAxis]);
+}
+
+/**
+ * Get the Y value of the joystick.
+ * This depends on the mapping of the joystick connected to the current port.
+ */
+float Joystick::GetY(JoystickHand hand)
+{
+ return GetRawAxis(m_axes[kYAxis]);
+}
+
+/**
+ * Get the Z value of the current joystick.
+ * This depends on the mapping of the joystick connected to the current port.
+ */
+float Joystick::GetZ()
+{
+ return GetRawAxis(m_axes[kZAxis]);
+}
+
+/**
+ * Get the twist value of the current joystick.
+ * This depends on the mapping of the joystick connected to the current port.
+ */
+float Joystick::GetTwist()
+{
+ return GetRawAxis(m_axes[kTwistAxis]);
+}
+
+/**
+ * Get the throttle value of the current joystick.
+ * This depends on the mapping of the joystick connected to the current port.
+ */
+float Joystick::GetThrottle()
+{
+ return GetRawAxis(m_axes[kThrottleAxis]);
+}
+
+/**
+ * Get the value of the axis.
+ *
+ * @param axis The axis to read, starting at 0.
+ * @return The value of the axis.
+ */
+float Joystick::GetRawAxis(uint32_t axis)
+{
+ return m_ds->GetStickAxis(m_port, axis);
+}
+
+/**
+ * For the current joystick, return the axis determined by the argument.
+ *
+ * This is for cases where the joystick axis is returned programatically, otherwise one of the
+ * previous functions would be preferable (for example GetX()).
+ *
+ * @param axis The axis to read.
+ * @return The value of the axis.
+ */
+float Joystick::GetAxis(AxisType axis)
+{
+ switch(axis)
+ {
+ case kXAxis: return this->GetX();
+ case kYAxis: return this->GetY();
+ case kZAxis: return this->GetZ();
+ case kTwistAxis: return this->GetTwist();
+ case kThrottleAxis: return this->GetThrottle();
+ default:
+ wpi_setWPIError(BadJoystickAxis);
+ return 0.0;
+ }
+}
+
+/**
+ * Read the state of the trigger on the joystick.
+ *
+ * Look up which button has been assigned to the trigger and read its state.
+ *
+ * @param hand This parameter is ignored for the Joystick class and is only here to complete the GenericHID interface.
+ * @return The state of the trigger.
+ */
+bool Joystick::GetTrigger(JoystickHand hand)
+{
+ return GetRawButton(m_buttons[kTriggerButton]);
+}
+
+/**
+ * Read the state of the top button on the joystick.
+ *
+ * Look up which button has been assigned to the top and read its state.
+ *
+ * @param hand This parameter is ignored for the Joystick class and is only here to complete the GenericHID interface.
+ * @return The state of the top button.
+ */
+bool Joystick::GetTop(JoystickHand hand)
+{
+ return GetRawButton(m_buttons[kTopButton]);
+}
+
+/**
+ * This is not supported for the Joystick.
+ * This method is only here to complete the GenericHID interface.
+ */
+bool Joystick::GetBumper(JoystickHand hand)
+{
+ // Joysticks don't have bumpers.
+ return false;
+}
+
+/**
+ * Get the button value (starting at button 1)
+ *
+ * The buttons are returned in a single 16 bit value with one bit representing the state
+ * of each button. The appropriate button is returned as a boolean value.
+ *
+ * @param button The button number to be read (starting at 1)
+ * @return The state of the button.
+ **/
+bool Joystick::GetRawButton(uint32_t button)
+{
+ return m_ds->GetStickButton(m_port, button);
+}
+
+/**
+ * Get the state of a POV on the joystick.
+ *
+ * @param pov The index of the POV to read (starting at 0)
+ * @return the angle of the POV in degrees, or -1 if the POV is not pressed.
+ */
+int Joystick::GetPOV(uint32_t pov) {
+ return m_ds->GetStickPOV(m_port, pov);
+}
+
+/**
+ * Get buttons based on an enumerated type.
+ *
+ * The button type will be looked up in the list of buttons and then read.
+ *
+ * @param button The type of button to read.
+ * @return The state of the button.
+ */
+bool Joystick::GetButton(ButtonType button)
+{
+ switch (button)
+ {
+ case kTriggerButton: return GetTrigger();
+ case kTopButton: return GetTop();
+ default:
+ return false;
+ }
+}
+
+/**
+ * Get the number of axis for a joystick
+ *
+ * @return the number of axis for the current joystick
+ */
+int Joystick::GetAxisCount()
+{
+ return m_ds->GetStickAxisCount(m_port);
+}
+
+/**
+ * Get the number of axis for a joystick
+ *
+* @return the number of buttons on the current joystick
+ */
+int Joystick::GetButtonCount()
+{
+ return m_ds->GetStickButtonCount(m_port);
+}
+
+/**
+ * Get the number of axis for a joystick
+ *
+ * @return then umber of POVs for the current joystick
+ */
+int Joystick::GetPOVCount()
+{
+ return m_ds->GetStickPOVCount(m_port);
+}
+
+
+/**
+ * Get the channel currently associated with the specified axis.
+ *
+ * @param axis The axis to look up the channel for.
+ * @return The channel fr the axis.
+ */
+uint32_t Joystick::GetAxisChannel(AxisType axis)
+{
+ return m_axes[axis];
+}
+
+/**
+ * Set the channel associated with a specified axis.
+ *
+ * @param axis The axis to set the channel for.
+ * @param channel The channel to set the axis to.
+ */
+void Joystick::SetAxisChannel(AxisType axis, uint32_t channel)
+{
+ m_axes[axis] = channel;
+}
+
+/**
+ * Get the magnitude of the direction vector formed by the joystick's
+ * current position relative to its origin
+ *
+ * @return The magnitude of the direction vector
+ */
+float Joystick::GetMagnitude(){
+ return sqrt(pow(GetX(),2) + pow(GetY(),2) );
+}
+
+/**
+ * Get the direction of the vector formed by the joystick and its origin
+ * in radians
+ *
+ * @return The direction of the vector in radians
+ */
+float Joystick::GetDirectionRadians(){
+ return atan2(GetX(), -GetY());
+}
+
+/**
+ * Get the direction of the vector formed by the joystick and its origin
+ * in degrees
+ *
+ * uses acos(-1) to represent Pi due to absence of readily accessible Pi
+ * constant in C++
+ *
+ * @return The direction of the vector in degrees
+ */
+float Joystick::GetDirectionDegrees(){
+ return (180/acos(-1))*GetDirectionRadians();
+}
+
+/**
+ * Set the rumble output for the joystick. The DS currently supports 2 rumble values,
+ * left rumble and right rumble
+ * @param type Which rumble value to set
+ * @param value The normalized value (0 to 1) to set the rumble to
+ */
+void Joystick::SetRumble(RumbleType type, float value) {
+ if (value < 0)
+ value = 0;
+ else if (value > 1)
+ value = 1;
+ if (type == kLeftRumble)
+ m_leftRumble = value*65535;
+ else
+ m_rightRumble = value*65535;
+ HALSetJoystickOutputs(m_port, m_outputs, m_leftRumble, m_rightRumble);
+}
+
+/**
+ * Set a single HID output value for the joystick.
+ * @param outputNumber The index of the output to set (1-32)
+ * @param value The value to set the output to
+ */
+
+void Joystick::SetOutput(uint8_t outputNumber, bool value) {
+ m_outputs = (m_outputs & ~(1 << (outputNumber-1))) | (value << (outputNumber-1));
+
+ HALSetJoystickOutputs(m_port, m_outputs, m_leftRumble, m_rightRumble);
+}
+
+/**
+ * Set all HID output values for the joystick.
+ * @param value The 32 bit output value (1 bit for each output)
+ */
+void Joystick::SetOutputs(uint32_t value) {
+ m_outputs = value;
+ HALSetJoystickOutputs(m_port, m_outputs, m_leftRumble, m_rightRumble);
+}
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/MotorSafetyHelper.cpp b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/MotorSafetyHelper.cpp
new file mode 100644
index 0000000..6bb7f48
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/MotorSafetyHelper.cpp
@@ -0,0 +1,156 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+
+#include "MotorSafetyHelper.h"
+
+#include "DriverStation.h"
+#include "MotorSafety.h"
+#include "Timer.h"
+#include "WPIErrors.h"
+
+#include <stdio.h>
+
+MotorSafetyHelper *MotorSafetyHelper::m_headHelper = NULL;
+ReentrantMutex MotorSafetyHelper::m_listMutex;
+
+/**
+ * The constructor for a MotorSafetyHelper object.
+ * The helper object is constructed for every object that wants to implement the Motor
+ * Safety protocol. The helper object has the code to actually do the timing and call the
+ * motors Stop() method when the timeout expires. The motor object is expected to call the
+ * Feed() method whenever the motors value is updated.
+ * @param safeObject a pointer to the motor object implementing MotorSafety. This is used
+ * to call the Stop() method on the motor.
+ */
+MotorSafetyHelper::MotorSafetyHelper(MotorSafety *safeObject)
+{
+ m_safeObject = safeObject;
+ m_enabled = false;
+ m_expiration = DEFAULT_SAFETY_EXPIRATION;
+ m_stopTime = Timer::GetFPGATimestamp();
+
+ ::std::unique_lock<ReentrantMutex> sync(m_listMutex);
+ m_nextHelper = m_headHelper;
+ m_headHelper = this;
+}
+
+
+MotorSafetyHelper::~MotorSafetyHelper()
+{
+ ::std::unique_lock<ReentrantMutex> sync(m_listMutex);
+ if (m_headHelper == this)
+ {
+ m_headHelper = m_nextHelper;
+ }
+ else
+ {
+ MotorSafetyHelper *prev = NULL;
+ MotorSafetyHelper *cur = m_headHelper;
+ while (cur != this && cur != NULL)
+ prev = cur, cur = cur->m_nextHelper;
+ if (cur == this)
+ prev->m_nextHelper = cur->m_nextHelper;
+ }
+}
+
+/**
+ * Feed the motor safety object.
+ * Resets the timer on this object that is used to do the timeouts.
+ */
+void MotorSafetyHelper::Feed()
+{
+ ::std::unique_lock<ReentrantMutex> sync(m_syncMutex);
+ m_stopTime = Timer::GetFPGATimestamp() + m_expiration;
+}
+
+/**
+ * Set the expiration time for the corresponding motor safety object.
+ * @param expirationTime The timeout value in seconds.
+ */
+void MotorSafetyHelper::SetExpiration(float expirationTime)
+{
+ ::std::unique_lock<ReentrantMutex> sync(m_syncMutex);
+ m_expiration = expirationTime;
+}
+
+/**
+ * Retrieve the timeout value for the corresponding motor safety object.
+ * @return the timeout value in seconds.
+ */
+float MotorSafetyHelper::GetExpiration()
+{
+ ::std::unique_lock<ReentrantMutex> sync(m_syncMutex);
+ return m_expiration;
+}
+
+/**
+ * Determine if the motor is still operating or has timed out.
+ * @return a true value if the motor is still operating normally and hasn't timed out.
+ */
+bool MotorSafetyHelper::IsAlive()
+{
+ ::std::unique_lock<ReentrantMutex> sync(m_syncMutex);
+ return !m_enabled || m_stopTime > Timer::GetFPGATimestamp();
+}
+
+/**
+ * Check if this motor has exceeded its timeout.
+ * This method is called periodically to determine if this motor has exceeded its timeout
+ * value. If it has, the stop method is called, and the motor is shut down until its value is
+ * updated again.
+ */
+void MotorSafetyHelper::Check()
+{
+ DriverStation *ds = DriverStation::GetInstance();
+ if (!m_enabled || ds->IsDisabled() || ds->IsTest()) return;
+
+ ::std::unique_lock<ReentrantMutex> sync(m_syncMutex);
+ if (m_stopTime < Timer::GetFPGATimestamp())
+ {
+ char buf[128];
+ char desc[64];
+ m_safeObject->GetDescription(desc);
+ snprintf(buf, 128, "%s... Output not updated often enough.", desc);
+ wpi_setWPIErrorWithContext(Timeout, buf);
+ m_safeObject->StopMotor();
+ }
+}
+
+/**
+ * Enable/disable motor safety for this device
+ * Turn on and off the motor safety option for this PWM object.
+ * @param enabled True if motor safety is enforced for this object
+ */
+void MotorSafetyHelper::SetSafetyEnabled(bool enabled)
+{
+ ::std::unique_lock<ReentrantMutex> sync(m_syncMutex);
+ m_enabled = enabled;
+}
+
+/**
+ * Return the state of the motor safety enabled flag
+ * Return if the motor safety is currently enabled for this devicce.
+ * @return True if motor safety is enforced for this device
+ */
+bool MotorSafetyHelper::IsSafetyEnabled()
+{
+ ::std::unique_lock<ReentrantMutex> sync(m_syncMutex);
+ return m_enabled;
+}
+
+/**
+ * Check the motors to see if any have timed out.
+ * This static method is called periodically to poll all the motors and stop any that have
+ * timed out.
+ */
+void MotorSafetyHelper::CheckMotors()
+{
+ ::std::unique_lock<ReentrantMutex> sync(m_listMutex);
+ for (MotorSafetyHelper *msh = m_headHelper; msh != NULL; msh = msh->m_nextHelper)
+ {
+ msh->Check();
+ }
+}
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/Notifier.cpp b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/Notifier.cpp
new file mode 100644
index 0000000..70d6bf5
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/Notifier.cpp
@@ -0,0 +1,265 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+
+#include "Notifier.h"
+#include "Timer.h"
+#include "Utility.h"
+#include "WPIErrors.h"
+#include "HAL/HAL.hpp"
+
+Notifier *Notifier::timerQueueHead = NULL;
+ReentrantMutex Notifier::queueSemaphore;
+void* Notifier::m_notifier = NULL;
+int Notifier::refcount = 0;
+
+/**
+ * Create a Notifier for timer event notification.
+ * @param handler The handler is called at the notification time which is set
+ * using StartSingle or StartPeriodic.
+ */
+Notifier::Notifier(TimerEventHandler handler, void *param)
+{
+ if (handler == NULL)
+ wpi_setWPIErrorWithContext(NullParameter, "handler must not be NULL");
+ m_handler = handler;
+ m_param = param;
+ m_periodic = false;
+ m_expirationTime = 0;
+ m_period = 0;
+ m_nextEvent = NULL;
+ m_queued = false;
+ m_handlerSemaphore = initializeSemaphore(SEMAPHORE_FULL);
+ {
+ ::std::unique_lock<ReentrantMutex> sync(queueSemaphore);
+ // do the first time intialization of static variables
+ if (refcount == 0)
+ {
+ int32_t status = 0;
+ m_notifier = initializeNotifier(ProcessQueue, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ }
+ refcount++;
+ }
+}
+
+/**
+ * Free the resources for a timer event.
+ * All resources will be freed and the timer event will be removed from the
+ * queue if necessary.
+ */
+Notifier::~Notifier()
+{
+ {
+ ::std::unique_lock<ReentrantMutex> sync(queueSemaphore);
+ DeleteFromQueue();
+
+ // Delete the static variables when the last one is going away
+ if (!(--refcount))
+ {
+ int32_t status = 0;
+ cleanNotifier(m_notifier, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ }
+ }
+
+ // Acquire the semaphore; this makes certain that the handler is
+ // not being executed by the interrupt manager.
+ takeSemaphore(m_handlerSemaphore);
+ // Delete while holding the semaphore so there can be no race.
+ deleteSemaphore(m_handlerSemaphore);
+}
+
+/**
+ * Update the alarm hardware to reflect the current first element in the queue.
+ * Compute the time the next alarm should occur based on the current time and the
+ * period for the first element in the timer queue.
+ * WARNING: this method does not do synchronization! It must be called from somewhere
+ * that is taking care of synchronizing access to the queue.
+ */
+void Notifier::UpdateAlarm()
+{
+ if (timerQueueHead != NULL)
+ {
+ int32_t status = 0;
+ updateNotifierAlarm(m_notifier, (uint32_t)(timerQueueHead->m_expirationTime * 1e6), &status);
+ wpi_setStaticErrorWithContext(timerQueueHead, status, getHALErrorMessage(status));
+ }
+}
+
+/**
+ * ProcessQueue is called whenever there is a timer interrupt.
+ * We need to wake up and process the current top item in the timer queue as long
+ * as its scheduled time is after the current time. Then the item is removed or
+ * rescheduled (repetitive events) in the queue.
+ */
+void Notifier::ProcessQueue(uint32_t mask, void *params)
+{
+ Notifier *current;
+ while (true) // keep processing past events until no more
+ {
+ {
+ ::std::unique_lock<ReentrantMutex> sync(queueSemaphore);
+ double currentTime = GetClock();
+ current = timerQueueHead;
+ if (current == NULL || current->m_expirationTime > currentTime)
+ {
+ break; // no more timer events to process
+ }
+ // need to process this entry
+ timerQueueHead = current->m_nextEvent;
+ if (current->m_periodic)
+ {
+ // if periodic, requeue the event
+ // compute when to put into queue
+ current->InsertInQueue(true);
+ }
+ else
+ {
+ // not periodic; removed from queue
+ current->m_queued = false;
+ }
+ // Take handler semaphore while holding queue semaphore to make sure
+ // the handler will execute to completion in case we are being deleted.
+ takeSemaphore(current->m_handlerSemaphore);
+ }
+
+ current->m_handler(current->m_param); // call the event handler
+ giveSemaphore(current->m_handlerSemaphore);
+ }
+ // reschedule the first item in the queue
+ ::std::unique_lock<ReentrantMutex> sync(queueSemaphore);
+ UpdateAlarm();
+}
+
+/**
+ * Insert this Notifier into the timer queue in right place.
+ * WARNING: this method does not do synchronization! It must be called from somewhere
+ * that is taking care of synchronizing access to the queue.
+ * @param reschedule If false, the scheduled alarm is based on the current time and UpdateAlarm
+ * method is called which will enable the alarm if necessary.
+ * If true, update the time by adding the period (no drift) when rescheduled periodic from ProcessQueue.
+ * This ensures that the public methods only update the queue after finishing inserting.
+ */
+void Notifier::InsertInQueue(bool reschedule)
+{
+ if (reschedule)
+ {
+ m_expirationTime += m_period;
+ }
+ else
+ {
+ m_expirationTime = GetClock() + m_period;
+ }
+ if (m_expirationTime > Timer::kRolloverTime)
+ {
+ m_expirationTime -= Timer::kRolloverTime;
+ }
+ if (timerQueueHead == NULL || timerQueueHead->m_expirationTime >= this->m_expirationTime)
+ {
+ // the queue is empty or greater than the new entry
+ // the new entry becomes the first element
+ this->m_nextEvent = timerQueueHead;
+ timerQueueHead = this;
+ if (!reschedule)
+ {
+ // since the first element changed, update alarm, unless we already plan to
+ UpdateAlarm();
+ }
+ }
+ else
+ {
+ for (Notifier **npp = &(timerQueueHead->m_nextEvent); ; npp = &(*npp)->m_nextEvent)
+ {
+ Notifier *n = *npp;
+ if (n == NULL || n->m_expirationTime > this->m_expirationTime)
+ {
+ *npp = this;
+ this->m_nextEvent = n;
+ break;
+ }
+ }
+ }
+ m_queued = true;
+}
+
+/**
+ * Delete this Notifier from the timer queue.
+ * WARNING: this method does not do synchronization! It must be called from somewhere
+ * that is taking care of synchronizing access to the queue.
+ * Remove this Notifier from the timer queue and adjust the next interrupt time to reflect
+ * the current top of the queue.
+ */
+void Notifier::DeleteFromQueue()
+{
+ if (m_queued)
+ {
+ m_queued = false;
+ wpi_assert(timerQueueHead != NULL);
+ if (timerQueueHead == this)
+ {
+ // remove the first item in the list - update the alarm
+ timerQueueHead = this->m_nextEvent;
+ UpdateAlarm();
+ }
+ else
+ {
+ for (Notifier *n = timerQueueHead; n != NULL; n = n->m_nextEvent)
+ {
+ if (n->m_nextEvent == this)
+ {
+ // this element is the next element from *n from the queue
+ n->m_nextEvent = this->m_nextEvent; // point around this one
+ }
+ }
+ }
+ }
+}
+
+/**
+ * Register for single event notification.
+ * A timer event is queued for a single event after the specified delay.
+ * @param delay Seconds to wait before the handler is called.
+ */
+void Notifier::StartSingle(double delay)
+{
+ ::std::unique_lock<ReentrantMutex> sync(queueSemaphore);
+ m_periodic = false;
+ m_period = delay;
+ DeleteFromQueue();
+ InsertInQueue(false);
+}
+
+/**
+ * Register for periodic event notification.
+ * A timer event is queued for periodic event notification. Each time the interrupt
+ * occurs, the event will be immediately requeued for the same time interval.
+ * @param period Period in seconds to call the handler starting one period after the call to this method.
+ */
+void Notifier::StartPeriodic(double period)
+{
+ ::std::unique_lock<ReentrantMutex> sync(queueSemaphore);
+ m_periodic = true;
+ m_period = period;
+ DeleteFromQueue();
+ InsertInQueue(false);
+}
+
+/**
+ * Stop timer events from occuring.
+ * Stop any repeating timer events from occuring. This will also remove any single
+ * notification events from the queue.
+ * If a timer-based call to the registered handler is in progress, this function will
+ * block until the handler call is complete.
+ */
+void Notifier::Stop()
+{
+ {
+ ::std::unique_lock<ReentrantMutex> sync(queueSemaphore);
+ DeleteFromQueue();
+ }
+ // Wait for a currently executing handler to complete before returning from Stop()
+ Synchronized sync(m_handlerSemaphore);
+}
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/PWM.cpp b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/PWM.cpp
new file mode 100644
index 0000000..8bb0f47
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/PWM.cpp
@@ -0,0 +1,363 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+
+#include "PWM.h"
+
+//#include "NetworkCommunication/UsageReporting.h"
+#include "Resource.h"
+#include "Utility.h"
+#include "WPIErrors.h"
+#include "HAL/HAL.hpp"
+
+constexpr float PWM::kDefaultPwmPeriod;
+constexpr float PWM::kDefaultPwmCenter;
+const int32_t PWM::kDefaultPwmStepsDown;
+const int32_t PWM::kPwmDisabled;
+
+/**
+ * Initialize PWMs given a channel.
+ *
+ * This method is private and is the common path for all the constructors for creating PWM
+ * instances. Checks channel value range and allocates the appropriate channel.
+ * The allocation is only done to help users ensure that they don't double assign channels.
+ * @param channel The PWM channel number. 0-9 are on-board, 10-19 are on the MXP port
+ */
+void PWM::InitPWM(uint32_t channel)
+{
+ char buf[64];
+
+ if (!CheckPWMChannel(channel))
+ {
+ snprintf(buf, 64, "PWM Channel %d", channel);
+ wpi_setWPIErrorWithContext(ChannelIndexOutOfRange, buf);
+ return;
+ }
+
+ int32_t status = 0;
+ allocatePWMChannel(m_pwm_ports[channel], &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+
+ m_channel = channel;
+
+ setPWM(m_pwm_ports[m_channel], kPwmDisabled, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+
+ m_eliminateDeadband = false;
+
+ HALReport(HALUsageReporting::kResourceType_PWM, channel);
+}
+
+/**
+ * Allocate a PWM given a channel number.
+ *
+ * @param channel The PWM channel.
+ */
+PWM::PWM(uint32_t channel)
+{
+ InitPWM(channel);
+}
+
+/**
+ * Free the PWM channel.
+ *
+ * Free the resource associated with the PWM channel and set the value to 0.
+ */
+PWM::~PWM()
+{
+ int32_t status = 0;
+
+ setPWM(m_pwm_ports[m_channel], kPwmDisabled, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+
+ freePWMChannel(m_pwm_ports[m_channel], &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+}
+
+/**
+ * Optionally eliminate the deadband from a speed controller.
+ * @param eliminateDeadband If true, set the motor curve on the Jaguar to eliminate
+ * the deadband in the middle of the range. Otherwise, keep the full range without
+ * modifying any values.
+ */
+void PWM::EnableDeadbandElimination(bool eliminateDeadband)
+{
+ if (StatusIsFatal()) return;
+ m_eliminateDeadband = eliminateDeadband;
+}
+
+/**
+ * Set the bounds on the PWM values.
+ * This sets the bounds on the PWM values for a particular each type of controller. The values
+ * determine the upper and lower speeds as well as the deadband bracket.
+ * @param max The Minimum pwm value
+ * @param deadbandMax The high end of the deadband range
+ * @param center The center speed (off)
+ * @param deadbandMin The low end of the deadband range
+ * @param min The minimum pwm value
+ */
+void PWM::SetBounds(int32_t max, int32_t deadbandMax, int32_t center, int32_t deadbandMin, int32_t min)
+{
+ if (StatusIsFatal()) return;
+ m_maxPwm = max;
+ m_deadbandMaxPwm = deadbandMax;
+ m_centerPwm = center;
+ m_deadbandMinPwm = deadbandMin;
+ m_minPwm = min;
+}
+
+
+/**
+ * Set the bounds on the PWM pulse widths.
+ * This sets the bounds on the PWM values for a particular type of controller. The values
+ * determine the upper and lower speeds as well as the deadband bracket.
+ * @param max The max PWM pulse width in ms
+ * @param deadbandMax The high end of the deadband range pulse width in ms
+ * @param center The center (off) pulse width in ms
+ * @param deadbandMin The low end of the deadband pulse width in ms
+ * @param min The minimum pulse width in ms
+ */
+void PWM::SetBounds(double max, double deadbandMax, double center, double deadbandMin, double min)
+{
+ // calculate the loop time in milliseconds
+ int32_t status = 0;
+ double loopTime = getLoopTiming(&status)/(kSystemClockTicksPerMicrosecond*1e3);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+
+ if (StatusIsFatal()) return;
+
+ m_maxPwm = (int32_t)((max-kDefaultPwmCenter)/loopTime+kDefaultPwmStepsDown-1);
+ m_deadbandMaxPwm = (int32_t)((deadbandMax-kDefaultPwmCenter)/loopTime+kDefaultPwmStepsDown-1);
+ m_centerPwm = (int32_t)((center-kDefaultPwmCenter)/loopTime+kDefaultPwmStepsDown-1);
+ m_deadbandMinPwm = (int32_t)((deadbandMin-kDefaultPwmCenter)/loopTime+kDefaultPwmStepsDown-1);
+ m_minPwm = (int32_t)((min-kDefaultPwmCenter)/loopTime+kDefaultPwmStepsDown-1);
+}
+
+/**
+ * Set the PWM value based on a position.
+ *
+ * This is intended to be used by servos.
+ *
+ * @pre SetMaxPositivePwm() called.
+ * @pre SetMinNegativePwm() called.
+ *
+ * @param pos The position to set the servo between 0.0 and 1.0.
+ */
+void PWM::SetPosition(float pos)
+{
+ if (StatusIsFatal()) return;
+ if (pos < 0.0)
+ {
+ pos = 0.0;
+ }
+ else if (pos > 1.0)
+ {
+ pos = 1.0;
+ }
+
+ // note, need to perform the multiplication below as floating point before converting to int
+ unsigned short rawValue = (int32_t)( (pos * (float) GetFullRangeScaleFactor()) + GetMinNegativePwm());
+// printf("MinNegPWM: %d FullRangeScaleFactor: %d Raw value: %5d Input value: %4.4f\n", GetMinNegativePwm(), GetFullRangeScaleFactor(), rawValue, pos);
+
+// wpi_assert((rawValue >= GetMinNegativePwm()) && (rawValue <= GetMaxPositivePwm()));
+ wpi_assert(rawValue != kPwmDisabled);
+
+ // send the computed pwm value to the FPGA
+ SetRaw((unsigned short)rawValue);
+}
+
+/**
+ * Get the PWM value in terms of a position.
+ *
+ * This is intended to be used by servos.
+ *
+ * @pre SetMaxPositivePwm() called.
+ * @pre SetMinNegativePwm() called.
+ *
+ * @return The position the servo is set to between 0.0 and 1.0.
+ */
+float PWM::GetPosition()
+{
+ if (StatusIsFatal()) return 0.0;
+ int32_t value = GetRaw();
+ if (value < GetMinNegativePwm())
+ {
+ return 0.0;
+ }
+ else if (value > GetMaxPositivePwm())
+ {
+ return 1.0;
+ }
+ else
+ {
+ return (float)(value - GetMinNegativePwm()) / (float)GetFullRangeScaleFactor();
+ }
+}
+
+/**
+ * Set the PWM value based on a speed.
+ *
+ * This is intended to be used by speed controllers.
+ *
+ * @pre SetMaxPositivePwm() called.
+ * @pre SetMinPositivePwm() called.
+ * @pre SetCenterPwm() called.
+ * @pre SetMaxNegativePwm() called.
+ * @pre SetMinNegativePwm() called.
+ *
+ * @param speed The speed to set the speed controller between -1.0 and 1.0.
+ */
+void PWM::SetSpeed(float speed)
+{
+ if (StatusIsFatal()) return;
+ // clamp speed to be in the range 1.0 >= speed >= -1.0
+ if (speed < -1.0)
+ {
+ speed = -1.0;
+ }
+ else if (speed > 1.0)
+ {
+ speed = 1.0;
+ }
+
+ // calculate the desired output pwm value by scaling the speed appropriately
+ int32_t rawValue;
+ if (speed == 0.0)
+ {
+ rawValue = GetCenterPwm();
+ }
+ else if (speed > 0.0)
+ {
+ rawValue = (int32_t)(speed * ((float)GetPositiveScaleFactor()) +
+ ((float) GetMinPositivePwm()) + 0.5);
+ }
+ else
+ {
+ rawValue = (int32_t)(speed * ((float)GetNegativeScaleFactor()) +
+ ((float) GetMaxNegativePwm()) + 0.5);
+ }
+
+ // the above should result in a pwm_value in the valid range
+ wpi_assert((rawValue >= GetMinNegativePwm()) && (rawValue <= GetMaxPositivePwm()));
+ wpi_assert(rawValue != kPwmDisabled);
+
+ // send the computed pwm value to the FPGA
+ SetRaw(rawValue);
+}
+
+/**
+ * Get the PWM value in terms of speed.
+ *
+ * This is intended to be used by speed controllers.
+ *
+ * @pre SetMaxPositivePwm() called.
+ * @pre SetMinPositivePwm() called.
+ * @pre SetMaxNegativePwm() called.
+ * @pre SetMinNegativePwm() called.
+ *
+ * @return The most recently set speed between -1.0 and 1.0.
+ */
+float PWM::GetSpeed()
+{
+ if (StatusIsFatal()) return 0.0;
+ int32_t value = GetRaw();
+ if (value == PWM::kPwmDisabled)
+ {
+ return 0.0;
+ }
+ else if (value > GetMaxPositivePwm())
+ {
+ return 1.0;
+ }
+ else if (value < GetMinNegativePwm())
+ {
+ return -1.0;
+ }
+ else if (value > GetMinPositivePwm())
+ {
+ return (float)(value - GetMinPositivePwm()) / (float)GetPositiveScaleFactor();
+ }
+ else if (value < GetMaxNegativePwm())
+ {
+ return (float)(value - GetMaxNegativePwm()) / (float)GetNegativeScaleFactor();
+ }
+ else
+ {
+ return 0.0;
+ }
+}
+
+/**
+ * Set the PWM value directly to the hardware.
+ *
+ * Write a raw value to a PWM channel.
+ *
+ * @param value Raw PWM value.
+ */
+void PWM::SetRaw(unsigned short value)
+{
+ if (StatusIsFatal()) return;
+
+ int32_t status = 0;
+ setPWM(m_pwm_ports[m_channel], value, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+}
+
+/**
+ * Get the PWM value directly from the hardware.
+ *
+ * Read a raw value from a PWM channel.
+ *
+ * @return Raw PWM control value.
+ */
+unsigned short PWM::GetRaw()
+{
+ if (StatusIsFatal()) return 0;
+
+ int32_t status = 0;
+ unsigned short value = getPWM(m_pwm_ports[m_channel], &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+
+ return value;
+}
+
+/**
+ * Slow down the PWM signal for old devices.
+ *
+ * @param mult The period multiplier to apply to this channel
+ */
+void PWM::SetPeriodMultiplier(PeriodMultiplier mult)
+{
+ if (StatusIsFatal()) return;
+
+ int32_t status = 0;
+
+ switch(mult)
+ {
+ case kPeriodMultiplier_4X:
+ setPWMPeriodScale(m_pwm_ports[m_channel], 3, &status); // Squelch 3 out of 4 outputs
+ break;
+ case kPeriodMultiplier_2X:
+ setPWMPeriodScale(m_pwm_ports[m_channel], 1, &status); // Squelch 1 out of 2 outputs
+ break;
+ case kPeriodMultiplier_1X:
+ setPWMPeriodScale(m_pwm_ports[m_channel], 0, &status); // Don't squelch any outputs
+ break;
+ default:
+ wpi_assert(false);
+ }
+
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+}
+
+void PWM::SetZeroLatch()
+{
+ if (StatusIsFatal()) return;
+
+ int32_t status = 0;
+
+ latchPWMZero(m_pwm_ports[m_channel], &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+}
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/PowerDistributionPanel.cpp b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/PowerDistributionPanel.cpp
new file mode 100644
index 0000000..a7c40b3
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/PowerDistributionPanel.cpp
@@ -0,0 +1,153 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2014. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+
+#include "PowerDistributionPanel.h"
+#include "WPIErrors.h"
+#include "HAL/PDP.hpp"
+
+/**
+ * Initialize the PDP.
+ */
+PowerDistributionPanel::PowerDistributionPanel() {
+}
+
+/**
+ * Query the input voltage of the PDP
+ * @return The voltage of the PDP in volts
+ */
+double
+PowerDistributionPanel::GetVoltage() {
+ int32_t status = 0;
+
+ double voltage = getPDPVoltage(&status);
+
+ if(status) {
+ wpi_setWPIErrorWithContext(Timeout, "");
+ }
+
+ return voltage;
+}
+
+/**
+ * Query the temperature of the PDP
+ * @return The temperature of the PDP in degrees Celsius
+ */
+double
+PowerDistributionPanel::GetTemperature() {
+ int32_t status = 0;
+
+ double temperature = getPDPTemperature(&status);
+
+ if(status) {
+ wpi_setWPIErrorWithContext(Timeout, "");
+ }
+
+ return temperature;
+}
+
+/**
+ * Query the current of a single channel of the PDP
+ * @return The current of one of the PDP channels (channels 0-15) in Amperes
+ */
+double
+PowerDistributionPanel::GetCurrent(uint8_t channel) {
+ int32_t status = 0;
+
+ if(!CheckPDPChannel(channel))
+ {
+ char buf[64];
+ snprintf(buf, 64, "PDP Channel %d", channel);
+ wpi_setWPIErrorWithContext(ChannelIndexOutOfRange, buf);
+ }
+
+ double current = getPDPChannelCurrent(channel, &status);
+
+ if(status) {
+ wpi_setWPIErrorWithContext(Timeout, "");
+ }
+
+ return current;
+}
+
+/**
+ * Query the total current of all monitored PDP channels (0-15)
+ * @return The the total current drawn from the PDP channels in Amperes
+ */
+double
+PowerDistributionPanel::GetTotalCurrent() {
+ int32_t status = 0;
+
+ double current = getPDPTotalCurrent(&status);
+
+ if(status) {
+ wpi_setWPIErrorWithContext(Timeout, "");
+ }
+
+ return current;
+}
+
+/**
+ * Query the total power drawn from the monitored PDP channels
+ * @return The the total power drawn from the PDP channels in Watts
+ */
+double
+PowerDistributionPanel::GetTotalPower() {
+ int32_t status = 0;
+
+ double power = getPDPTotalPower(&status);
+
+ if(status) {
+ wpi_setWPIErrorWithContext(Timeout, "");
+ }
+
+ return power;
+}
+
+/**
+ * Query the total energy drawn from the monitored PDP channels
+ * @return The the total energy drawn from the PDP channels in Joules
+ */
+double
+PowerDistributionPanel::GetTotalEnergy() {
+ int32_t status = 0;
+
+ double energy = getPDPTotalEnergy(&status);
+
+ if(status) {
+ wpi_setWPIErrorWithContext(Timeout, "");
+ }
+
+ return energy;
+}
+
+/**
+ * Reset the total energy drawn from the PDP
+ * @see PowerDistributionPanel#GetTotalEnergy
+ */
+void
+PowerDistributionPanel::ResetTotalEnergy() {
+ int32_t status = 0;
+
+ resetPDPTotalEnergy(&status);
+
+ if(status) {
+ wpi_setWPIErrorWithContext(Timeout, "");
+ }
+}
+
+/**
+ * Remove all of the fault flags on the PDP
+ */
+void
+PowerDistributionPanel::ClearStickyFaults() {
+ int32_t status = 0;
+
+ clearPDPStickyFaults(&status);
+
+ if(status) {
+ wpi_setWPIErrorWithContext(Timeout, "");
+ }
+}
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/Relay.cpp b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/Relay.cpp
new file mode 100644
index 0000000..6b3b50c
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/Relay.cpp
@@ -0,0 +1,210 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+
+#include "Relay.h"
+
+//#include "NetworkCommunication/UsageReporting.h"
+#include "Resource.h"
+#include "WPIErrors.h"
+#include "HAL/HAL.hpp"
+
+// Allocate each direction separately.
+static Resource *relayChannels = NULL;
+
+/**
+ * Common relay initialization method.
+ * This code is common to all Relay constructors and initializes the relay and reserves
+ * all resources that need to be locked. Initially the relay is set to both lines at 0v.
+ */
+void Relay::InitRelay()
+{
+ char buf[64];
+ Resource::CreateResourceObject(&relayChannels, dio_kNumSystems * kRelayChannels * 2);
+ if (!SensorBase::CheckRelayChannel(m_channel))
+ {
+ snprintf(buf, 64, "Relay Channel %d", m_channel);
+ wpi_setWPIErrorWithContext(ChannelIndexOutOfRange, buf);
+ return;
+ }
+
+ if (m_direction == kBothDirections || m_direction == kForwardOnly)
+ {
+ snprintf(buf, 64, "Forward Relay %d", m_channel);
+ if (relayChannels->Allocate(m_channel * 2, buf) == ~0ul)
+ {
+ CloneError(relayChannels);
+ return;
+ }
+
+ HALReport(HALUsageReporting::kResourceType_Relay, m_channel);
+ }
+ if (m_direction == kBothDirections || m_direction == kReverseOnly)
+ {
+ snprintf(buf, 64, "Reverse Relay %d", m_channel);
+ if (relayChannels->Allocate(m_channel * 2 + 1, buf) == ~0ul)
+ {
+ CloneError(relayChannels);
+ return;
+ }
+
+ HALReport(HALUsageReporting::kResourceType_Relay, m_channel + 128);
+ }
+
+ int32_t status = 0;
+ setRelayForward(m_relay_ports[m_channel], false, &status);
+ setRelayReverse(m_relay_ports[m_channel], false, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+
+}
+
+/**
+ * Relay constructor given a channel.
+ * @param channel The channel number (0-3).
+ * @param direction The direction that the Relay object will control.
+ */
+Relay::Relay(uint32_t channel, Relay::Direction direction)
+ : m_channel (channel)
+ , m_direction (direction)
+{
+ InitRelay();
+}
+
+/**
+ * Free the resource associated with a relay.
+ * The relay channels are set to free and the relay output is turned off.
+ */
+Relay::~Relay()
+{
+
+ int32_t status = 0;
+ setRelayForward(m_relay_ports[m_channel], false, &status);
+ setRelayReverse(m_relay_ports[m_channel], false, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+
+ if (m_direction == kBothDirections || m_direction == kForwardOnly)
+ {
+ relayChannels->Free(m_channel * 2);
+ }
+ if (m_direction == kBothDirections || m_direction == kReverseOnly)
+ {
+ relayChannels->Free(m_channel * 2 + 1);
+ }
+}
+
+/**
+ * Set the relay state.
+ *
+ * Valid values depend on which directions of the relay are controlled by the object.
+ *
+ * When set to kBothDirections, the relay can be any of the four states:
+ * 0v-0v, 0v-12v, 12v-0v, 12v-12v
+ *
+ * When set to kForwardOnly or kReverseOnly, you can specify the constant for the
+ * direction or you can simply specify kOff and kOn. Using only kOff and kOn is
+ * recommended.
+ *
+ * @param value The state to set the relay.
+ */
+void Relay::Set(Relay::Value value)
+{
+ if (StatusIsFatal()) return;
+
+ int32_t status = 0;
+
+ switch (value)
+ {
+ case kOff:
+ if (m_direction == kBothDirections || m_direction == kForwardOnly)
+ {
+ setRelayForward(m_relay_ports[m_channel], false, &status);
+ }
+ if (m_direction == kBothDirections || m_direction == kReverseOnly)
+ {
+ setRelayReverse(m_relay_ports[m_channel], false, &status);
+ }
+ break;
+ case kOn:
+ if (m_direction == kBothDirections || m_direction == kForwardOnly)
+ {
+ setRelayForward(m_relay_ports[m_channel], true, &status);
+ }
+ if (m_direction == kBothDirections || m_direction == kReverseOnly)
+ {
+ setRelayReverse(m_relay_ports[m_channel], true, &status);
+ }
+ break;
+ case kForward:
+ if (m_direction == kReverseOnly)
+ {
+ wpi_setWPIError(IncompatibleMode);
+ break;
+ }
+ if (m_direction == kBothDirections || m_direction == kForwardOnly)
+ {
+ setRelayForward(m_relay_ports[m_channel], true, &status);
+ }
+ if (m_direction == kBothDirections)
+ {
+ setRelayReverse(m_relay_ports[m_channel], false, &status);
+ }
+ break;
+ case kReverse:
+ if (m_direction == kForwardOnly)
+ {
+ wpi_setWPIError(IncompatibleMode);
+ break;
+ }
+ if (m_direction == kBothDirections)
+ {
+ setRelayForward(m_relay_ports[m_channel], false, &status);
+ }
+ if (m_direction == kBothDirections || m_direction == kReverseOnly)
+ {
+ setRelayReverse(m_relay_ports[m_channel], true, &status);
+ }
+ break;
+ }
+
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+}
+
+/**
+ * Get the Relay State
+ *
+ * Gets the current state of the relay.
+ *
+ * When set to kForwardOnly or kReverseOnly, value is returned as kOn/kOff not
+ * kForward/kReverse (per the recommendation in Set)
+ *
+ * @return The current state of the relay as a Relay::Value
+ */
+Relay::Value Relay::Get() {
+ int32_t status;
+
+ if(getRelayForward(m_relay_ports[m_channel], &status)) {
+ if(getRelayReverse(m_relay_ports[m_channel], &status)) {
+ return kOn;
+ } else {
+ if(m_direction == kForwardOnly) {
+ return kOn;
+ } else {
+ return kForward;
+ }
+ }
+ } else {
+ if(getRelayReverse(m_relay_ports[m_channel], &status)) {
+ if(m_direction == kReverseOnly) {
+ return kOn;
+ } else {
+ return kReverse;
+ }
+ } else {
+ return kOff;
+ }
+ }
+
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+}
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/RobotBase.cpp b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/RobotBase.cpp
new file mode 100644
index 0000000..4b2c435
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/RobotBase.cpp
@@ -0,0 +1,166 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+
+#include "RobotBase.h"
+
+#include "DriverStation.h"
+//#include "NetworkCommunication/FRCComm.h"
+//#include "NetworkCommunication/symModuleLink.h"
+//#include "NetworkCommunication/UsageReporting.h"
+#include "RobotState.h"
+#include "HLUsageReporting.h"
+#include "Internal/HardwareHLReporting.h"
+#include "Utility.h"
+#include <cstring>
+#include "HAL/HAL.hpp"
+#include <cstdio>
+
+#ifdef __vxworks
+// VXWorks needs som special unloading code
+#include <moduleLib.h>
+#include <unldLib.h>
+#include <taskLib.h>
+#endif
+
+RobotBase* RobotBase::m_instance = NULL;
+
+void RobotBase::setInstance(RobotBase* robot)
+{
+ wpi_assert(m_instance == NULL);
+ m_instance = robot;
+}
+
+RobotBase &RobotBase::getInstance()
+{
+ return *m_instance;
+}
+
+void RobotBase::robotSetup(RobotBase *robot)
+{
+ robot->Prestart();
+ robot->StartCompetition();
+}
+
+/**
+ * Constructor for a generic robot program.
+ * User code should be placed in the constructor that runs before the Autonomous or Operator
+ * Control period starts. The constructor will run to completion before Autonomous is entered.
+ *
+ * This must be used to ensure that the communications code starts. In the future it would be
+ * nice to put this code into it's own task that loads on boot so ensure that it runs.
+ */
+RobotBase::RobotBase()
+ : m_task (NULL)
+ , m_ds (NULL)
+{
+ m_ds = DriverStation::GetInstance();
+ RobotState::SetImplementation(DriverStation::GetInstance()); \
+ HLUsageReporting::SetImplementation(new HardwareHLReporting()); \
+
+ RobotBase::setInstance(this);
+
+ FILE *file = NULL;
+ file = fopen("/tmp/frc_versions/FRC_Lib_Version.ini", "w");
+
+ fputs("2015 C++ 1.0.0 - modified by FRC Team 971 Spartan Robotics", file);
+ if (file != NULL)
+ fclose(file);
+}
+
+/**
+ * Free the resources for a RobotBase class.
+ * This includes deleting all classes that might have been allocated as Singletons to they
+ * would never be deleted except here.
+ */
+RobotBase::~RobotBase()
+{
+ SensorBase::DeleteSingletons();
+ delete m_task;
+ m_task = NULL;
+ m_instance = NULL;
+}
+
+/**
+ * Determine if the Robot is currently enabled.
+ * @return True if the Robot is currently enabled by the field controls.
+ */
+bool RobotBase::IsEnabled()
+{
+ return m_ds->IsEnabled();
+}
+
+/**
+ * Determine if the Robot is currently disabled.
+ * @return True if the Robot is currently disabled by the field controls.
+ */
+bool RobotBase::IsDisabled()
+{
+ return m_ds->IsDisabled();
+}
+
+/**
+ * Determine if the robot is currently in Autonomous mode.
+ * @return True if the robot is currently operating Autonomously as determined by the field controls.
+ */
+bool RobotBase::IsAutonomous()
+{
+ return m_ds->IsAutonomous();
+}
+
+/**
+ * Determine if the robot is currently in Operator Control mode.
+ * @return True if the robot is currently operating in Tele-Op mode as determined by the field controls.
+ */
+bool RobotBase::IsOperatorControl()
+{
+ return m_ds->IsOperatorControl();
+}
+
+/**
+ * Determine if the robot is currently in Test mode.
+ * @return True if the robot is currently running tests as determined by the field controls.
+ */
+bool RobotBase::IsTest()
+{
+ return m_ds->IsTest();
+}
+
+/**
+ * This hook is called right before startCompetition(). By default, tell the DS that the robot is now ready to
+ * be enabled. If you don't want for the robot to be enabled yet, you can override this method to do nothing.
+ * If you do so, you will need to call HALNetworkCommunicationObserveUserProgramStarting() from your code when
+ * you are ready for the robot to be enabled.
+ */
+void RobotBase::Prestart()
+{
+ HALNetworkCommunicationObserveUserProgramStarting();
+}
+
+/**
+ * Indicates if new data is available from the driver station.
+ * @return Has new data arrived over the network since the last time this function was called?
+ */
+bool RobotBase::IsNewDataAvailable()
+{
+ return m_ds->IsNewControlData();
+}
+
+/**
+ * This class exists for the sole purpose of getting its destructor called when the module unloads.
+ * Before the module is done unloading, we need to delete the RobotBase derived singleton. This should delete
+ * the other remaining singletons that were registered. This should also stop all tasks that are using
+ * the Task class.
+ */
+class RobotDeleter
+{
+public:
+ RobotDeleter() {}
+ ~RobotDeleter()
+ {
+ delete &RobotBase::getInstance();
+ }
+};
+static RobotDeleter g_robotDeleter;
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/RobotDrive.cpp b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/RobotDrive.cpp
new file mode 100644
index 0000000..3eb90ff
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/RobotDrive.cpp
@@ -0,0 +1,752 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+
+#include "RobotDrive.h"
+
+#include "CANJaguar.h"
+#include "GenericHID.h"
+#include "Joystick.h"
+#include "Talon.h"
+//#include "NetworkCommunication/UsageReporting.h"
+#include "Utility.h"
+#include "WPIErrors.h"
+#include <math.h>
+
+#undef max
+#include <algorithm>
+
+const int32_t RobotDrive::kMaxNumberOfMotors;
+
+/*
+ * Driving functions
+ * These functions provide an interface to multiple motors that is used for C programming
+ * The Drive(speed, direction) function is the main part of the set that makes it easy
+ * to set speeds and direction independently in one call.
+ */
+
+/**
+ * Common function to initialize all the robot drive constructors.
+ * Create a motor safety object (the real reason for the common code) and
+ * initialize all the motor assignments. The default timeout is set for the robot drive.
+ */
+void RobotDrive::InitRobotDrive() {
+ m_frontLeftMotor = NULL;
+ m_frontRightMotor = NULL;
+ m_rearRightMotor = NULL;
+ m_rearLeftMotor = NULL;
+ m_sensitivity = 0.5;
+ m_maxOutput = 1.0;
+ m_safetyHelper = new MotorSafetyHelper(this);
+ m_safetyHelper->SetSafetyEnabled(true);
+ m_syncGroup = 0;
+}
+
+/**
+ * Constructor for RobotDrive with 2 motors specified with channel numbers.
+ * Set up parameters for a two wheel drive system where the
+ * left and right motor pwm channels are specified in the call.
+ * This call assumes Talons for controlling the motors.
+ * @param leftMotorChannel The PWM channel number that drives the left motor. 0-9 are on-board, 10-19 are on the MXP port
+ * @param rightMotorChannel The PWM channel number that drives the right motor. 0-9 are on-board, 10-19 are on the MXP port
+ */
+RobotDrive::RobotDrive(uint32_t leftMotorChannel, uint32_t rightMotorChannel)
+{
+ InitRobotDrive();
+ m_rearLeftMotor = new Talon(leftMotorChannel);
+ m_rearRightMotor = new Talon(rightMotorChannel);
+ for (int32_t i=0; i < kMaxNumberOfMotors; i++)
+ {
+ m_invertedMotors[i] = 1;
+ }
+ SetLeftRightMotorOutputs(0.0, 0.0);
+ m_deleteSpeedControllers = true;
+}
+
+/**
+ * Constructor for RobotDrive with 4 motors specified with channel numbers.
+ * Set up parameters for a four wheel drive system where all four motor
+ * pwm channels are specified in the call.
+ * This call assumes Talons for controlling the motors.
+ * @param frontLeftMotor Front left motor channel number. 0-9 are on-board, 10-19 are on the MXP port
+ * @param rearLeftMotor Rear Left motor channel number. 0-9 are on-board, 10-19 are on the MXP port
+ * @param frontRightMotor Front right motor channel number. 0-9 are on-board, 10-19 are on the MXP port
+ * @param rearRightMotor Rear Right motor channel number. 0-9 are on-board, 10-19 are on the MXP port
+ */
+RobotDrive::RobotDrive(uint32_t frontLeftMotor, uint32_t rearLeftMotor,
+ uint32_t frontRightMotor, uint32_t rearRightMotor)
+{
+ InitRobotDrive();
+ m_rearLeftMotor = new Talon(rearLeftMotor);
+ m_rearRightMotor = new Talon(rearRightMotor);
+ m_frontLeftMotor = new Talon(frontLeftMotor);
+ m_frontRightMotor = new Talon(frontRightMotor);
+ for (int32_t i=0; i < kMaxNumberOfMotors; i++)
+ {
+ m_invertedMotors[i] = 1;
+ }
+ SetLeftRightMotorOutputs(0.0, 0.0);
+ m_deleteSpeedControllers = true;
+}
+
+/**
+ * Constructor for RobotDrive with 2 motors specified as SpeedController objects.
+ * The SpeedController version of the constructor enables programs to use the RobotDrive classes with
+ * subclasses of the SpeedController objects, for example, versions with ramping or reshaping of
+ * the curve to suit motor bias or deadband elimination.
+ * @param leftMotor The left SpeedController object used to drive the robot.
+ * @param rightMotor the right SpeedController object used to drive the robot.
+ */
+RobotDrive::RobotDrive(SpeedController *leftMotor, SpeedController *rightMotor)
+{
+ InitRobotDrive();
+ if (leftMotor == NULL || rightMotor == NULL)
+ {
+ wpi_setWPIError(NullParameter);
+ m_rearLeftMotor = m_rearRightMotor = NULL;
+ return;
+ }
+ m_rearLeftMotor = leftMotor;
+ m_rearRightMotor = rightMotor;
+ for (int32_t i=0; i < kMaxNumberOfMotors; i++)
+ {
+ m_invertedMotors[i] = 1;
+ }
+ m_deleteSpeedControllers = false;
+}
+
+RobotDrive::RobotDrive(SpeedController &leftMotor, SpeedController &rightMotor)
+{
+ InitRobotDrive();
+ m_rearLeftMotor = &leftMotor;
+ m_rearRightMotor = &rightMotor;
+ for (int32_t i=0; i < kMaxNumberOfMotors; i++)
+ {
+ m_invertedMotors[i] = 1;
+ }
+ m_deleteSpeedControllers = false;
+}
+
+/**
+ * Constructor for RobotDrive with 4 motors specified as SpeedController objects.
+ * Speed controller input version of RobotDrive (see previous comments).
+ * @param rearLeftMotor The back left SpeedController object used to drive the robot.
+ * @param frontLeftMotor The front left SpeedController object used to drive the robot
+ * @param rearRightMotor The back right SpeedController object used to drive the robot.
+ * @param frontRightMotor The front right SpeedController object used to drive the robot.
+ */
+RobotDrive::RobotDrive(SpeedController *frontLeftMotor, SpeedController *rearLeftMotor,
+ SpeedController *frontRightMotor, SpeedController *rearRightMotor)
+{
+ InitRobotDrive();
+ if (frontLeftMotor == NULL || rearLeftMotor == NULL || frontRightMotor == NULL || rearRightMotor == NULL)
+ {
+ wpi_setWPIError(NullParameter);
+ return;
+ }
+ m_frontLeftMotor = frontLeftMotor;
+ m_rearLeftMotor = rearLeftMotor;
+ m_frontRightMotor = frontRightMotor;
+ m_rearRightMotor = rearRightMotor;
+ for (int32_t i=0; i < kMaxNumberOfMotors; i++)
+ {
+ m_invertedMotors[i] = 1;
+ }
+ m_deleteSpeedControllers = false;
+}
+
+RobotDrive::RobotDrive(SpeedController &frontLeftMotor, SpeedController &rearLeftMotor,
+ SpeedController &frontRightMotor, SpeedController &rearRightMotor)
+{
+ InitRobotDrive();
+ m_frontLeftMotor = &frontLeftMotor;
+ m_rearLeftMotor = &rearLeftMotor;
+ m_frontRightMotor = &frontRightMotor;
+ m_rearRightMotor = &rearRightMotor;
+ for (int32_t i=0; i < kMaxNumberOfMotors; i++)
+ {
+ m_invertedMotors[i] = 1;
+ }
+ m_deleteSpeedControllers = false;
+}
+
+/**
+ * RobotDrive destructor.
+ * Deletes motor objects that were not passed in and created internally only.
+ **/
+RobotDrive::~RobotDrive()
+{
+ if (m_deleteSpeedControllers)
+ {
+ delete m_frontLeftMotor;
+ delete m_rearLeftMotor;
+ delete m_frontRightMotor;
+ delete m_rearRightMotor;
+ }
+ delete m_safetyHelper;
+}
+
+/**
+ * Drive the motors at "speed" and "curve".
+ *
+ * The speed and curve are -1.0 to +1.0 values where 0.0 represents stopped and
+ * not turning. The algorithm for adding in the direction attempts to provide a constant
+ * turn radius for differing speeds.
+ *
+ * This function will most likely be used in an autonomous routine.
+ *
+ * @param outputMagnitude The forward component of the output magnitude to send to the motors.
+ * @param curve The rate of turn, constant for different forward speeds.
+ */
+void RobotDrive::Drive(float outputMagnitude, float curve)
+{
+ float leftOutput, rightOutput;
+ static bool reported = false;
+ if (!reported)
+ {
+ HALReport(HALUsageReporting::kResourceType_RobotDrive, GetNumMotors(), HALUsageReporting::kRobotDrive_ArcadeRatioCurve);
+ reported = true;
+ }
+
+ if (curve < 0)
+ {
+ float value = log(-curve);
+ float ratio = (value - m_sensitivity)/(value + m_sensitivity);
+ if (ratio == 0) ratio =.0000000001;
+ leftOutput = outputMagnitude / ratio;
+ rightOutput = outputMagnitude;
+ }
+ else if (curve > 0)
+ {
+ float value = log(curve);
+ float ratio = (value - m_sensitivity)/(value + m_sensitivity);
+ if (ratio == 0) ratio =.0000000001;
+ leftOutput = outputMagnitude;
+ rightOutput = outputMagnitude / ratio;
+ }
+ else
+ {
+ leftOutput = outputMagnitude;
+ rightOutput = outputMagnitude;
+ }
+ SetLeftRightMotorOutputs(leftOutput, rightOutput);
+}
+
+/**
+ * Provide tank steering using the stored robot configuration.
+ * Drive the robot using two joystick inputs. The Y-axis will be selected from
+ * each Joystick object.
+ * @param leftStick The joystick to control the left side of the robot.
+ * @param rightStick The joystick to control the right side of the robot.
+ */
+void RobotDrive::TankDrive(GenericHID *leftStick, GenericHID *rightStick, bool squaredInputs)
+{
+ if (leftStick == NULL || rightStick == NULL)
+ {
+ wpi_setWPIError(NullParameter);
+ return;
+ }
+ TankDrive(leftStick->GetY(), rightStick->GetY(), squaredInputs);
+}
+
+void RobotDrive::TankDrive(GenericHID &leftStick, GenericHID &rightStick, bool squaredInputs)
+{
+ TankDrive(leftStick.GetY(), rightStick.GetY(), squaredInputs);
+}
+
+/**
+ * Provide tank steering using the stored robot configuration.
+ * This function lets you pick the axis to be used on each Joystick object for the left
+ * and right sides of the robot.
+ * @param leftStick The Joystick object to use for the left side of the robot.
+ * @param leftAxis The axis to select on the left side Joystick object.
+ * @param rightStick The Joystick object to use for the right side of the robot.
+ * @param rightAxis The axis to select on the right side Joystick object.
+ */
+void RobotDrive::TankDrive(GenericHID *leftStick, uint32_t leftAxis,
+ GenericHID *rightStick, uint32_t rightAxis, bool squaredInputs)
+{
+ if (leftStick == NULL || rightStick == NULL)
+ {
+ wpi_setWPIError(NullParameter);
+ return;
+ }
+ TankDrive(leftStick->GetRawAxis(leftAxis), rightStick->GetRawAxis(rightAxis), squaredInputs);
+}
+
+void RobotDrive::TankDrive(GenericHID &leftStick, uint32_t leftAxis,
+ GenericHID &rightStick, uint32_t rightAxis, bool squaredInputs)
+{
+ TankDrive(leftStick.GetRawAxis(leftAxis), rightStick.GetRawAxis(rightAxis), squaredInputs);
+}
+
+
+/**
+ * Provide tank steering using the stored robot configuration.
+ * This function lets you directly provide joystick values from any source.
+ * @param leftValue The value of the left stick.
+ * @param rightValue The value of the right stick.
+ */
+void RobotDrive::TankDrive(float leftValue, float rightValue, bool squaredInputs)
+{
+ static bool reported = false;
+ if (!reported)
+ {
+ HALReport(HALUsageReporting::kResourceType_RobotDrive, GetNumMotors(), HALUsageReporting::kRobotDrive_Tank);
+ reported = true;
+ }
+
+ // square the inputs (while preserving the sign) to increase fine control while permitting full power
+ leftValue = Limit(leftValue);
+ rightValue = Limit(rightValue);
+ if(squaredInputs)
+ {
+ if (leftValue >= 0.0)
+ {
+ leftValue = (leftValue * leftValue);
+ }
+ else
+ {
+ leftValue = -(leftValue * leftValue);
+ }
+ if (rightValue >= 0.0)
+ {
+ rightValue = (rightValue * rightValue);
+ }
+ else
+ {
+ rightValue = -(rightValue * rightValue);
+ }
+ }
+
+ SetLeftRightMotorOutputs(leftValue, rightValue);
+}
+
+/**
+ * Arcade drive implements single stick driving.
+ * Given a single Joystick, the class assumes the Y axis for the move value and the X axis
+ * for the rotate value.
+ * (Should add more information here regarding the way that arcade drive works.)
+ * @param stick The joystick to use for Arcade single-stick driving. The Y-axis will be selected
+ * for forwards/backwards and the X-axis will be selected for rotation rate.
+ * @param squaredInputs If true, the sensitivity will be increased for small values
+ */
+void RobotDrive::ArcadeDrive(GenericHID *stick, bool squaredInputs)
+{
+ // simply call the full-featured ArcadeDrive with the appropriate values
+ ArcadeDrive(stick->GetY(), stick->GetX(), squaredInputs);
+}
+
+/**
+ * Arcade drive implements single stick driving.
+ * Given a single Joystick, the class assumes the Y axis for the move value and the X axis
+ * for the rotate value.
+ * (Should add more information here regarding the way that arcade drive works.)
+ * @param stick The joystick to use for Arcade single-stick driving. The Y-axis will be selected
+ * for forwards/backwards and the X-axis will be selected for rotation rate.
+ * @param squaredInputs If true, the sensitivity will be increased for small values
+ */
+void RobotDrive::ArcadeDrive(GenericHID &stick, bool squaredInputs)
+{
+ // simply call the full-featured ArcadeDrive with the appropriate values
+ ArcadeDrive(stick.GetY(), stick.GetX(), squaredInputs);
+}
+
+/**
+ * Arcade drive implements single stick driving.
+ * Given two joystick instances and two axis, compute the values to send to either two
+ * or four motors.
+ * @param moveStick The Joystick object that represents the forward/backward direction
+ * @param moveAxis The axis on the moveStick object to use for fowards/backwards (typically Y_AXIS)
+ * @param rotateStick The Joystick object that represents the rotation value
+ * @param rotateAxis The axis on the rotation object to use for the rotate right/left (typically X_AXIS)
+ * @param squaredInputs Setting this parameter to true increases the sensitivity at lower speeds
+ */
+void RobotDrive::ArcadeDrive(GenericHID* moveStick, uint32_t moveAxis,
+ GenericHID* rotateStick, uint32_t rotateAxis,
+ bool squaredInputs)
+{
+ float moveValue = moveStick->GetRawAxis(moveAxis);
+ float rotateValue = rotateStick->GetRawAxis(rotateAxis);
+
+ ArcadeDrive(moveValue, rotateValue, squaredInputs);
+}
+
+/**
+ * Arcade drive implements single stick driving.
+ * Given two joystick instances and two axis, compute the values to send to either two
+ * or four motors.
+ * @param moveStick The Joystick object that represents the forward/backward direction
+ * @param moveAxis The axis on the moveStick object to use for fowards/backwards (typically Y_AXIS)
+ * @param rotateStick The Joystick object that represents the rotation value
+ * @param rotateAxis The axis on the rotation object to use for the rotate right/left (typically X_AXIS)
+ * @param squaredInputs Setting this parameter to true increases the sensitivity at lower speeds
+ */
+
+void RobotDrive::ArcadeDrive(GenericHID &moveStick, uint32_t moveAxis,
+ GenericHID &rotateStick, uint32_t rotateAxis,
+ bool squaredInputs)
+{
+ float moveValue = moveStick.GetRawAxis(moveAxis);
+ float rotateValue = rotateStick.GetRawAxis(rotateAxis);
+
+ ArcadeDrive(moveValue, rotateValue, squaredInputs);
+}
+
+/**
+ * Arcade drive implements single stick driving.
+ * This function lets you directly provide joystick values from any source.
+ * @param moveValue The value to use for fowards/backwards
+ * @param rotateValue The value to use for the rotate right/left
+ * @param squaredInputs If set, increases the sensitivity at low speeds
+ */
+void RobotDrive::ArcadeDrive(float moveValue, float rotateValue, bool squaredInputs)
+{
+ static bool reported = false;
+ if (!reported)
+ {
+ HALReport(HALUsageReporting::kResourceType_RobotDrive, GetNumMotors(), HALUsageReporting::kRobotDrive_ArcadeStandard);
+ reported = true;
+ }
+
+ // local variables to hold the computed PWM values for the motors
+ float leftMotorOutput;
+ float rightMotorOutput;
+
+ moveValue = Limit(moveValue);
+ rotateValue = Limit(rotateValue);
+
+ if (squaredInputs)
+ {
+ // square the inputs (while preserving the sign) to increase fine control while permitting full power
+ if (moveValue >= 0.0)
+ {
+ moveValue = (moveValue * moveValue);
+ }
+ else
+ {
+ moveValue = -(moveValue * moveValue);
+ }
+ if (rotateValue >= 0.0)
+ {
+ rotateValue = (rotateValue * rotateValue);
+ }
+ else
+ {
+ rotateValue = -(rotateValue * rotateValue);
+ }
+ }
+
+ if (moveValue > 0.0)
+ {
+ if (rotateValue > 0.0)
+ {
+ leftMotorOutput = moveValue - rotateValue;
+ rightMotorOutput = std::max(moveValue, rotateValue);
+ }
+ else
+ {
+ leftMotorOutput = std::max(moveValue, -rotateValue);
+ rightMotorOutput = moveValue + rotateValue;
+ }
+ }
+ else
+ {
+ if (rotateValue > 0.0)
+ {
+ leftMotorOutput = - std::max(-moveValue, rotateValue);
+ rightMotorOutput = moveValue + rotateValue;
+ }
+ else
+ {
+ leftMotorOutput = moveValue - rotateValue;
+ rightMotorOutput = - std::max(-moveValue, -rotateValue);
+ }
+ }
+ SetLeftRightMotorOutputs(leftMotorOutput, rightMotorOutput);
+}
+
+/**
+ * Drive method for Mecanum wheeled robots.
+ *
+ * A method for driving with Mecanum wheeled robots. There are 4 wheels
+ * on the robot, arranged so that the front and back wheels are toed in 45 degrees.
+ * When looking at the wheels from the top, the roller axles should form an X across the robot.
+ *
+ * This is designed to be directly driven by joystick axes.
+ *
+ * @param x The speed that the robot should drive in the X direction. [-1.0..1.0]
+ * @param y The speed that the robot should drive in the Y direction.
+ * This input is inverted to match the forward == -1.0 that joysticks produce. [-1.0..1.0]
+ * @param rotation The rate of rotation for the robot that is completely independent of
+ * the translation. [-1.0..1.0]
+ * @param gyroAngle The current angle reading from the gyro. Use this to implement field-oriented controls.
+ */
+void RobotDrive::MecanumDrive_Cartesian(float x, float y, float rotation, float gyroAngle)
+{
+ static bool reported = false;
+ if (!reported)
+ {
+ HALReport(HALUsageReporting::kResourceType_RobotDrive, GetNumMotors(), HALUsageReporting::kRobotDrive_MecanumCartesian);
+ reported = true;
+ }
+
+ double xIn = x;
+ double yIn = y;
+ // Negate y for the joystick.
+ yIn = -yIn;
+ // Compenstate for gyro angle.
+ RotateVector(xIn, yIn, gyroAngle);
+
+ double wheelSpeeds[kMaxNumberOfMotors];
+ wheelSpeeds[kFrontLeftMotor] = xIn + yIn + rotation;
+ wheelSpeeds[kFrontRightMotor] = -xIn + yIn - rotation;
+ wheelSpeeds[kRearLeftMotor] = -xIn + yIn + rotation;
+ wheelSpeeds[kRearRightMotor] = xIn + yIn - rotation;
+
+ Normalize(wheelSpeeds);
+
+ m_frontLeftMotor->Set(wheelSpeeds[kFrontLeftMotor] * m_invertedMotors[kFrontLeftMotor] * m_maxOutput, m_syncGroup);
+ m_frontRightMotor->Set(wheelSpeeds[kFrontRightMotor] * m_invertedMotors[kFrontRightMotor] * m_maxOutput, m_syncGroup);
+ m_rearLeftMotor->Set(wheelSpeeds[kRearLeftMotor] * m_invertedMotors[kRearLeftMotor] * m_maxOutput, m_syncGroup);
+ m_rearRightMotor->Set(wheelSpeeds[kRearRightMotor] * m_invertedMotors[kRearRightMotor] * m_maxOutput, m_syncGroup);
+
+ if (m_syncGroup != 0)
+ {
+ CANJaguar::UpdateSyncGroup(m_syncGroup);
+ }
+
+ m_safetyHelper->Feed();
+}
+
+/**
+ * Drive method for Mecanum wheeled robots.
+ *
+ * A method for driving with Mecanum wheeled robots. There are 4 wheels
+ * on the robot, arranged so that the front and back wheels are toed in 45 degrees.
+ * When looking at the wheels from the top, the roller axles should form an X across the robot.
+ *
+ * @param magnitude The speed that the robot should drive in a given direction. [-1.0..1.0]
+ * @param direction The direction the robot should drive in degrees. The direction and maginitute are
+ * independent of the rotation rate.
+ * @param rotation The rate of rotation for the robot that is completely independent of
+ * the magnitute or direction. [-1.0..1.0]
+ */
+void RobotDrive::MecanumDrive_Polar(float magnitude, float direction, float rotation)
+{
+ static bool reported = false;
+ if (!reported)
+ {
+ HALReport(HALUsageReporting::kResourceType_RobotDrive, GetNumMotors(), HALUsageReporting::kRobotDrive_MecanumPolar);
+ reported = true;
+ }
+
+ // Normalized for full power along the Cartesian axes.
+ magnitude = Limit(magnitude) * sqrt(2.0);
+ // The rollers are at 45 degree angles.
+ double dirInRad = (direction + 45.0) * 3.14159 / 180.0;
+ double cosD = cos(dirInRad);
+ double sinD = sin(dirInRad);
+
+ double wheelSpeeds[kMaxNumberOfMotors];
+ wheelSpeeds[kFrontLeftMotor] = sinD * magnitude + rotation;
+ wheelSpeeds[kFrontRightMotor] = cosD * magnitude - rotation;
+ wheelSpeeds[kRearLeftMotor] = cosD * magnitude + rotation;
+ wheelSpeeds[kRearRightMotor] = sinD * magnitude - rotation;
+
+ Normalize(wheelSpeeds);
+
+ m_frontLeftMotor->Set(wheelSpeeds[kFrontLeftMotor] * m_invertedMotors[kFrontLeftMotor] * m_maxOutput, m_syncGroup);
+ m_frontRightMotor->Set(wheelSpeeds[kFrontRightMotor] * m_invertedMotors[kFrontRightMotor] * m_maxOutput, m_syncGroup);
+ m_rearLeftMotor->Set(wheelSpeeds[kRearLeftMotor] * m_invertedMotors[kRearLeftMotor] * m_maxOutput, m_syncGroup);
+ m_rearRightMotor->Set(wheelSpeeds[kRearRightMotor] * m_invertedMotors[kRearRightMotor] * m_maxOutput, m_syncGroup);
+
+ if (m_syncGroup != 0)
+ {
+ CANJaguar::UpdateSyncGroup(m_syncGroup);
+ }
+
+ m_safetyHelper->Feed();
+}
+
+/**
+ * Holonomic Drive method for Mecanum wheeled robots.
+ *
+ * This is an alias to MecanumDrive_Polar() for backward compatability
+ *
+ * @param magnitude The speed that the robot should drive in a given direction. [-1.0..1.0]
+ * @param direction The direction the robot should drive. The direction and maginitute are
+ * independent of the rotation rate.
+ * @param rotation The rate of rotation for the robot that is completely independent of
+ * the magnitute or direction. [-1.0..1.0]
+ */
+void RobotDrive::HolonomicDrive(float magnitude, float direction, float rotation)
+{
+ MecanumDrive_Polar(magnitude, direction, rotation);
+}
+
+/** Set the speed of the right and left motors.
+ * This is used once an appropriate drive setup function is called such as
+ * TwoWheelDrive(). The motors are set to "leftOutput" and "rightOutput"
+ * and includes flipping the direction of one side for opposing motors.
+ * @param leftOutput The speed to send to the left side of the robot.
+ * @param rightOutput The speed to send to the right side of the robot.
+ */
+void RobotDrive::SetLeftRightMotorOutputs(float leftOutput, float rightOutput)
+{
+ wpi_assert(m_rearLeftMotor != NULL && m_rearRightMotor != NULL);
+
+ if (m_frontLeftMotor != NULL)
+ m_frontLeftMotor->Set(Limit(leftOutput) * m_invertedMotors[kFrontLeftMotor] * m_maxOutput, m_syncGroup);
+ m_rearLeftMotor->Set(Limit(leftOutput) * m_invertedMotors[kRearLeftMotor] * m_maxOutput, m_syncGroup);
+
+ if (m_frontRightMotor != NULL)
+ m_frontRightMotor->Set(-Limit(rightOutput) * m_invertedMotors[kFrontRightMotor] * m_maxOutput, m_syncGroup);
+ m_rearRightMotor->Set(-Limit(rightOutput) * m_invertedMotors[kRearRightMotor] * m_maxOutput, m_syncGroup);
+
+ if (m_syncGroup != 0)
+ {
+ CANJaguar::UpdateSyncGroup(m_syncGroup);
+ }
+
+ m_safetyHelper->Feed();
+}
+
+/**
+ * Limit motor values to the -1.0 to +1.0 range.
+ */
+float RobotDrive::Limit(float num)
+{
+ if (num > 1.0)
+ {
+ return 1.0;
+ }
+ if (num < -1.0)
+ {
+ return -1.0;
+ }
+ return num;
+}
+
+/**
+ * Normalize all wheel speeds if the magnitude of any wheel is greater than 1.0.
+ */
+void RobotDrive::Normalize(double *wheelSpeeds)
+{
+ double maxMagnitude = fabs(wheelSpeeds[0]);
+ int32_t i;
+ for (i=1; i<kMaxNumberOfMotors; i++)
+ {
+ double temp = fabs(wheelSpeeds[i]);
+ if (maxMagnitude < temp) maxMagnitude = temp;
+ }
+ if (maxMagnitude > 1.0)
+ {
+ for (i=0; i<kMaxNumberOfMotors; i++)
+ {
+ wheelSpeeds[i] = wheelSpeeds[i] / maxMagnitude;
+ }
+ }
+}
+
+/**
+ * Rotate a vector in Cartesian space.
+ */
+void RobotDrive::RotateVector(double &x, double &y, double angle)
+{
+ double cosA = cos(angle * (3.14159 / 180.0));
+ double sinA = sin(angle * (3.14159 / 180.0));
+ double xOut = x * cosA - y * sinA;
+ double yOut = x * sinA + y * cosA;
+ x = xOut;
+ y = yOut;
+}
+
+/*
+ * Invert a motor direction.
+ * This is used when a motor should run in the opposite direction as the drive
+ * code would normally run it. Motors that are direct drive would be inverted, the
+ * Drive code assumes that the motors are geared with one reversal.
+ * @param motor The motor index to invert.
+ * @param isInverted True if the motor should be inverted when operated.
+ */
+void RobotDrive::SetInvertedMotor(MotorType motor, bool isInverted)
+{
+ if (motor < 0 || motor > 3)
+ {
+ wpi_setWPIError(InvalidMotorIndex);
+ return;
+ }
+ m_invertedMotors[motor] = isInverted ? -1 : 1;
+}
+
+/**
+ * Set the turning sensitivity.
+ *
+ * This only impacts the Drive() entry-point.
+ * @param sensitivity Effectively sets the turning sensitivity (or turn radius for a given value)
+ */
+void RobotDrive::SetSensitivity(float sensitivity)
+{
+ m_sensitivity = sensitivity;
+}
+
+/**
+ * Configure the scaling factor for using RobotDrive with motor controllers in a mode other than PercentVbus.
+ * @param maxOutput Multiplied with the output percentage computed by the drive functions.
+ */
+void RobotDrive::SetMaxOutput(double maxOutput)
+{
+ m_maxOutput = maxOutput;
+}
+
+/**
+ * Set the number of the sync group for the motor controllers. If the motor controllers are {@link CANJaguar}s,
+ * then they will all be added to this sync group, causing them to update their values at the same time.
+ *
+ * @param syncGroup the update group to add the motor controllers to
+ */
+void RobotDrive::SetCANJaguarSyncGroup(uint8_t syncGroup) {
+ m_syncGroup = syncGroup;
+}
+
+void RobotDrive::SetExpiration(float timeout)
+{
+ m_safetyHelper->SetExpiration(timeout);
+}
+
+float RobotDrive::GetExpiration()
+{
+ return m_safetyHelper->GetExpiration();
+}
+
+bool RobotDrive::IsAlive()
+{
+ return m_safetyHelper->IsAlive();
+}
+
+bool RobotDrive::IsSafetyEnabled()
+{
+ return m_safetyHelper->IsSafetyEnabled();
+}
+
+void RobotDrive::SetSafetyEnabled(bool enabled)
+{
+ m_safetyHelper->SetSafetyEnabled(enabled);
+}
+
+void RobotDrive::GetDescription(char *desc)
+{
+ sprintf(desc, "RobotDrive");
+}
+
+void RobotDrive::StopMotor()
+{
+ if (m_frontLeftMotor != NULL) m_frontLeftMotor->Disable();
+ if (m_frontRightMotor != NULL) m_frontRightMotor->Disable();
+ if (m_rearLeftMotor != NULL) m_rearLeftMotor->Disable();
+ if (m_rearRightMotor != NULL) m_rearRightMotor->Disable();
+ m_safetyHelper->Feed();
+}
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/SPI.cpp b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/SPI.cpp
new file mode 100644
index 0000000..4878dd9
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/SPI.cpp
@@ -0,0 +1,185 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+
+#include "SPI.h"
+
+#include "WPIErrors.h"
+#include "HAL/Digital.hpp"
+
+#include <string.h>
+
+
+/**
+ * Constructor
+ *
+ * @param SPIport the physical SPI port
+ */
+SPI::SPI(Port SPIport)
+{
+ m_port = SPIport;
+ int32_t status = 0;
+ spiInitialize(m_port, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+
+ static int32_t instances = 0;
+ instances++;
+ HALReport(HALUsageReporting::kResourceType_SPI, instances);
+}
+
+/**
+ * Destructor.
+ */
+SPI::~SPI()
+{
+ spiClose(m_port);
+}
+
+/**
+ * Configure the rate of the generated clock signal.
+ *
+ * The default value is 500,000Hz.
+ * The maximum value is 4,000,000Hz.
+ *
+ * @param hz The clock rate in Hertz.
+ */
+void SPI::SetClockRate(double hz)
+{
+ spiSetSpeed(m_port, hz);
+}
+
+/**
+ * Configure the order that bits are sent and received on the wire
+ * to be most significant bit first.
+ */
+void SPI::SetMSBFirst()
+{
+ m_msbFirst = true;
+ spiSetOpts(m_port, (int) m_msbFirst, (int) m_sampleOnTrailing, (int) m_clk_idle_high);
+}
+
+/**
+ * Configure the order that bits are sent and received on the wire
+ * to be least significant bit first.
+ */
+void SPI::SetLSBFirst()
+{
+ m_msbFirst = false;
+ spiSetOpts(m_port, (int) m_msbFirst, (int) m_sampleOnTrailing, (int) m_clk_idle_high);
+}
+
+/**
+ * Configure that the data is stable on the falling edge and the data
+ * changes on the rising edge.
+ */
+void SPI::SetSampleDataOnFalling()
+{
+ m_sampleOnTrailing = true;
+ spiSetOpts(m_port, (int) m_msbFirst, (int) m_sampleOnTrailing, (int) m_clk_idle_high);
+}
+
+/**
+ * Configure that the data is stable on the rising edge and the data
+ * changes on the falling edge.
+ */
+void SPI::SetSampleDataOnRising()
+{
+ m_sampleOnTrailing = false;
+ spiSetOpts(m_port, (int) m_msbFirst, (int) m_sampleOnTrailing, (int) m_clk_idle_high);
+}
+
+/**
+ * Configure the clock output line to be active low.
+ * This is sometimes called clock polarity high or clock idle high.
+ */
+void SPI::SetClockActiveLow()
+{
+ m_clk_idle_high = true;
+ spiSetOpts(m_port, (int) m_msbFirst, (int) m_sampleOnTrailing, (int) m_clk_idle_high);
+}
+
+/**
+ * Configure the clock output line to be active high.
+ * This is sometimes called clock polarity low or clock idle low.
+ */
+void SPI::SetClockActiveHigh()
+{
+ m_clk_idle_high = false;
+ spiSetOpts(m_port, (int) m_msbFirst, (int) m_sampleOnTrailing, (int) m_clk_idle_high);
+}
+
+/**
+ * Configure the chip select line to be active high.
+ */
+void SPI::SetChipSelectActiveHigh()
+{
+ int32_t status = 0;
+ spiSetChipSelectActiveHigh(m_port, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+}
+
+/**
+ * Configure the chip select line to be active low.
+ */
+void SPI::SetChipSelectActiveLow()
+{
+ int32_t status = 0;
+ spiSetChipSelectActiveLow(m_port, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+}
+
+
+/**
+ * Write data to the slave device. Blocks until there is space in the
+ * output FIFO.
+ *
+ * If not running in output only mode, also saves the data received
+ * on the MISO input during the transfer into the receive FIFO.
+ */
+int32_t SPI::Write(uint8_t* data, uint8_t size)
+{
+ int32_t retVal = 0;
+ retVal = spiWrite(m_port, data, size);
+ return retVal;
+}
+
+/**
+ * Read a word from the receive FIFO.
+ *
+ * Waits for the current transfer to complete if the receive FIFO is empty.
+ *
+ * If the receive FIFO is empty, there is no active transfer, and initiate
+ * is false, errors.
+ *
+ * @param initiate If true, this function pushes "0" into the
+ * transmit buffer and initiates a transfer.
+ * If false, this function assumes that data is
+ * already in the receive FIFO from a previous write.
+ */
+int32_t SPI::Read(bool initiate, uint8_t* dataReceived, uint8_t size)
+{
+ int32_t retVal = 0;
+ if(initiate){
+ uint8_t* dataToSend = new uint8_t[size];
+ memset(dataToSend, 0, size);
+ retVal = spiTransaction(m_port, dataToSend, dataReceived, size);
+ }
+ else
+ retVal = spiRead(m_port, dataReceived, size);
+ return retVal;
+}
+
+/**
+ * Perform a simultaneous read/write transaction with the device
+ *
+ * @param dataToSend The data to be written out to the device
+ * @param dataReceived Buffer to receive data from the device
+ * @param size The length of the transaction, in bytes
+ */
+int32_t SPI::Transaction(uint8_t* dataToSend, uint8_t* dataReceived, uint8_t size){
+ int32_t retVal = 0;
+ retVal = spiTransaction(m_port, dataToSend, dataReceived, size);
+ return retVal;
+}
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/SafePWM.cpp b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/SafePWM.cpp
new file mode 100644
index 0000000..289d5fe
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/SafePWM.cpp
@@ -0,0 +1,106 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+
+#include "SafePWM.h"
+
+#include "MotorSafetyHelper.h"
+
+/**
+ * Initialize a SafePWM object by setting defaults
+ */
+void SafePWM::InitSafePWM()
+{
+ m_safetyHelper = new MotorSafetyHelper(this);
+ m_safetyHelper->SetSafetyEnabled(false);
+}
+
+/**
+ * Constructor for a SafePWM object taking a channel number.
+ * @param channel The PWM channel number 0-9 are on-board, 10-19 are on the MXP port
+ */
+SafePWM::SafePWM(uint32_t channel): PWM(channel)
+{
+ InitSafePWM();
+}
+
+SafePWM::~SafePWM()
+{
+ delete m_safetyHelper;
+}
+
+/**
+ * Set the expiration time for the PWM object
+ * @param timeout The timeout (in seconds) for this motor object
+ */
+void SafePWM::SetExpiration(float timeout)
+{
+ m_safetyHelper->SetExpiration(timeout);
+}
+
+/**
+ * Return the expiration time for the PWM object.
+ * @returns The expiration time value.
+ */
+float SafePWM::GetExpiration()
+{
+ return m_safetyHelper->GetExpiration();
+}
+
+/**
+ * Check if the PWM object is currently alive or stopped due to a timeout.
+ * @returns a bool value that is true if the motor has NOT timed out and should still
+ * be running.
+ */
+bool SafePWM::IsAlive()
+{
+ return m_safetyHelper->IsAlive();
+}
+
+/**
+ * Stop the motor associated with this PWM object.
+ * This is called by the MotorSafetyHelper object when it has a timeout for this PWM and needs to
+ * stop it from running.
+ */
+void SafePWM::StopMotor()
+{
+ SetRaw(kPwmDisabled);
+}
+
+/**
+ * Enable/disable motor safety for this device
+ * Turn on and off the motor safety option for this PWM object.
+ * @param enabled True if motor safety is enforced for this object
+ */
+void SafePWM::SetSafetyEnabled(bool enabled)
+{
+ m_safetyHelper->SetSafetyEnabled(enabled);
+}
+
+/**
+ * Check if motor safety is enabled for this object
+ * @returns True if motor safety is enforced for this object
+ */
+bool SafePWM::IsSafetyEnabled()
+{
+ return m_safetyHelper->IsSafetyEnabled();
+}
+
+void SafePWM::GetDescription(char *desc)
+{
+ sprintf(desc, "PWM %d", GetChannel());
+}
+
+/**
+ * Feed the MotorSafety timer when setting the speed.
+ * This method is called by the subclass motor whenever it updates its speed, thereby reseting
+ * the timeout value.
+ * @param speed Value to pass to the PWM class
+ */
+void SafePWM::SetSpeed(float speed)
+{
+ PWM::SetSpeed(speed);
+ m_safetyHelper->Feed();
+}
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/SensorBase.cpp b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/SensorBase.cpp
new file mode 100644
index 0000000..d403b14
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/SensorBase.cpp
@@ -0,0 +1,202 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+
+#include "SensorBase.h"
+
+#include "NetworkCommunication/LoadOut.h"
+#include "WPIErrors.h"
+#include "HAL/HAL.hpp"
+
+const uint32_t SensorBase::kDigitalChannels;
+const uint32_t SensorBase::kAnalogInputs;
+const uint32_t SensorBase::kSolenoidChannels;
+const uint32_t SensorBase::kSolenoidModules;
+const uint32_t SensorBase::kPwmChannels;
+const uint32_t SensorBase::kRelayChannels;
+const uint32_t SensorBase::kPDPChannels;
+const uint32_t SensorBase::kChassisSlots;
+SensorBase *SensorBase::m_singletonList = NULL;
+
+static bool portsInitialized = false;
+void* SensorBase::m_digital_ports[kDigitalChannels];
+void* SensorBase::m_relay_ports[kRelayChannels];
+void* SensorBase::m_pwm_ports[kPwmChannels];
+
+/**
+ * Creates an instance of the sensor base and gets an FPGA handle
+ */
+SensorBase::SensorBase()
+{
+ if(!portsInitialized) {
+ for (uint32_t i = 0; i < kDigitalChannels; i++)
+ {
+ void* port = getPort(i);
+ int32_t status = 0;
+ m_digital_ports[i] = initializeDigitalPort(port, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ }
+
+ for (uint32_t i = 0; i < kRelayChannels; i++)
+ {
+ void* port = getPort(i);
+ int32_t status = 0;
+ m_relay_ports[i] = initializeDigitalPort(port, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ }
+
+ for (uint32_t i = 0; i < kPwmChannels; i++)
+ {
+ void* port = getPort(i);
+ int32_t status = 0;
+ m_pwm_ports[i] = initializeDigitalPort(port, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ }
+ }
+}
+
+/**
+ * Frees the resources for a SensorBase.
+ */
+SensorBase::~SensorBase()
+{
+}
+
+/**
+ * Add sensor to the singleton list.
+ * Add this sensor to the list of singletons that need to be deleted when
+ * the robot program exits. Each of the sensors on this list are singletons,
+ * that is they aren't allocated directly with new, but instead are allocated
+ * by the static GetInstance method. As a result, they are never deleted when
+ * the program exits. Consequently these sensors may still be holding onto
+ * resources and need to have their destructors called at the end of the program.
+ */
+void SensorBase::AddToSingletonList()
+{
+ m_nextSingleton = m_singletonList;
+ m_singletonList = this;
+}
+
+/**
+ * Delete all the singleton classes on the list.
+ * All the classes that were allocated as singletons need to be deleted so
+ * their resources can be freed.
+ */
+void SensorBase::DeleteSingletons()
+{
+ for (SensorBase *next = m_singletonList; next != NULL;)
+ {
+ SensorBase *tmp = next;
+ next = next->m_nextSingleton;
+ delete tmp;
+ }
+ m_singletonList = NULL;
+}
+
+/**
+ * Check that the solenoid module number is valid.
+ *
+ * @return Solenoid module is valid and present
+ */
+bool SensorBase::CheckSolenoidModule(uint8_t moduleNumber)
+{
+ if (moduleNumber < 64)
+ return true;
+ return false;
+}
+
+/**
+ * Check that the digital channel number is valid.
+ * Verify that the channel number is one of the legal channel numbers. Channel numbers are
+ * 1-based.
+ *
+ * @return Digital channel is valid
+ */
+bool SensorBase::CheckDigitalChannel(uint32_t channel)
+{
+ if (channel < kDigitalChannels)
+ return true;
+ return false;
+}
+
+/**
+ * Check that the digital channel number is valid.
+ * Verify that the channel number is one of the legal channel numbers. Channel numbers are
+ * 1-based.
+ *
+ * @return Relay channel is valid
+ */
+bool SensorBase::CheckRelayChannel(uint32_t channel)
+{
+ if (channel < kRelayChannels)
+ return true;
+ return false;
+}
+
+/**
+ * Check that the digital channel number is valid.
+ * Verify that the channel number is one of the legal channel numbers. Channel numbers are
+ * 1-based.
+ *
+ * @return PWM channel is valid
+ */
+bool SensorBase::CheckPWMChannel(uint32_t channel)
+{
+ if (channel < kPwmChannels)
+ return true;
+ return false;
+}
+
+/**
+ * Check that the analog input number is value.
+ * Verify that the analog input number is one of the legal channel numbers. Channel numbers
+ * are 0-based.
+ *
+ * @return Analog channel is valid
+ */
+bool SensorBase::CheckAnalogInput(uint32_t channel)
+{
+ if (channel < kAnalogInputs)
+ return true;
+ return false;
+}
+
+/**
+ * Check that the analog output number is valid.
+ * Verify that the analog output number is one of the legal channel numbers. Channel numbers
+ * are 0-based.
+ *
+ * @return Analog channel is valid
+ */
+bool SensorBase::CheckAnalogOutput(uint32_t channel)
+{
+ if (channel < kAnalogOutputs)
+ return true;
+ return false;
+}
+
+/**
+ * Verify that the solenoid channel number is within limits.
+ *
+ * @return Solenoid channel is valid
+ */
+bool SensorBase::CheckSolenoidChannel(uint32_t channel)
+{
+ if (channel < kSolenoidChannels)
+ return true;
+ return false;
+}
+
+/**
+ * Verify that the power distribution channel number is within limits.
+ *
+ * @return PDP channel is valid
+ */
+bool SensorBase::CheckPDPChannel(uint32_t channel)
+{
+ if (channel < kPDPChannels)
+ return true;
+ return false;
+}
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/Servo.cpp b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/Servo.cpp
new file mode 100644
index 0000000..d9a3dcd
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/Servo.cpp
@@ -0,0 +1,111 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+
+#include "Servo.h"
+
+//#include "NetworkCommunication/UsageReporting.h"
+
+constexpr float Servo::kMaxServoAngle;
+constexpr float Servo::kMinServoAngle;
+
+constexpr float Servo::kDefaultMaxServoPWM;
+constexpr float Servo::kDefaultMinServoPWM;
+
+/**
+ * Common initialization code called by all constructors.
+ *
+ * InitServo() assigns defaults for the period multiplier for the servo PWM control signal, as
+ * well as the minimum and maximum PWM values supported by the servo.
+ */
+void Servo::InitServo()
+{
+ SetBounds(kDefaultMaxServoPWM, 0.0, 0.0, 0.0, kDefaultMinServoPWM);
+ SetPeriodMultiplier(kPeriodMultiplier_4X);
+
+ HALReport(HALUsageReporting::kResourceType_Servo, GetChannel());
+}
+
+/**
+ * @param channel The PWM channel to which the servo is attached. 0-9 are on-board, 10-19 are on the MXP port
+ */
+Servo::Servo(uint32_t channel) : SafePWM(channel)
+{
+ InitServo();
+// printf("Done initializing servo %d\n", channel);
+}
+
+Servo::~Servo()
+{
+}
+
+/**
+ * Set the servo position.
+ *
+ * Servo values range from 0.0 to 1.0 corresponding to the range of full left to full right.
+ *
+ * @param value Position from 0.0 to 1.0.
+ */
+void Servo::Set(float value)
+{
+ SetPosition(value);
+}
+
+/**
+ * Set the servo to offline.
+ *
+ * Set the servo raw value to 0 (undriven)
+ */
+void Servo::SetOffline() {
+ SetRaw(0);
+}
+
+/**
+ * Get the servo position.
+ *
+ * Servo values range from 0.0 to 1.0 corresponding to the range of full left to full right.
+ *
+ * @return Position from 0.0 to 1.0.
+ */
+float Servo::Get()
+{
+ return GetPosition();
+}
+
+/**
+ * Set the servo angle.
+ *
+ * Assume that the servo angle is linear with respect to the PWM value (big assumption, need to test).
+ *
+ * Servo angles that are out of the supported range of the servo simply "saturate" in that direction
+ * In other words, if the servo has a range of (X degrees to Y degrees) than angles of less than X
+ * result in an angle of X being set and angles of more than Y degrees result in an angle of Y being set.
+ *
+ * @param degrees The angle in degrees to set the servo.
+ */
+void Servo::SetAngle(float degrees)
+{
+ if (degrees < kMinServoAngle)
+ {
+ degrees = kMinServoAngle;
+ }
+ else if (degrees > kMaxServoAngle)
+ {
+ degrees = kMaxServoAngle;
+ }
+
+ SetPosition(((float) (degrees - kMinServoAngle)) / GetServoAngleRange());
+}
+
+/**
+ * Get the servo angle.
+ *
+ * Assume that the servo angle is linear with respect to the PWM value (big assumption, need to test).
+ * @return The angle in degrees to which the servo is set.
+ */
+float Servo::GetAngle()
+{
+ return (float)GetPosition() * GetServoAngleRange() + kMinServoAngle;
+}
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/Solenoid.cpp b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/Solenoid.cpp
new file mode 100644
index 0000000..1ef3d7a
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/Solenoid.cpp
@@ -0,0 +1,115 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+
+#include "Solenoid.h"
+//#include "NetworkCommunication/UsageReporting.h"
+#include "WPIErrors.h"
+
+/**
+ * Common function to implement constructor behavior.
+ */
+void Solenoid::InitSolenoid()
+{
+ char buf[64];
+ if (!CheckSolenoidModule(m_moduleNumber))
+ {
+ snprintf(buf, 64, "Solenoid Module %d", m_moduleNumber);
+ wpi_setWPIErrorWithContext(ModuleIndexOutOfRange, buf);
+ return;
+ }
+ if (!CheckSolenoidChannel(m_channel))
+ {
+ snprintf(buf, 64, "Solenoid Channel %d", m_channel);
+ wpi_setWPIErrorWithContext(ChannelIndexOutOfRange, buf);
+ return;
+ }
+ Resource::CreateResourceObject(&m_allocated, kSolenoidChannels * 63);
+
+ snprintf(buf, 64, "Solenoid %d (Module: %d)", m_channel, m_moduleNumber);
+ if (m_allocated->Allocate(m_moduleNumber * kSolenoidChannels + m_channel, buf) == ~0ul)
+ {
+ CloneError(m_allocated);
+ return;
+ }
+
+ HALReport(HALUsageReporting::kResourceType_Solenoid, m_channel, m_moduleNumber);
+}
+
+/**
+ * Constructor using the default PCM ID (0).
+ *
+ * @param channel The channel on the PCM to control (0..7).
+ */
+Solenoid::Solenoid(uint32_t channel)
+ : SolenoidBase (GetDefaultSolenoidModule())
+ , m_channel (channel)
+{
+ InitSolenoid();
+}
+
+/**
+ * Constructor.
+ *
+ * @param moduleNumber The CAN ID of the PCM the solenoid is attached to
+ * @param channel The channel on the PCM to control (0..7).
+ */
+Solenoid::Solenoid(uint8_t moduleNumber, uint32_t channel)
+ : SolenoidBase (moduleNumber)
+ , m_channel (channel)
+{
+ InitSolenoid();
+}
+
+/**
+ * Destructor.
+ */
+Solenoid::~Solenoid()
+{
+ if (CheckSolenoidModule(m_moduleNumber))
+ {
+ m_allocated->Free(m_moduleNumber * kSolenoidChannels + m_channel);
+ }
+}
+
+/**
+ * Set the value of a solenoid.
+ *
+ * @param on Turn the solenoid output off or on.
+ */
+void Solenoid::Set(bool on)
+{
+ if (StatusIsFatal()) return;
+ uint8_t value = on ? 0xFF : 0x00;
+ uint8_t mask = 1 << m_channel;
+
+ SolenoidBase::Set(value, mask);
+}
+
+/**
+ * Read the current value of the solenoid.
+ *
+ * @return The current value of the solenoid.
+ */
+bool Solenoid::Get()
+{
+ if (StatusIsFatal()) return false;
+ uint8_t value = GetAll() & ( 1 << m_channel);
+ return (value != 0);
+}
+
+/**
+ * Check if solenoid is blacklisted.
+ * If a solenoid is shorted, it is added to the blacklist and
+ * disabled until power cycle, or until faults are cleared.
+ * @see ClearAllPCMStickyFaults()
+ *
+ * @return If solenoid is disabled due to short.
+ */
+bool Solenoid::IsBlackListed()
+{
+ int value = GetPCMSolenoidBlackList() & ( 1 << m_channel);
+ return (value != 0);
+}
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/SolenoidBase.cpp b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/SolenoidBase.cpp
new file mode 100644
index 0000000..f1b46d6
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/SolenoidBase.cpp
@@ -0,0 +1,111 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+
+#include "SolenoidBase.h"
+
+// Needs to be global since the protected resource spans all Solenoid objects.
+Resource *SolenoidBase::m_allocated = NULL;
+
+/**
+ * Constructor
+ *
+ * @param moduleNumber The CAN PCM ID.
+ */
+SolenoidBase::SolenoidBase(uint8_t moduleNumber)
+ : m_moduleNumber (moduleNumber)
+{
+ for (uint32_t i = 0; i < kSolenoidChannels; i++)
+ {
+ void* port = getPortWithModule(moduleNumber, i);
+ int32_t status = 0;
+ m_ports[i] = initializeSolenoidPort(port, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ }
+}
+
+/**
+ * Destructor.
+ */
+SolenoidBase::~SolenoidBase()
+{
+}
+
+/**
+ * Set the value of a solenoid.
+ *
+ * @param value The value you want to set on the module.
+ * @param mask The channels you want to be affected.
+ */
+void SolenoidBase::Set(uint8_t value, uint8_t mask)
+{
+ int32_t status = 0;
+ setSolenoids(m_ports[0], value, mask, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+}
+
+/**
+ * Read all 8 solenoids as a single byte
+ *
+ * @return The current value of all 8 solenoids on the module.
+ */
+uint8_t SolenoidBase::GetAll()
+{
+ uint8_t value = 0;
+ int32_t status = 0;
+ for (int i = 0; i < 8; i++) { // XXX: Unhardcode
+ value |= getSolenoid(m_ports[i], &status) << i;
+ }
+ return value;
+}
+/**
+ * Reads complete solenoid blacklist for all 8 solenoids as a single byte.
+ *
+ * If a solenoid is shorted, it is added to the blacklist and
+ * disabled until power cycle, or until faults are cleared.
+ * @see ClearAllPCMStickyFaults()
+ *
+ * @return The solenoid blacklist of all 8 solenoids on the module.
+ */
+uint8_t SolenoidBase::GetPCMSolenoidBlackList()
+{
+ int32_t status = 0;
+ return getPCMSolenoidBlackList(m_ports[0], &status);
+}
+/**
+ * @return true if PCM sticky fault is set : The common
+ * highside solenoid voltage rail is too low,
+ * most likely a solenoid channel is shorted.
+ */
+bool SolenoidBase::GetPCMSolenoidVoltageStickyFault()
+{
+ int32_t status = 0;
+ return getPCMSolenoidVoltageStickyFault(m_ports[0], &status);
+}
+/**
+ * @return true if PCM is in fault state : The common
+ * highside solenoid voltage rail is too low,
+ * most likely a solenoid channel is shorted.
+ */
+bool SolenoidBase::GetPCMSolenoidVoltageFault()
+{
+ int32_t status = 0;
+ return getPCMSolenoidVoltageFault(m_ports[0], &status);
+}
+/**
+ * Clear ALL sticky faults inside PCM that Compressor is wired to.
+ *
+ * If a sticky fault is set, then it will be persistently cleared. Compressor drive
+ * maybe momentarily disable while flags are being cleared. Care should be
+ * taken to not call this too frequently, otherwise normal compressor
+ * functionality may be prevented.
+ *
+ * If no sticky faults are set then this call will have no effect.
+ */
+void SolenoidBase::ClearAllPCMStickyFaults()
+{
+ int32_t status = 0;
+ return clearAllPCMStickyFaults_sol(m_ports[0], &status);
+}
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/Talon.cpp b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/Talon.cpp
new file mode 100644
index 0000000..a2eb396
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/Talon.cpp
@@ -0,0 +1,87 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+
+#include "Talon.h"
+
+//#include "NetworkCommunication/UsageReporting.h"
+
+/**
+ * Common initialization code called by all constructors.
+ *
+ * Note that the Talon uses the following bounds for PWM values. These values should work reasonably well for
+ * most controllers, but if users experience issues such as asymmetric behavior around
+ * the deadband or inability to saturate the controller in either direction, calibration is recommended.
+ * The calibration procedure can be found in the Talon User Manual available from CTRE.
+ *
+ * 2.037ms = full "forward"
+ * 1.539ms = the "high end" of the deadband range
+ * 1.513ms = center of the deadband range (off)
+ * 1.487ms = the "low end" of the deadband range
+ * 0.989ms = full "reverse"
+ */
+void Talon::InitTalon() {
+ SetBounds(2.037, 1.539, 1.513, 1.487, .989);
+ SetPeriodMultiplier(kPeriodMultiplier_1X);
+ SetRaw(m_centerPwm);
+ SetZeroLatch();
+
+ HALReport(HALUsageReporting::kResourceType_Talon, GetChannel());
+}
+
+/**
+ * Constructor for a Talon (original or Talon SR)
+ * @param channel The PWM channel number that the Talon is attached to. 0-9 are on-board, 10-19 are on the MXP port
+ */
+Talon::Talon(uint32_t channel) : SafePWM(channel)
+{
+ InitTalon();
+}
+
+Talon::~Talon()
+{
+}
+
+/**
+ * Set the PWM value.
+ *
+ * The PWM value is set using a range of -1.0 to 1.0, appropriately
+ * scaling the value for the FPGA.
+ *
+ * @param speed The speed value between -1.0 and 1.0 to set.
+ * @param syncGroup Unused interface.
+ */
+void Talon::Set(float speed, uint8_t syncGroup)
+{
+ SetSpeed(speed);
+}
+
+/**
+ * Get the recently set value of the PWM.
+ *
+ * @return The most recently set value for the PWM between -1.0 and 1.0.
+ */
+float Talon::Get()
+{
+ return GetSpeed();
+}
+
+/**
+ * Common interface for disabling a motor.
+ */
+void Talon::Disable()
+{
+ SetRaw(kPwmDisabled);
+}
+
+/**
+ * Write out the PID value as seen in the PIDOutput base object.
+ *
+ * @param output Write out the PWM value as was found in the PIDController
+ */
+void Talon::PIDWrite(float output)
+{
+ Set(output);
+}
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/TalonSRX.cpp b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/TalonSRX.cpp
new file mode 100644
index 0000000..d0f9ce6
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/TalonSRX.cpp
@@ -0,0 +1,86 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+
+#include "TalonSRX.h"
+
+//#include "NetworkCommunication/UsageReporting.h"
+
+/**
+ * Common initialization code called by all constructors.
+ *
+ * Note that the TalonSRX uses the following bounds for PWM values. These values should work reasonably well for
+ * most controllers, but if users experience issues such as asymmetric behaviour around
+ * the deadband or inability to saturate the controller in either direction, calibration is recommended.
+ * The calibration procedure can be found in the TalonSRX User Manual available from Cross The Road Electronics.
+ * 2.004ms = full "forward"
+ * 1.52ms = the "high end" of the deadband range
+ * 1.50ms = center of the deadband range (off)
+ * 1.48ms = the "low end" of the deadband range
+ * 0.997ms = full "reverse"
+ */
+void TalonSRX::InitTalonSRX() {
+ SetBounds(2.004, 1.52, 1.50, 1.48, .997);
+ SetPeriodMultiplier(kPeriodMultiplier_1X);
+ SetRaw(m_centerPwm);
+ SetZeroLatch();
+
+ HALReport(HALUsageReporting::kResourceType_Talon, GetChannel());
+}
+
+/**
+ * Construct a TalonSRX connected via PWM
+ * @param channel The PWM channel that the TalonSRX is attached to. 0-9 are on-board, 10-19 are on the MXP port
+ */
+TalonSRX::TalonSRX(uint32_t channel) : SafePWM(channel)
+{
+ InitTalonSRX();
+}
+
+TalonSRX::~TalonSRX()
+{
+}
+
+/**
+ * Set the PWM value.
+ *
+ * The PWM value is set using a range of -1.0 to 1.0, appropriately
+ * scaling the value for the FPGA.
+ *
+ * @param speed The speed value between -1.0 and 1.0 to set.
+ * @param syncGroup Unused interface.
+ */
+void TalonSRX::Set(float speed, uint8_t syncGroup)
+{
+ SetSpeed(speed);
+}
+
+/**
+ * Get the recently set value of the PWM.
+ *
+ * @return The most recently set value for the PWM between -1.0 and 1.0.
+ */
+float TalonSRX::Get()
+{
+ return GetSpeed();
+}
+
+/**
+ * Common interface for disabling a motor.
+ */
+void TalonSRX::Disable()
+{
+ SetRaw(kPwmDisabled);
+}
+
+/**
+ * Write out the PID value as seen in the PIDOutput base object.
+ *
+ * @param output Write out the PWM value as was found in the PIDController
+ */
+void TalonSRX::PIDWrite(float output)
+{
+ Set(output);
+}
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/Task.cpp b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/Task.cpp
new file mode 100644
index 0000000..4b80142
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/Task.cpp
@@ -0,0 +1,213 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+
+#include "Task.h"
+
+//#include "NetworkCommunication/UsageReporting.h"
+#include "WPIErrors.h"
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include "HAL/HAL.hpp"
+
+#ifndef OK
+#define OK 0
+#endif /* OK */
+#ifndef ERROR
+#define ERROR (-1)
+#endif /* ERROR */
+
+const uint32_t Task::kDefaultPriority;
+
+/**
+ * Create but don't launch a task.
+ * @param name The name of the task. "FRC_" will be prepended to the task name.
+ * @param function The address of the function to run as the new task.
+ * @param priority The VxWorks priority for the task.
+ * @param stackSize The size of the stack for the task
+ */
+Task::Task(const char* name, FUNCPTR function, int32_t priority, uint32_t stackSize)
+{
+ m_taskID = NULL_TASK;
+ m_function = function;
+ m_priority = priority;
+ m_stackSize = stackSize;
+ m_taskName = new char[strlen(name) + 5];
+ strcpy(m_taskName, "FRC_");
+ strcpy(m_taskName+4, name);
+
+ static int32_t instances = 0;
+ instances++;
+ HALReport(HALUsageReporting::kResourceType_Task, instances, 0, m_taskName);
+}
+
+Task::~Task()
+{
+ if (m_taskID != NULL_TASK) Stop();
+ delete [] m_taskName;
+ m_taskName = NULL;
+}
+
+/**
+ * Starts this task.
+ * If it is already running or unable to start, it fails and returns false.
+ */
+bool Task::Start(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint32_t arg3, uint32_t arg4,
+ uint32_t arg5, uint32_t arg6, uint32_t arg7, uint32_t arg8, uint32_t arg9)
+{
+ m_taskID = spawnTask(m_taskName,
+ m_priority,
+ VXWORKS_FP_TASK, // options
+ m_stackSize, // stack size
+ m_function, // function to start
+ arg0, arg1, arg2, arg3, arg4, // parameter 1 - pointer to this class
+ arg5, arg6, arg7, arg8, arg9);// additional unused parameters
+ if (m_taskID == NULL_TASK) {
+ HandleError(ERROR);
+ return false;
+ }
+ return true;
+}
+
+/**
+ * Restarts a running task.
+ * If the task isn't started, it starts it.
+ * @return false if the task is running and we are unable to kill the previous instance
+ */
+bool Task::Restart()
+{
+ return HandleError(restartTask(m_taskID));
+}
+
+/**
+ * Kills the running task.
+ * @returns true on success false if the task doesn't exist or we are unable to kill it.
+ */
+bool Task::Stop()
+{
+ bool ok = true;
+ if (Verify())
+ {
+ ok = HandleError(deleteTask(m_taskID));
+ }
+ m_taskID = NULL_TASK;
+ return ok;
+}
+
+/**
+ * Returns true if the task is ready to execute (i.e. not suspended, delayed, or blocked).
+ * @return true if ready, false if not ready.
+ */
+bool Task::IsReady()
+{
+ return isTaskReady(m_taskID);
+}
+
+/**
+ * Returns true if the task was explicitly suspended by calling Suspend()
+ * @return true if suspended, false if not suspended.
+ */
+bool Task::IsSuspended()
+{
+ return isTaskSuspended(m_taskID);
+}
+
+/**
+ * Pauses a running task.
+ * Returns true on success, false if unable to pause or the task isn't running.
+ */
+bool Task::Suspend()
+{
+ return HandleError(suspendTask(m_taskID));
+}
+
+/**
+ * Resumes a paused task.
+ * Returns true on success, false if unable to resume or if the task isn't running/paused.
+ */
+bool Task::Resume()
+{
+ return HandleError(resumeTask(m_taskID));
+}
+
+/**
+ * Verifies a task still exists.
+ * @returns true on success.
+ */
+bool Task::Verify()
+{
+ return verifyTaskID(m_taskID) == OK;
+}
+
+/**
+ * Gets the priority of a task.
+ * @returns task priority or 0 if an error occured
+ */
+int32_t Task::GetPriority()
+{
+ if (HandleError(getTaskPriority(m_taskID, &m_priority)))
+ return m_priority;
+ else
+ return 0;
+}
+
+/**
+ * This routine changes a task's priority to a specified priority.
+ * Priorities range from 0, the highest priority, to 255, the lowest priority.
+ * Default task priority is 100.
+ * @param priority The priority the task should run at.
+ * @returns true on success.
+ */
+bool Task::SetPriority(int32_t priority)
+{
+ m_priority = priority;
+ return HandleError(setTaskPriority(m_taskID, m_priority));
+}
+
+/**
+ * Returns the name of the task.
+ * @returns Pointer to the name of the task or NULL if not allocated
+ */
+const char* Task::GetName()
+{
+ return m_taskName;
+}
+
+/**
+ * Get the ID of a task
+ * @returns Task ID of this task. Task::kInvalidTaskID (-1) if the task has not been started or has already exited.
+ */
+TASK Task::GetID()
+{
+ if (Verify())
+ return m_taskID;
+ return NULL_TASK;
+}
+
+/**
+ * Handles errors generated by task related code.
+ */
+bool Task::HandleError(STATUS results)
+{
+ if (results != ERROR) return true;
+ int errsv = errno;
+ if (errsv == HAL_objLib_OBJ_ID_ERROR) {
+ wpi_setWPIErrorWithContext(TaskIDError, m_taskName);
+ } else if (errsv == HAL_objLib_OBJ_DELETED) {
+ wpi_setWPIErrorWithContext(TaskDeletedError, m_taskName);
+ } else if (errsv == HAL_taskLib_ILLEGAL_OPTIONS) {
+ wpi_setWPIErrorWithContext(TaskOptionsError, m_taskName);
+ } else if (errsv == HAL_memLib_NOT_ENOUGH_MEMORY) {
+ wpi_setWPIErrorWithContext(TaskMemoryError, m_taskName);
+ } else if (errsv == HAL_taskLib_ILLEGAL_PRIORITY) {
+ wpi_setWPIErrorWithContext(TaskPriorityError, m_taskName);
+ } else {
+ printf("ERROR: errno=%i", errsv);
+ wpi_setWPIErrorWithContext(TaskError, m_taskName);
+ }
+ return false;
+}
+
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/Timer.cpp b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/Timer.cpp
new file mode 100644
index 0000000..e13737c
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/Timer.cpp
@@ -0,0 +1,191 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+
+#include "Timer.h"
+
+#include <time.h>
+
+#include "HAL/HAL.hpp"
+#include "HAL/cpp/Synchronized.hpp"
+#include "Utility.h"
+#include <iostream>
+
+/**
+ * Pause the task for a specified time.
+ *
+ * Pause the execution of the program for a specified period of time given in seconds.
+ * Motors will continue to run at their last assigned values, and sensors will continue to
+ * update. Only the task containing the wait will pause until the wait time is expired.
+ *
+ * @param seconds Length of time to pause, in seconds.
+ */
+void Wait(double seconds)
+{
+ if (seconds < 0.0) return;
+ delaySeconds(seconds);
+}
+
+/**
+ * Return the FPGA system clock time in seconds.
+ * This is deprecated and just forwards to Timer::GetFPGATimestamp().
+ * @return Robot running time in seconds.
+ */
+double GetClock()
+{
+ return Timer::GetFPGATimestamp();
+}
+
+/**
+ * @brief Gives real-time clock system time with nanosecond resolution
+ * @return The time, just in case you want the robot to start autonomous at 8pm on Saturday.
+*/
+double GetTime()
+{
+ struct timespec tp;
+
+ clock_gettime(CLOCK_REALTIME,&tp);
+ double realTime = (double)tp.tv_sec + (double)((double)tp.tv_nsec*1e-9);
+
+ return (realTime);
+}
+
+/**
+ * Create a new timer object.
+ *
+ * Create a new timer object and reset the time to zero. The timer is initially not running and
+ * must be started.
+ */
+Timer::Timer()
+ : m_startTime (0.0)
+ , m_accumulatedTime (0.0)
+ , m_running (false)
+{
+ //Creates a semaphore to control access to critical regions.
+ //Initially 'open'
+ Reset();
+}
+
+Timer::~Timer() {}
+
+/**
+ * Get the current time from the timer. If the clock is running it is derived from
+ * the current system clock the start time stored in the timer class. If the clock
+ * is not running, then return the time when it was last stopped.
+ *
+ * @return Current time value for this timer in seconds
+ */
+double Timer::Get()
+{
+ double result;
+ double currentTime = GetFPGATimestamp();
+
+ ::std::unique_lock<Mutex> sync(m_lock);
+ if(m_running)
+ {
+ // If the current time is before the start time, then the FPGA clock
+ // rolled over. Compensate by adding the ~71 minutes that it takes
+ // to roll over to the current time.
+ if(currentTime < m_startTime) {
+ currentTime += kRolloverTime;
+ }
+
+ result = (currentTime - m_startTime) + m_accumulatedTime;
+ }
+ else
+ {
+ result = m_accumulatedTime;
+ }
+
+ return result;
+}
+
+/**
+ * Reset the timer by setting the time to 0.
+ *
+ * Make the timer startTime the current time so new requests will be relative to now
+ */
+void Timer::Reset()
+{
+ ::std::unique_lock<Mutex> sync(m_lock);
+ m_accumulatedTime = 0;
+ m_startTime = GetFPGATimestamp();
+}
+
+/**
+ * Start the timer running.
+ * Just set the running flag to true indicating that all time requests should be
+ * relative to the system clock.
+ */
+void Timer::Start()
+{
+ ::std::unique_lock<Mutex> sync(m_lock);
+ if (!m_running)
+ {
+ m_startTime = GetFPGATimestamp();
+ m_running = true;
+ }
+}
+
+/**
+ * Stop the timer.
+ * This computes the time as of now and clears the running flag, causing all
+ * subsequent time requests to be read from the accumulated time rather than
+ * looking at the system clock.
+ */
+void Timer::Stop()
+{
+ double temp = Get();
+
+ ::std::unique_lock<Mutex> sync(m_lock);
+ if (m_running)
+ {
+ m_accumulatedTime = temp;
+ m_running = false;
+ }
+}
+
+/**
+ * Check if the period specified has passed and if it has, advance the start
+ * time by that period. This is useful to decide if it's time to do periodic
+ * work without drifting later by the time it took to get around to checking.
+ *
+ * @param period The period to check for (in seconds).
+ * @return True if the period has passed.
+ */
+bool Timer::HasPeriodPassed(double period)
+{
+ if (Get() > period)
+ {
+ ::std::unique_lock<Mutex> sync(m_lock);
+ // Advance the start time by the period.
+ m_startTime += period;
+ // Don't set it to the current time... we want to avoid drift.
+ return true;
+ }
+ return false;
+}
+
+/**
+ * Return the FPGA system clock time in seconds.
+ *
+ * Return the time from the FPGA hardware clock in seconds since the FPGA
+ * started.
+ * Rolls over after 71 minutes.
+ * @returns Robot running time in seconds.
+ */
+double Timer::GetFPGATimestamp()
+{
+ // FPGA returns the timestamp in microseconds
+ // Call the helper GetFPGATime() in Utility.cpp
+ return GetFPGATime() * 1.0e-6;
+}
+
+// Internal function that reads the PPC timestamp counter.
+extern "C"
+{
+ uint32_t niTimestamp32(void);
+ uint64_t niTimestamp64(void);
+}
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/USBCamera.cpp b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/USBCamera.cpp
new file mode 100644
index 0000000..8d36be1
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/USBCamera.cpp
@@ -0,0 +1,328 @@
+#include "USBCamera.h"
+
+#include "Utility.h"
+
+#include <regex>
+#include <chrono>
+#include <thread>
+#include <iostream>
+#include <iomanip>
+
+// This macro expands the given imaq function to ensure that it is called and
+// properly checked for an error, calling the wpi_setImaqErrorWithContext
+// macro
+// To call it, just give the name of the function and the arguments
+#define SAFE_IMAQ_CALL(funName, ...) { \
+ unsigned int error = funName(__VA_ARGS__); \
+ if (error != IMAQdxErrorSuccess) \
+ wpi_setImaqErrorWithContext(error, #funName); \
+ }
+
+// Constants for the manual and auto types
+static const std::string AUTO = "Auto";
+static const std::string MANUAL = "Manual";
+
+/**
+ * Helper function to determine the size of a jpeg. The general structure of
+ * how to parse a jpeg for length can be found in this stackoverflow article:
+ * http://stackoverflow.com/a/1602428. Be sure to also read the comments for
+ * the SOS flag explanation.
+ */
+unsigned int USBCamera::GetJpegSize(void* buffer, unsigned int buffSize) {
+ uint8_t* data = (uint8_t*) buffer;
+ if (!wpi_assert(data[0] == 0xff && data[1] == 0xd8)) return 0;
+ unsigned int pos = 2;
+ while (pos < buffSize) {
+ // All control markers start with 0xff, so if this isn't present,
+ // the JPEG is not valid
+ if (!wpi_assert(data[pos] == 0xff)) return 0;
+ unsigned char t = data[pos+1];
+ // These are RST markers. We just skip them and move onto the next marker
+ if (t == 0x01 || (t >= 0xd0 && t <= 0xd7)) {
+ pos += 2;
+ } else if (t == 0xd9) {
+ // End of Image, add 2 for this and 0-indexed
+ return pos + 2;
+ } else if (!wpi_assert(t != 0xd8)) {
+ // Another start of image, invalid image
+ return 0;
+ } else if (t == 0xda) {
+ // SOS marker. The next two bytes are a 16-bit big-endian int that is
+ // the length of the SOS header, skip that
+ unsigned int len = (((unsigned int) (data[pos+2] & 0xff)) << 8 | ((unsigned int) data[pos+3] & 0xff));
+ pos += len + 2;
+ // The next marker is the first marker that is 0xff followed by a non-RST
+ // element. 0xff followed by 0x00 is an escaped 0xff. 0xd0-0xd7 are RST
+ // markers
+ while (data[pos] != 0xff || data[pos+1] == 0x00 || (data[pos+1] >= 0xd0 && data[pos+1] <= 0xd7)) {
+ pos += 1;
+ if (pos >= buffSize) return 0;
+ }
+ } else {
+ // This is one of several possible markers. The next two bytes are a 16-bit
+ // big-endian int with the length of the marker header, skip that then
+ // continue searching
+ unsigned int len = (((unsigned int) (data[pos+2] & 0xff)) << 8 | ((unsigned int) data[pos+3] & 0xff));
+ pos += len + 2;
+ }
+ }
+
+ return 0;
+}
+
+USBCamera::USBCamera(std::string name, bool useJpeg) :
+ m_id(0),
+ m_name(name),
+ m_useJpeg(useJpeg),
+ m_active(false),
+ m_open(false),
+ m_mutex(),
+ m_width(320),
+ m_height(240),
+ m_fps(30),
+ m_whiteBalance(AUTO),
+ m_whiteBalanceValue(0),
+ m_whiteBalanceValuePresent(false),
+ m_exposure(MANUAL),
+ m_exposureValue(50),
+ m_exposureValuePresent(false),
+ m_brightness(80),
+ m_needSettingsUpdate(true) {
+}
+
+void USBCamera::OpenCamera() {
+ std::unique_lock<std::recursive_mutex> lock(m_mutex);
+ for (unsigned int i = 0; i < 3; i++) {
+ uInt32 id = 0;
+ // Can't use SAFE_IMAQ_CALL here because we only error on the third time
+ IMAQdxError error = IMAQdxOpenCamera(m_name.c_str(), IMAQdxCameraControlModeController, &id);
+ if (error != IMAQdxErrorSuccess) {
+ // Only error on the 3rd try
+ if (i >= 2)
+ wpi_setImaqErrorWithContext(error, "IMAQdxOpenCamera");
+ // Sleep for a few seconds to ensure the error has been dealt with
+ std::this_thread::sleep_for(std::chrono::milliseconds(2000));
+ } else {
+ m_id = id;
+ m_open = true;
+ return;
+ }
+ }
+}
+
+void USBCamera::CloseCamera() {
+ std::unique_lock<std::recursive_mutex> lock(m_mutex);
+ if (!m_open) return;
+ SAFE_IMAQ_CALL(IMAQdxCloseCamera, m_id);
+ m_id = 0;
+ m_open = false;
+}
+
+void USBCamera::StartCapture() {
+ std::unique_lock<std::recursive_mutex> lock(m_mutex);
+ if (!m_open || m_active) return;
+ SAFE_IMAQ_CALL(IMAQdxConfigureGrab, m_id);
+ SAFE_IMAQ_CALL(IMAQdxStartAcquisition, m_id);
+ m_active = true;
+}
+
+void USBCamera::StopCapture() {
+ std::unique_lock<std::recursive_mutex> lock(m_mutex);
+ if (!m_open || !m_active) return;
+ SAFE_IMAQ_CALL(IMAQdxStopAcquisition, m_id);
+ SAFE_IMAQ_CALL(IMAQdxUnconfigureAcquisition, m_id);
+ m_active = false;
+}
+
+void USBCamera::UpdateSettings() {
+ std::unique_lock<std::recursive_mutex> lock(m_mutex);
+ bool wasActive = m_active;
+
+ if (wasActive)
+ StopCapture();
+ if (m_open)
+ CloseCamera();
+ OpenCamera();
+
+ uInt32 count = 0;
+ uInt32 currentMode = 0;
+ SAFE_IMAQ_CALL(IMAQdxEnumerateVideoModes, m_id, NULL, &count, ¤tMode);
+ IMAQdxVideoMode modes[count];
+ SAFE_IMAQ_CALL(IMAQdxEnumerateVideoModes, m_id, modes, &count, ¤tMode);
+
+ // Groups are:
+ // 0 - width
+ // 1 - height
+ // 2 - format
+ // 3 - fps
+ std::regex reMode("([0-9]+)\\s*x\\s*([0-9]+)\\s+(.*?)\\s+([0-9.]+)\\s*fps");
+ IMAQdxVideoMode* foundMode = nullptr;
+ IMAQdxVideoMode* currentModePtr = &modes[currentMode];
+ double foundFps = 1000.0;
+
+ // Loop through the modes, and find the match with the lowest fps
+ for (unsigned int i = 0; i < count; i++) {
+ std::cmatch m;
+ if (!std::regex_match(modes[i].Name, m, reMode))
+ continue;
+ unsigned int width = (unsigned int) std::stoul(m[1].str());
+ unsigned int height = (unsigned int) std::stoul(m[2].str());
+ if (width != m_width)
+ continue;
+ if (height != m_height)
+ continue;
+ double fps = atof(m[4].str().c_str());
+ if (fps < m_fps)
+ continue;
+ if (fps > foundFps)
+ continue;
+ bool isJpeg = m[3].str().compare("jpeg") == 0 || m[3].str().compare("JPEG") == 0;
+ if ((m_useJpeg && !isJpeg) || (!m_useJpeg && isJpeg))
+ continue;
+ foundMode = &modes[i];
+ foundFps = fps;
+ }
+ if (foundMode != nullptr) {
+ if (foundMode->Value != currentModePtr->Value) {
+ SAFE_IMAQ_CALL(IMAQdxSetAttribute, m_id, IMAQdxAttributeVideoMode, IMAQdxValueTypeU32, foundMode->Value);
+ }
+ }
+
+ if (m_whiteBalance.compare(AUTO) == 0) {
+ SAFE_IMAQ_CALL(IMAQdxSetAttribute, m_id, ATTR_WB_MODE, IMAQdxValueTypeString, AUTO.c_str());
+ } else {
+ SAFE_IMAQ_CALL(IMAQdxSetAttribute, m_id, ATTR_WB_MODE, IMAQdxValueTypeString, MANUAL.c_str());
+ if (m_whiteBalanceValuePresent)
+ SAFE_IMAQ_CALL(IMAQdxSetAttribute, m_id, ATTR_WB_VALUE, IMAQdxValueTypeU32, m_whiteBalanceValue);
+ }
+
+ if (m_exposure.compare(AUTO) == 0) {
+ SAFE_IMAQ_CALL(IMAQdxSetAttribute, m_id, ATTR_EX_MODE, IMAQdxValueTypeString, std::string("AutoAperaturePriority").c_str());
+ } else {
+ SAFE_IMAQ_CALL(IMAQdxSetAttribute, m_id, ATTR_EX_MODE, IMAQdxValueTypeString, MANUAL.c_str());
+ if (m_exposureValuePresent) {
+ double minv = 0.0;
+ double maxv = 0.0;
+ SAFE_IMAQ_CALL(IMAQdxGetAttributeMinimum, m_id, ATTR_EX_VALUE, IMAQdxValueTypeF64, &minv);
+ SAFE_IMAQ_CALL(IMAQdxGetAttributeMaximum, m_id, ATTR_EX_VALUE, IMAQdxValueTypeF64, &maxv);
+ double val = minv + ((maxv - minv) * ((double) m_exposureValue / 100.0));
+ SAFE_IMAQ_CALL(IMAQdxSetAttribute, m_id, ATTR_EX_VALUE, IMAQdxValueTypeF64, val);
+ }
+ }
+
+ SAFE_IMAQ_CALL(IMAQdxSetAttribute, m_id, ATTR_BR_MODE, IMAQdxValueTypeString, MANUAL.c_str());
+ double minv = 0.0;
+ double maxv = 0.0;
+ SAFE_IMAQ_CALL(IMAQdxGetAttributeMinimum, m_id, ATTR_BR_VALUE, IMAQdxValueTypeF64, &minv);
+ SAFE_IMAQ_CALL(IMAQdxGetAttributeMaximum, m_id, ATTR_BR_VALUE, IMAQdxValueTypeF64, &maxv);
+ double val = minv + ((maxv - minv) * ((double) m_brightness / 100.0));
+ SAFE_IMAQ_CALL(IMAQdxSetAttribute, m_id, ATTR_BR_VALUE, IMAQdxValueTypeF64, val);
+
+ if (wasActive)
+ StartCapture();
+}
+
+void USBCamera::SetFPS(double fps) {
+ std::unique_lock<std::recursive_mutex> lock(m_mutex);
+ if (m_fps != fps) {
+ m_needSettingsUpdate = true;
+ m_fps = fps;
+ }
+}
+
+void USBCamera::SetSize(unsigned int width, unsigned int height) {
+ std::unique_lock<std::recursive_mutex> lock(m_mutex);
+ if (m_width != width || m_height != height) {
+ m_needSettingsUpdate = true;
+ m_width = width;
+ m_height = height;
+ }
+}
+
+void USBCamera::SetBrightness(unsigned int brightness) {
+ std::unique_lock<std::recursive_mutex> lock(m_mutex);
+ if (m_brightness != brightness) {
+ m_needSettingsUpdate = true;
+ m_brightness = brightness;
+ }
+}
+
+unsigned int USBCamera::GetBrightness() {
+ std::unique_lock<std::recursive_mutex> lock(m_mutex);
+ return m_brightness;
+}
+
+void USBCamera::SetWhiteBalanceAuto() {
+ std::unique_lock<std::recursive_mutex> lock(m_mutex);
+ m_whiteBalance = AUTO;
+ m_whiteBalanceValue = 0;
+ m_whiteBalanceValuePresent = false;
+ m_needSettingsUpdate = true;
+}
+
+void USBCamera::SetWhiteBalanceHoldCurrent() {
+ std::unique_lock<std::recursive_mutex> lock(m_mutex);
+ m_whiteBalance = MANUAL;
+ m_whiteBalanceValue = 0;
+ m_whiteBalanceValuePresent = false;
+ m_needSettingsUpdate = true;
+}
+
+void USBCamera::SetWhiteBalanceManual(unsigned int whiteBalance) {
+ std::unique_lock<std::recursive_mutex> lock(m_mutex);
+ m_whiteBalance = MANUAL;
+ m_whiteBalanceValue = whiteBalance;
+ m_whiteBalanceValuePresent = true;
+ m_needSettingsUpdate = true;
+}
+
+void USBCamera::SetExposureAuto() {
+ std::unique_lock<std::recursive_mutex> lock(m_mutex);
+ m_exposure = AUTO;
+ m_exposureValue = 0;
+ m_exposureValuePresent = false;
+ m_needSettingsUpdate = true;
+}
+
+void USBCamera::SetExposureHoldCurrent() {
+ std::unique_lock<std::recursive_mutex> lock(m_mutex);
+ m_exposure = MANUAL;
+ m_exposureValue = 0;
+ m_exposureValuePresent = false;
+ m_needSettingsUpdate = true;
+}
+
+void USBCamera::SetExposureManual(unsigned int level) {
+ std::unique_lock<std::recursive_mutex> lock(m_mutex);
+ m_exposure = MANUAL;
+ if (level > 100) m_exposureValue = 100;
+ else m_exposureValue = level;
+ m_exposureValuePresent = true;
+ m_needSettingsUpdate = true;
+}
+
+void USBCamera::GetImage(Image* image) {
+ std::unique_lock<std::recursive_mutex> lock(m_mutex);
+ if (m_needSettingsUpdate || m_useJpeg) {
+ m_needSettingsUpdate = false;
+ m_useJpeg = false;
+ UpdateSettings();
+ }
+ // BufNum is not actually used for anything at our level, since we are
+ // waiting until the next image is ready anyway
+ uInt32 bufNum;
+ SAFE_IMAQ_CALL(IMAQdxGrab, m_id, image, 1, &bufNum);
+}
+
+unsigned int USBCamera::GetImageData(void* buffer, unsigned int bufferSize) {
+ std::unique_lock<std::recursive_mutex> lock(m_mutex);
+ if (m_needSettingsUpdate || !m_useJpeg) {
+ m_needSettingsUpdate = false;
+ m_useJpeg = true;
+ UpdateSettings();
+ }
+ // BufNum is not actually used for anything at our level
+ uInt32 bufNum;
+ SAFE_IMAQ_CALL(IMAQdxGetImageData, m_id, buffer, bufferSize, IMAQdxBufferNumberModeLast, 0, &bufNum);
+ return GetJpegSize(buffer, bufferSize);
+}
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/Ultrasonic.cpp b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/Ultrasonic.cpp
new file mode 100644
index 0000000..339ff7c
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/Ultrasonic.cpp
@@ -0,0 +1,304 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+
+#include "Ultrasonic.h"
+
+#include "Counter.h"
+#include "DigitalInput.h"
+#include "DigitalOutput.h"
+//#include "NetworkCommunication/UsageReporting.h"
+#include "Timer.h"
+#include "Utility.h"
+#include "WPIErrors.h"
+
+constexpr double Ultrasonic::kPingTime; ///< Time (sec) for the ping trigger pulse.
+const uint32_t Ultrasonic::kPriority; ///< Priority that the ultrasonic round robin task runs.
+constexpr double Ultrasonic::kMaxUltrasonicTime; ///< Max time (ms) between readings.
+constexpr double Ultrasonic::kSpeedOfSoundInchesPerSec;
+Task Ultrasonic::m_task("UltrasonicChecker", (FUNCPTR)UltrasonicChecker); // task doing the round-robin automatic sensing
+Ultrasonic *Ultrasonic::m_firstSensor = NULL; // head of the ultrasonic sensor list
+bool Ultrasonic::m_automaticEnabled = false; // automatic round robin mode
+SEMAPHORE_ID Ultrasonic::m_semaphore = 0;
+
+/**
+ * Background task that goes through the list of ultrasonic sensors and pings each one in turn. The counter
+ * is configured to read the timing of the returned echo pulse.
+ *
+ * DANGER WILL ROBINSON, DANGER WILL ROBINSON:
+ * This code runs as a task and assumes that none of the ultrasonic sensors will change while it's
+ * running. If one does, then this will certainly break. Make sure to disable automatic mode before changing
+ * anything with the sensors!!
+ */
+void Ultrasonic::UltrasonicChecker()
+{
+ Ultrasonic *u = NULL;
+ while (m_automaticEnabled)
+ {
+ if (u == NULL) u = m_firstSensor;
+ if (u == NULL) return;
+ if (u->IsEnabled())
+ u->m_pingChannel->Pulse(kPingTime); // do the ping
+ u = u->m_nextSensor;
+ Wait(0.1); // wait for ping to return
+ }
+}
+
+/**
+ * Initialize the Ultrasonic Sensor.
+ * This is the common code that initializes the ultrasonic sensor given that there
+ * are two digital I/O channels allocated. If the system was running in automatic mode (round robin)
+ * when the new sensor is added, it is stopped, the sensor is added, then automatic mode is
+ * restored.
+ */
+void Ultrasonic::Initialize()
+{
+ bool originalMode = m_automaticEnabled;
+ if (m_semaphore == 0) m_semaphore = initializeSemaphore(SEMAPHORE_FULL);
+ SetAutomaticMode(false); // kill task when adding a new sensor
+ takeSemaphore(m_semaphore); // link this instance on the list
+ {
+ m_nextSensor = m_firstSensor;
+ m_firstSensor = this;
+ }
+ giveSemaphore(m_semaphore);
+
+ m_counter = new Counter(m_echoChannel); // set up counter for this sensor
+ m_counter->SetMaxPeriod(1.0);
+ m_counter->SetSemiPeriodMode(true);
+ m_counter->Reset();
+ m_enabled = true; // make it available for round robin scheduling
+ SetAutomaticMode(originalMode);
+
+ static int instances = 0;
+ instances++;
+ HALReport(HALUsageReporting::kResourceType_Ultrasonic, instances);
+}
+
+/**
+ * Create an instance of the Ultrasonic Sensor
+ * This is designed to supchannel the Daventech SRF04 and Vex ultrasonic sensors.
+ * @param pingChannel The digital output channel that sends the pulse to initiate the sensor sending
+ * the ping.
+ * @param echoChannel The digital input channel that receives the echo. The length of time that the
+ * echo is high represents the round trip time of the ping, and the distance.
+ * @param units The units returned in either kInches or kMilliMeters
+ */
+Ultrasonic::Ultrasonic(uint32_t pingChannel, uint32_t echoChannel, DistanceUnit units)
+{
+ m_pingChannel = new DigitalOutput(pingChannel);
+ m_echoChannel = new DigitalInput(echoChannel);
+ m_allocatedChannels = true;
+ m_units = units;
+ Initialize();
+}
+
+/**
+ * Create an instance of an Ultrasonic Sensor from a DigitalInput for the echo channel and a DigitalOutput
+ * for the ping channel.
+ * @param pingChannel The digital output object that starts the sensor doing a ping. Requires a 10uS pulse to start.
+ * @param echoChannel The digital input object that times the return pulse to determine the range.
+ * @param units The units returned in either kInches or kMilliMeters
+ */
+Ultrasonic::Ultrasonic(DigitalOutput *pingChannel, DigitalInput *echoChannel, DistanceUnit units)
+{
+ if (pingChannel == NULL || echoChannel == NULL)
+ {
+ wpi_setWPIError(NullParameter);
+ return;
+ }
+ m_allocatedChannels = false;
+ m_pingChannel = pingChannel;
+ m_echoChannel = echoChannel;
+ m_units = units;
+ Initialize();
+}
+
+/**
+ * Create an instance of an Ultrasonic Sensor from a DigitalInput for the echo channel and a DigitalOutput
+ * for the ping channel.
+ * @param pingChannel The digital output object that starts the sensor doing a ping. Requires a 10uS pulse to start.
+ * @param echoChannel The digital input object that times the return pulse to determine the range.
+ * @param units The units returned in either kInches or kMilliMeters
+ */
+Ultrasonic::Ultrasonic(DigitalOutput &pingChannel, DigitalInput &echoChannel, DistanceUnit units)
+{
+ m_allocatedChannels = false;
+ m_pingChannel = &pingChannel;
+ m_echoChannel = &echoChannel;
+ m_units = units;
+ Initialize();
+}
+
+/**
+ * Destructor for the ultrasonic sensor.
+ * Delete the instance of the ultrasonic sensor by freeing the allocated digital channels.
+ * If the system was in automatic mode (round robin), then it is stopped, then started again
+ * after this sensor is removed (provided this wasn't the last sensor).
+ */
+Ultrasonic::~Ultrasonic()
+{
+ bool wasAutomaticMode = m_automaticEnabled;
+ SetAutomaticMode(false);
+ if (m_allocatedChannels)
+ {
+ delete m_pingChannel;
+ delete m_echoChannel;
+ }
+ wpi_assert(m_firstSensor != NULL);
+
+ takeSemaphore(m_semaphore);
+ {
+ if (this == m_firstSensor)
+ {
+ m_firstSensor = m_nextSensor;
+ if (m_firstSensor == NULL)
+ {
+ SetAutomaticMode(false);
+ }
+ }
+ else
+ {
+ wpi_assert(m_firstSensor->m_nextSensor != NULL);
+ for (Ultrasonic *s = m_firstSensor; s != NULL; s = s->m_nextSensor)
+ {
+ if (this == s->m_nextSensor)
+ {
+ s->m_nextSensor = s->m_nextSensor->m_nextSensor;
+ break;
+ }
+ }
+ }
+ }
+ giveSemaphore(m_semaphore);
+ if (m_firstSensor != NULL && wasAutomaticMode)
+ SetAutomaticMode(true);
+}
+
+/**
+ * Turn Automatic mode on/off.
+ * When in Automatic mode, all sensors will fire in round robin, waiting a set
+ * time between each sensor.
+ * @param enabling Set to true if round robin scheduling should start for all the ultrasonic sensors. This
+ * scheduling method assures that the sensors are non-interfering because no two sensors fire at the same time.
+ * If another scheduling algorithm is preffered, it can be implemented by pinging the sensors manually and waiting
+ * for the results to come back.
+ */
+void Ultrasonic::SetAutomaticMode(bool enabling)
+{
+ if (enabling == m_automaticEnabled)
+ return; // ignore the case of no change
+
+ m_automaticEnabled = enabling;
+ if (enabling)
+ {
+ // enabling automatic mode.
+ // Clear all the counters so no data is valid
+ for (Ultrasonic *u = m_firstSensor; u != NULL; u = u->m_nextSensor)
+ {
+ u->m_counter->Reset();
+ }
+ // Start round robin task
+ wpi_assert(m_task.Verify() == false); // should be false since was previously disabled
+ m_task.Start();
+ }
+ else
+ {
+ // disabling automatic mode. Wait for background task to stop running.
+ while (m_task.Verify())
+ Wait(0.15); // just a little longer than the ping time for round-robin to stop
+
+ // clear all the counters (data now invalid) since automatic mode is stopped
+ for (Ultrasonic *u = m_firstSensor; u != NULL; u = u->m_nextSensor)
+ {
+ u->m_counter->Reset();
+ }
+ m_task.Stop();
+ }
+}
+
+/**
+ * Single ping to ultrasonic sensor.
+ * Send out a single ping to the ultrasonic sensor. This only works if automatic (round robin)
+ * mode is disabled. A single ping is sent out, and the counter should count the semi-period
+ * when it comes in. The counter is reset to make the current value invalid.
+ */
+void Ultrasonic::Ping()
+{
+ wpi_assert(!m_automaticEnabled);
+ m_counter->Reset(); // reset the counter to zero (invalid data now)
+ m_pingChannel->Pulse(kPingTime); // do the ping to start getting a single range
+}
+
+/**
+ * Check if there is a valid range measurement.
+ * The ranges are accumulated in a counter that will increment on each edge of the echo (return)
+ * signal. If the count is not at least 2, then the range has not yet been measured, and is invalid.
+ */
+bool Ultrasonic::IsRangeValid()
+{
+ return m_counter->Get() > 1;
+}
+
+/**
+ * Get the range in inches from the ultrasonic sensor.
+ * @return double Range in inches of the target returned from the ultrasonic sensor. If there is
+ * no valid value yet, i.e. at least one measurement hasn't completed, then return 0.
+ */
+double Ultrasonic::GetRangeInches()
+{
+ if (IsRangeValid())
+ return m_counter->GetPeriod() * kSpeedOfSoundInchesPerSec / 2.0;
+ else
+ return 0;
+}
+
+/**
+ * Get the range in millimeters from the ultrasonic sensor.
+ * @return double Range in millimeters of the target returned by the ultrasonic sensor.
+ * If there is no valid value yet, i.e. at least one measurement hasn't complted, then return 0.
+ */
+double Ultrasonic::GetRangeMM()
+{
+ return GetRangeInches() * 25.4;
+}
+
+/**
+ * Get the range in the current DistanceUnit for the PIDSource base object.
+ *
+ * @return The range in DistanceUnit
+ */
+double Ultrasonic::PIDGet()
+{
+ switch(m_units)
+ {
+ case Ultrasonic::kInches:
+ return GetRangeInches();
+ case Ultrasonic::kMilliMeters:
+ return GetRangeMM();
+ default:
+ return 0.0;
+ }
+}
+
+/**
+ * Set the current DistanceUnit that should be used for the PIDSource base object.
+ *
+ * @param units The DistanceUnit that should be used.
+ */
+void Ultrasonic::SetDistanceUnits(DistanceUnit units)
+{
+ m_units = units;
+}
+
+/**
+ * Get the current DistanceUnit that is used for the PIDSource base object.
+ *
+ * @return The type of DistanceUnit that is being used.
+ */
+Ultrasonic::DistanceUnit Ultrasonic::GetDistanceUnits()
+{
+ return m_units;
+}
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/Utility.cpp b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/Utility.cpp
new file mode 100644
index 0000000..d94472b
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/Utility.cpp
@@ -0,0 +1,271 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+
+#include "Utility.h"
+
+//#include "NetworkCommunication/FRCComm.h"
+#include "HAL/HAL.hpp"
+#include "Task.h"
+#include <iostream>
+#include <sstream>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <execinfo.h>
+#include <cxxabi.h>
+#include "nivision.h"
+
+static bool suspendOnAssertEnabled = false;
+
+/**
+ * Enable suspend on assert.
+ * If enabled, the user task will be suspended whenever an assert fails. This
+ * will allow the user to attach to the task with the debugger and examine variables
+ * around the failure.
+ */
+void wpi_suspendOnAssertEnabled(bool enabled)
+{
+ suspendOnAssertEnabled = enabled;
+}
+
+/**
+ * Assert implementation.
+ * This allows breakpoints to be set on an assert.
+ * The users don't call this, but instead use the wpi_assert macros in Utility.h.
+ */
+bool wpi_assert_impl(bool conditionValue,
+ const char *conditionText,
+ const char *message,
+ const char *fileName,
+ uint32_t lineNumber,
+ const char *funcName)
+{
+ if(!conditionValue)
+ {
+ std::stringstream errorStream;
+
+ errorStream << "Assertion \"" << conditionText << "\" ";
+ errorStream << "on line " << lineNumber << " ";
+ errorStream << "of "<< basename(fileName) << " ";
+
+ if(message)
+ {
+ errorStream << "failed: " << message << std::endl;
+ }
+ else
+ {
+ errorStream << "failed." << std::endl;
+ }
+
+ errorStream << GetStackTrace(2);
+
+ std::string error = errorStream.str();
+
+ // Print the error and send it to the DriverStation
+ std::cout << error << std::endl;
+ HALSetErrorData(error.c_str(), error.size(), 100);
+
+ if (suspendOnAssertEnabled) suspendTask(0);
+ }
+
+ return conditionValue;
+}
+
+/**
+ * Common error routines for wpi_assertEqual_impl and wpi_assertNotEqual_impl
+ * This should not be called directly; it should only be used by wpi_assertEqual_impl
+ * and wpi_assertNotEqual_impl.
+ */
+void wpi_assertEqual_common_impl(const char *valueA,
+ const char *valueB,
+ const char *equalityType,
+ const char *message,
+ const char *fileName,
+ uint32_t lineNumber,
+ const char *funcName)
+{
+ std::stringstream errorStream;
+
+ errorStream << "Assertion \"" << valueA << " " << equalityType << " " << valueB << "\" ";
+ errorStream << "on line " << lineNumber << " ";
+ errorStream << "of "<< basename(fileName) << " ";
+
+ if(message)
+ {
+ errorStream << "failed: " << message << std::endl;
+ }
+ else
+ {
+ errorStream << "failed." << std::endl;
+ }
+
+ errorStream << GetStackTrace(3);
+
+ std::string error = errorStream.str();
+
+ // Print the error and send it to the DriverStation
+ std::cout << error << std::endl;
+ HALSetErrorData(error.c_str(), error.size(), 100);
+
+ if (suspendOnAssertEnabled) suspendTask(0);
+}
+
+/**
+ * Assert equal implementation.
+ * This determines whether the two given integers are equal. If not,
+ * the value of each is printed along with an optional message string.
+ * The users don't call this, but instead use the wpi_assertEqual macros in Utility.h.
+ */
+bool wpi_assertEqual_impl(int valueA,
+ int valueB,
+ const char *valueAString,
+ const char *valueBString,
+ const char *message,
+ const char *fileName,
+ uint32_t lineNumber,
+ const char *funcName)
+{
+ if(!(valueA == valueB))
+ {
+ wpi_assertEqual_common_impl(valueAString, valueBString,
+ "==", message, fileName, lineNumber, funcName);
+ }
+ return valueA == valueB;
+}
+
+/**
+ * Assert not equal implementation.
+ * This determines whether the two given integers are equal. If so,
+ * the value of each is printed along with an optional message string.
+ * The users don't call this, but instead use the wpi_assertNotEqual macros in Utility.h.
+ */
+bool wpi_assertNotEqual_impl(int valueA,
+ int valueB,
+ const char *valueAString,
+ const char *valueBString,
+ const char *message,
+ const char *fileName,
+ uint32_t lineNumber,
+ const char *funcName)
+{
+ if(!(valueA != valueB))
+ {
+ wpi_assertEqual_common_impl(valueAString, valueBString,
+ "!=", message, fileName, lineNumber, funcName);
+ }
+ return valueA != valueB;
+}
+
+
+/**
+ * Return the FPGA Version number.
+ * For now, expect this to be competition year.
+ * @return FPGA Version number.
+ */
+uint16_t GetFPGAVersion()
+{
+ int32_t status = 0;
+ uint16_t version = getFPGAVersion(&status);
+ wpi_setGlobalErrorWithContext(status, getHALErrorMessage(status));
+ return version;
+}
+
+/**
+ * Return the FPGA Revision number.
+ * The format of the revision is 3 numbers.
+ * The 12 most significant bits are the Major Revision.
+ * the next 8 bits are the Minor Revision.
+ * The 12 least significant bits are the Build Number.
+ * @return FPGA Revision number.
+ */
+uint32_t GetFPGARevision()
+{
+ int32_t status = 0;
+ uint32_t revision = getFPGARevision(&status);
+ wpi_setGlobalErrorWithContext(status, getHALErrorMessage(status));
+ return revision;
+}
+
+/**
+ * Read the microsecond-resolution timer on the FPGA.
+ *
+ * @return The current time in microseconds according to the FPGA (since FPGA reset).
+ */
+uint32_t GetFPGATime()
+{
+ int32_t status = 0;
+ uint32_t time = getFPGATime(&status);
+ wpi_setGlobalErrorWithContext(status, getHALErrorMessage(status));
+ return time;
+}
+
+/**
+ * Get the state of the "USER" button on the RoboRIO
+ * @return True if the button is currently pressed down
+ */
+bool GetUserButton()
+{
+ int32_t status = 0;
+
+ bool value = getFPGAButton(&status);
+ wpi_setGlobalError(status);
+
+ return value;
+}
+
+/**
+ * Demangle a C++ symbol, used for printing stack traces.
+ */
+static std::string demangle(char const *mangledSymbol)
+{
+ char buffer[256];
+ size_t length;
+ int status;
+
+
+ if(sscanf(mangledSymbol, "%*[^(]%*[(]%255[^)+]", buffer))
+ {
+ char *symbol = abi::__cxa_demangle(buffer, NULL, &length, &status);
+ if(status == 0)
+ {
+ return symbol;
+ }
+ else
+ {
+ // If the symbol couldn't be demangled, it's probably a C function,
+ // so just return it as-is.
+ return buffer;
+ }
+ }
+
+ // If everything else failed, just return the mangled symbol
+ return mangledSymbol;
+}
+
+/**
+ * Get a stack trace, ignoring the first "offset" symbols.
+ * @param offset The number of symbols at the top of the stack to ignore
+ */
+std::string GetStackTrace(uint32_t offset)
+{
+ void *stackTrace[128];
+ int stackSize = backtrace(stackTrace, 128);
+ char **mangledSymbols = backtrace_symbols(stackTrace, stackSize);
+ std::stringstream trace;
+
+ for(int i = offset; i < stackSize; i++)
+ {
+ // Only print recursive functions once in a row.
+ if(i == 0 ||stackTrace[i] != stackTrace[i - 1])
+ {
+ trace << "\tat " << demangle(mangledSymbols[i]) << std::endl;
+ }
+ }
+
+ free(mangledSymbols);
+
+ return trace.str();
+}
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/Victor.cpp b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/Victor.cpp
new file mode 100644
index 0000000..c1cfb71
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/Victor.cpp
@@ -0,0 +1,89 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+
+#include "Victor.h"
+
+//#include "NetworkCommunication/UsageReporting.h"
+
+/**
+ * Common initialization code called by all constructors.
+ *
+ * Note that the Victor uses the following bounds for PWM values. These values were determined
+ * empirically and optimized for the Victor 888. These values should work reasonably well for
+ * Victor 884 controllers as well but if users experience issues such as asymmetric behaviour around
+ * the deadband or inability to saturate the controller in either direction, calibration is recommended.
+ * The calibration procedure can be found in the Victor 884 User Manual available from IFI.
+ *
+ * 2.027ms = full "forward"
+ * 1.525ms = the "high end" of the deadband range
+ * 1.507ms = center of the deadband range (off)
+ * 1.49ms = the "low end" of the deadband range
+ * 1.026ms = full "reverse"
+ */
+void Victor::InitVictor() {
+ SetBounds(2.027, 1.525, 1.507, 1.49, 1.026);
+
+ SetPeriodMultiplier(kPeriodMultiplier_2X);
+ SetRaw(m_centerPwm);
+ SetZeroLatch();
+
+ HALReport(HALUsageReporting::kResourceType_Victor, GetChannel());
+}
+
+/**
+ * Constructor for a Victor
+ * @param channel The PWM channel number that the Victor is attached to. 0-9 are on-board, 10-19 are on the MXP port
+ */
+Victor::Victor(uint32_t channel) : SafePWM(channel)
+{
+ InitVictor();
+}
+
+Victor::~Victor()
+{
+}
+
+/**
+ * Set the PWM value.
+ *
+ * The PWM value is set using a range of -1.0 to 1.0, appropriately
+ * scaling the value for the FPGA.
+ *
+ * @param speed The speed value between -1.0 and 1.0 to set.
+ * @param syncGroup Unused interface.
+ */
+void Victor::Set(float speed, uint8_t syncGroup)
+{
+ SetSpeed(speed);
+}
+
+/**
+ * Get the recently set value of the PWM.
+ *
+ * @return The most recently set value for the PWM between -1.0 and 1.0.
+ */
+float Victor::Get()
+{
+ return GetSpeed();
+}
+
+/**
+ * Common interface for disabling a motor.
+ */
+void Victor::Disable()
+{
+ SetRaw(kPwmDisabled);
+}
+
+/**
+ * Write out the PID value as seen in the PIDOutput base object.
+ *
+ * @param output Write out the PWM value as was found in the PIDController
+ */
+void Victor::PIDWrite(float output)
+{
+ Set(output);
+}
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/VictorSP.cpp b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/VictorSP.cpp
new file mode 100644
index 0000000..39cdda6
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/VictorSP.cpp
@@ -0,0 +1,87 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+
+#include "VictorSP.h"
+
+//#include "NetworkCommunication/UsageReporting.h"
+
+/**
+ * Common initialization code called by all constructors.
+ *
+ * Note that the VictorSP uses the following bounds for PWM values. These values should work reasonably well for
+ * most controllers, but if users experience issues such as asymmetric behavior around
+ * the deadband or inability to saturate the controller in either direction, calibration is recommended.
+ * The calibration procedure can be found in the VictorSP User Manual available from Vex.
+ *
+ * 2.004ms = full "forward"
+ * 1.52ms = the "high end" of the deadband range
+ * 1.50ms = center of the deadband range (off)
+ * 1.48ms = the "low end" of the deadband range
+ * 0.997ms = full "reverse"
+ */
+void VictorSP::InitVictorSP() {
+ SetBounds(2.004, 1.52, 1.50, 1.48, .997);
+ SetPeriodMultiplier(kPeriodMultiplier_1X);
+ SetRaw(m_centerPwm);
+ SetZeroLatch();
+
+ HALReport(HALUsageReporting::kResourceType_Talon, GetChannel());
+}
+
+/**
+ * Constructor for a VictorSP
+ * @param channel The PWM channel that the VictorSP is attached to. 0-9 are on-board, 10-19 are on the MXP port
+ */
+VictorSP::VictorSP(uint32_t channel) : SafePWM(channel)
+{
+ InitVictorSP();
+}
+
+VictorSP::~VictorSP()
+{
+}
+
+/**
+ * Set the PWM value.
+ *
+ * The PWM value is set using a range of -1.0 to 1.0, appropriately
+ * scaling the value for the FPGA.
+ *
+ * @param speed The speed value between -1.0 and 1.0 to set.
+ * @param syncGroup Unused interface.
+ */
+void VictorSP::Set(float speed, uint8_t syncGroup)
+{
+ SetSpeed(speed);
+}
+
+/**
+ * Get the recently set value of the PWM.
+ *
+ * @return The most recently set value for the PWM between -1.0 and 1.0.
+ */
+float VictorSP::Get()
+{
+ return GetSpeed();
+}
+
+/**
+ * Common interface for disabling a motor.
+ */
+void VictorSP::Disable()
+{
+ SetRaw(kPwmDisabled);
+}
+
+/**
+ * Write out the PID value as seen in the PIDOutput base object.
+ *
+ * @param output Write out the PWM value as was found in the PIDController
+ */
+void VictorSP::PIDWrite(float output)
+{
+ Set(output);
+}
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/Vision/AxisCamera.cpp b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/Vision/AxisCamera.cpp
new file mode 100644
index 0000000..47658d0
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/Vision/AxisCamera.cpp
@@ -0,0 +1,645 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2014. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+
+#include "Vision/AxisCamera.h"
+
+#include "WPIErrors.h"
+
+#include <cstring>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <Timer.h>
+#include <iostream>
+#include <sstream>
+
+static const unsigned int kMaxPacketSize = 1536;
+static const unsigned int kImageBufferAllocationIncrement = 1000;
+
+static const std::string kWhiteBalanceStrings[] =
+{ "auto", "hold", "fixed_outdoor1", "fixed_outdoor2", "fixed_indoor",
+ "fixed_fluor1", "fixed_fluor2", };
+
+static const std::string kExposureControlStrings[] =
+{ "auto", "hold", "flickerfree50", "flickerfree60", };
+
+static const std::string kResolutionStrings[] =
+{ "640x480", "480x360", "320x240", "240x180", "176x144", "160x120", };
+
+static const std::string kRotationStrings[] =
+{ "0", "180", };
+
+/**
+ * AxisCamera constructor
+ * @param cameraHost The host to find the camera at, typically an IP address
+ */
+AxisCamera::AxisCamera(std::string const& cameraHost)
+ : m_cameraHost(cameraHost)
+ , m_cameraSocket(-1)
+ , m_freshImage(false)
+ , m_brightness(50)
+ , m_whiteBalance(kWhiteBalance_Automatic)
+ , m_colorLevel(50)
+ , m_exposureControl(kExposureControl_Automatic)
+ , m_exposurePriority(50)
+ , m_maxFPS(0)
+ , m_resolution(kResolution_640x480)
+ , m_compression(50)
+ , m_rotation(kRotation_0)
+ , m_parametersDirty(true)
+ , m_streamDirty(true)
+ , m_done(false)
+{
+ m_captureThread = std::thread(&AxisCamera::Capture, this);
+}
+
+AxisCamera::~AxisCamera()
+{
+ m_done = true;
+ m_captureThread.join();
+}
+
+/*
+ * Return true if the latest image from the camera has not been retrieved by calling GetImage() yet.
+ * @return true if the image has not been retrieved yet.
+ */
+bool AxisCamera::IsFreshImage() const
+{
+ return m_freshImage;
+}
+
+/**
+ * Get an image from the camera and store it in the provided image.
+ * @param image The imaq image to store the result in. This must be an HSL or RGB image.
+ * @return 1 upon success, zero on a failure
+ */
+int AxisCamera::GetImage(Image *image)
+{
+ if (m_imageData.size() == 0)
+ {
+ return 0;
+ }
+
+ std::lock_guard<std::mutex> lock(m_imageDataMutex);
+
+ Priv_ReadJPEGString_C(image, m_imageData.data(), m_imageData.size());
+
+ m_freshImage = false;
+
+ return 1;
+}
+
+/**
+ * Get an image from the camera and store it in the provided image.
+ * @param image The image to store the result in. This must be an HSL or RGB image
+ * @return 1 upon success, zero on a failure
+ */
+int AxisCamera::GetImage(ColorImage *image)
+{
+ return GetImage(image->GetImaqImage());
+}
+
+/**
+ * Instantiate a new image object and fill it with the latest image from the camera.
+ *
+ * The returned pointer is owned by the caller and is their responsibility to delete.
+ * @return a pointer to an HSLImage object
+ */
+HSLImage *AxisCamera::GetImage()
+{
+ HSLImage *image = new HSLImage();
+ GetImage(image);
+ return image;
+}
+
+/**
+ * Copy an image into an existing buffer.
+ * This copies an image into an existing buffer rather than creating a new image
+ * in memory. That way a new image is only allocated when the image being copied is
+ * larger than the destination.
+ * This method is called by the PCVideoServer class.
+ * @param imageData The destination image.
+ * @param numBytes The size of the destination image.
+ * @return 0 if failed (no source image or no memory), 1 if success.
+ */
+int AxisCamera::CopyJPEG(char **destImage, unsigned int &destImageSize, unsigned int &destImageBufferSize)
+{
+ std::lock_guard<std::mutex> lock(m_imageDataMutex);
+ if (destImage == NULL)
+ wpi_setWPIErrorWithContext(NullParameter, "destImage must not be NULL");
+
+ if (m_imageData.size() == 0)
+ return 0; // if no source image
+
+ if (destImageBufferSize < m_imageData.size()) // if current destination buffer too small
+ {
+ if (*destImage != NULL) delete [] *destImage;
+ destImageBufferSize = m_imageData.size() + kImageBufferAllocationIncrement;
+ *destImage = new char[destImageBufferSize];
+ if (*destImage == NULL) return 0;
+ }
+ // copy this image into destination buffer
+ if (*destImage == NULL)
+ {
+ wpi_setWPIErrorWithContext(NullParameter, "*destImage must not be NULL");
+ }
+
+ std::copy(m_imageData.begin(), m_imageData.end(), *destImage);
+ destImageSize = m_imageData.size();;
+ return 1;
+}
+
+/**
+ * Request a change in the brightness of the camera images.
+ * @param brightness valid values 0 .. 100
+ */
+void AxisCamera::WriteBrightness(int brightness)
+{
+ if (brightness < 0 || brightness > 100)
+ {
+ wpi_setWPIErrorWithContext(ParameterOutOfRange,
+ "Brightness must be from 0 to 100");
+ return;
+ }
+
+ std::lock_guard<std::mutex> lock(m_parametersMutex);
+
+ if (m_brightness != brightness)
+ {
+ m_brightness = brightness;
+ m_parametersDirty = true;
+ }
+}
+
+/**
+ * @return The configured brightness of the camera images
+ */
+int AxisCamera::GetBrightness()
+{
+ std::lock_guard<std::mutex> lock(m_parametersMutex);
+ return m_brightness;
+}
+
+/**
+ * Request a change in the white balance on the camera.
+ * @param whiteBalance Valid values from the <code>WhiteBalance</code> enum.
+ */
+void AxisCamera::WriteWhiteBalance(AxisCamera::WhiteBalance whiteBalance)
+{
+ std::lock_guard<std::mutex> lock(m_parametersMutex);
+
+ if (m_whiteBalance != whiteBalance)
+ {
+ m_whiteBalance = whiteBalance;
+ m_parametersDirty = true;
+ }
+}
+
+/**
+ * @return The configured white balances of the camera images
+ */
+AxisCamera::WhiteBalance AxisCamera::GetWhiteBalance()
+{
+ std::lock_guard<std::mutex> lock(m_parametersMutex);
+ return m_whiteBalance;
+}
+
+/**
+ * Request a change in the color level of the camera images.
+ * @param colorLevel valid values are 0 .. 100
+ */
+void AxisCamera::WriteColorLevel(int colorLevel)
+{
+ if (colorLevel < 0 || colorLevel > 100)
+ {
+ wpi_setWPIErrorWithContext(ParameterOutOfRange,
+ "Color level must be from 0 to 100");
+ return;
+ }
+
+ std::lock_guard<std::mutex> lock(m_parametersMutex);
+
+ if (m_colorLevel != colorLevel)
+ {
+ m_colorLevel = colorLevel;
+ m_parametersDirty = true;
+ }
+}
+
+/**
+ * @return The configured color level of the camera images
+ */
+int AxisCamera::GetColorLevel()
+{
+ std::lock_guard<std::mutex> lock(m_parametersMutex);
+ return m_colorLevel;
+}
+
+/**
+ * Request a change in the camera's exposure mode.
+ * @param exposureControl A mode to write in the <code>Exposure</code> enum.
+ */
+void AxisCamera::WriteExposureControl(
+ AxisCamera::ExposureControl exposureControl)
+{
+ std::lock_guard<std::mutex> lock(m_parametersMutex);
+
+ if (m_exposureControl != exposureControl)
+ {
+ m_exposureControl = exposureControl;
+ m_parametersDirty = true;
+ }
+}
+
+/**
+ * @return The configured exposure control mode of the camera
+ */
+AxisCamera::ExposureControl AxisCamera::GetExposureControl()
+{
+ std::lock_guard<std::mutex> lock(m_parametersMutex);
+ return m_exposureControl;
+}
+
+/**
+ * Request a change in the exposure priority of the camera.
+ * @param exposurePriority Valid values are 0, 50, 100.
+ * 0 = Prioritize image quality
+ * 50 = None
+ * 100 = Prioritize frame rate
+ */
+void AxisCamera::WriteExposurePriority(int exposurePriority)
+{
+ if (exposurePriority != 0 && exposurePriority != 50
+ && exposurePriority != 100)
+ {
+ wpi_setWPIErrorWithContext(ParameterOutOfRange,
+ "Exposure priority must be from 0, 50, or 100");
+ return;
+ }
+
+ std::lock_guard<std::mutex> lock(m_parametersMutex);
+
+ if (m_exposurePriority != exposurePriority)
+ {
+ m_exposurePriority = exposurePriority;
+ m_parametersDirty = true;
+ }
+}
+
+/**
+ * @return The configured exposure priority of the camera
+ */
+int AxisCamera::GetExposurePriority()
+{
+ std::lock_guard<std::mutex> lock(m_parametersMutex);
+ return m_exposurePriority;
+}
+
+/**
+ * Write the maximum frames per second that the camera should send
+ * Write 0 to send as many as possible.
+ * @param maxFPS The number of frames the camera should send in a second, exposure permitting.
+ */
+void AxisCamera::WriteMaxFPS(int maxFPS)
+{
+ std::lock_guard<std::mutex> lock(m_parametersMutex);
+
+ if (m_maxFPS != maxFPS)
+ {
+ m_maxFPS = maxFPS;
+ m_parametersDirty = true;
+ m_streamDirty = true;
+ }
+}
+
+/**
+ * @return The configured maximum FPS of the camera
+ */
+int AxisCamera::GetMaxFPS()
+{
+ std::lock_guard<std::mutex> lock(m_parametersMutex);
+ return m_maxFPS;
+}
+
+/**
+ * Write resolution value to camera.
+ * @param resolution The camera resolution value to write to the camera.
+ */
+void AxisCamera::WriteResolution(AxisCamera::Resolution resolution)
+{
+ std::lock_guard<std::mutex> lock(m_parametersMutex);
+
+ if (m_resolution != resolution)
+ {
+ m_resolution = resolution;
+ m_parametersDirty = true;
+ m_streamDirty = true;
+ }
+}
+
+/**
+ * @return The configured resolution of the camera (not necessarily the same
+ * resolution as the most recent image, if it was changed recently.)
+ */
+AxisCamera::Resolution AxisCamera::GetResolution()
+{
+ std::lock_guard<std::mutex> lock(m_parametersMutex);
+ return m_resolution;
+}
+
+/**
+ * Write the rotation value to the camera.
+ * If you mount your camera upside down, use this to adjust the image for you.
+ * @param rotation The angle to rotate the camera (<code>AxisCamera::Rotation::k0</code>
+ * or <code>AxisCamera::Rotation::k180</code>)
+ */
+void AxisCamera::WriteRotation(AxisCamera::Rotation rotation)
+{
+ std::lock_guard<std::mutex> lock(m_parametersMutex);
+
+ if (m_rotation != rotation)
+ {
+ m_rotation = rotation;
+ m_parametersDirty = true;
+ m_streamDirty = true;
+ }
+}
+
+/**
+ * @return The configured rotation mode of the camera
+ */
+AxisCamera::Rotation AxisCamera::GetRotation()
+{
+ std::lock_guard<std::mutex> lock(m_parametersMutex);
+ return m_rotation;
+}
+
+/**
+ * Write the compression value to the camera.
+ * @param compression Values between 0 and 100.
+ */
+void AxisCamera::WriteCompression(int compression)
+{
+ if (compression < 0 || compression > 100)
+ {
+ wpi_setWPIErrorWithContext(ParameterOutOfRange,
+ "Compression must be from 0 to 100");
+ return;
+ }
+
+ std::lock_guard<std::mutex> lock(m_parametersMutex);
+
+ if (m_compression != compression)
+ {
+ m_compression = compression;
+ m_parametersDirty = true;
+ m_streamDirty = true;
+ }
+}
+
+/**
+ * @return The configured compression level of the camera
+ */
+int AxisCamera::GetCompression()
+{
+ std::lock_guard<std::mutex> lock(m_parametersMutex);
+ return m_compression;
+}
+
+/**
+ * Method called in the capture thread to receive images from the camera
+ */
+void AxisCamera::Capture()
+{
+ int consecutiveErrors = 0;
+
+ // Loop on trying to setup the camera connection. This happens in a background
+ // thread so it shouldn't effect the operation of user programs.
+ while (!m_done)
+ {
+ std::string requestString = "GET /mjpg/video.mjpg HTTP/1.1\n"
+ "User-Agent: HTTPStreamClient\n"
+ "Connection: Keep-Alive\n"
+ "Cache-Control: no-cache\n"
+ "Authorization: Basic RlJDOkZSQw==\n\n";
+ m_captureMutex.lock();
+ m_cameraSocket = CreateCameraSocket(requestString, consecutiveErrors > 5);
+ if (m_cameraSocket != -1)
+ {
+ ReadImagesFromCamera();
+ consecutiveErrors = 0;
+ }
+ else
+ {
+ consecutiveErrors++;
+ }
+ m_captureMutex.unlock();
+ Wait(0.5);
+ }
+}
+
+/**
+ * This function actually reads the images from the camera.
+ */
+void AxisCamera::ReadImagesFromCamera()
+{
+ char *imgBuffer = NULL;
+ int imgBufferLength = 0;
+
+ // TODO: these recv calls must be non-blocking. Otherwise if the camera
+ // fails during a read, the code hangs and never retries when the camera comes
+ // back up.
+
+ int counter = 2;
+ while (!m_done)
+ {
+ char initialReadBuffer[kMaxPacketSize] = "";
+ char intermediateBuffer[1];
+ char *trailingPtr = initialReadBuffer;
+ int trailingCounter = 0;
+ while (counter)
+ {
+ // TODO: fix me... this cannot be the most efficient way to approach this, reading one byte at a time.
+ if (recv(m_cameraSocket, intermediateBuffer, 1, 0) == -1)
+ {
+ wpi_setErrnoErrorWithContext("Failed to read image header");
+ close(m_cameraSocket);
+ return;
+ }
+ strncat(initialReadBuffer, intermediateBuffer, 1);
+ // trailingCounter ensures that we start looking for the 4 byte string after
+ // there is at least 4 bytes total. Kind of obscure.
+ // look for 2 blank lines (\r\n)
+ if (NULL != strstr(trailingPtr, "\r\n\r\n"))
+ {
+ --counter;
+ }
+ if (++trailingCounter >= 4)
+ {
+ trailingPtr++;
+ }
+ }
+ counter = 1;
+ char *contentLength = strstr(initialReadBuffer, "Content-Length: ");
+ if (contentLength == NULL)
+ {
+ wpi_setWPIErrorWithContext(IncompatibleMode,
+ "No content-length token found in packet");
+ close(m_cameraSocket);
+ return;
+ }
+ contentLength = contentLength + 16; // skip past "content length"
+ int readLength = atol(contentLength); // get the image byte count
+
+ // Make sure buffer is large enough
+ if (imgBufferLength < readLength)
+ {
+ if (imgBuffer)
+ delete[] imgBuffer;
+ imgBufferLength = readLength + kImageBufferAllocationIncrement;
+ imgBuffer = new char[imgBufferLength];
+ if (imgBuffer == NULL)
+ {
+ imgBufferLength = 0;
+ continue;
+ }
+ }
+
+ // Read the image data for "Content-Length" bytes
+ int bytesRead = 0;
+ int remaining = readLength;
+ while (bytesRead < readLength)
+ {
+ int bytesThisRecv = recv(m_cameraSocket, &imgBuffer[bytesRead],
+ remaining, 0);
+ bytesRead += bytesThisRecv;
+ remaining -= bytesThisRecv;
+ }
+
+ // Update image
+ {
+ std::lock_guard<std::mutex> lock(m_imageDataMutex);
+
+ m_imageData.assign(imgBuffer, imgBuffer + imgBufferLength);
+ m_freshImage = true;
+ }
+
+ if (WriteParameters())
+ {
+ break;
+ }
+ }
+
+ close(m_cameraSocket);
+}
+
+/**
+ * Send a request to the camera to set all of the parameters. This is called
+ * in the capture thread between each frame. This strategy avoids making lots
+ * of redundant HTTP requests, accounts for failed initial requests, and
+ * avoids blocking calls in the main thread unless necessary.
+ *
+ * This method does nothing if no parameters have been modified since it last
+ * completely successfully.
+ *
+ * @return <code>true</code> if the stream should be restarted due to a
+ * parameter changing.
+ */
+bool AxisCamera::WriteParameters()
+{
+ if (m_parametersDirty)
+ {
+ std::stringstream request;
+ request << "GET /axis-cgi/admin/param.cgi?action=update";
+
+ m_parametersMutex.lock();
+ request << "&ImageSource.I0.Sensor.Brightness=" << m_brightness;
+ request << "&ImageSource.I0.Sensor.WhiteBalance=" << kWhiteBalanceStrings[m_whiteBalance];
+ request << "&ImageSource.I0.Sensor.ColorLevel=" << m_colorLevel;
+ request << "&ImageSource.I0.Sensor.Exposure=" << kExposureControlStrings[m_exposureControl];
+ request << "&ImageSource.I0.Sensor.ExposurePriority=" << m_exposurePriority;
+ request << "&Image.I0.Stream.FPS=" << m_maxFPS;
+ request << "&Image.I0.Appearance.Resolution=" << kResolutionStrings[m_resolution];
+ request << "&Image.I0.Appearance.Compression=" << m_compression;
+ request << "&Image.I0.Appearance.Rotation=" << kRotationStrings[m_rotation];
+ m_parametersMutex.unlock();
+
+ request << " HTTP/1.1" << std::endl;
+ request << "User-Agent: HTTPStreamClient" << std::endl;
+ request << "Connection: Keep-Alive" << std::endl;
+ request << "Cache-Control: no-cache" << std::endl;
+ request << "Authorization: Basic RlJDOkZSQw==" << std::endl;
+ request << std::endl;
+
+ int socket = CreateCameraSocket(request.str(), false);
+ if (socket == -1)
+ {
+ wpi_setErrnoErrorWithContext("Error setting camera parameters");
+ }
+ else
+ {
+ close(socket);
+ m_parametersDirty = false;
+
+ if (m_streamDirty)
+ {
+ m_streamDirty = false;
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+/**
+ * Create a socket connected to camera
+ * Used to create a connection to the camera for both capturing images and setting parameters.
+ * @param requestString The initial request string to send upon successful connection.
+ * @param setError If true, rais an error if there's a problem creating the connection.
+ * This is only enabled after several unsucessful connections, so a single one doesn't
+ * cause an error message to be printed if it immediately recovers.
+ * @return -1 if failed, socket handle if successful.
+ */
+int AxisCamera::CreateCameraSocket(std::string const& requestString, bool setError)
+{
+ struct addrinfo *address = 0;
+ int camSocket;
+
+ /* create socket */
+ if ((camSocket = socket(AF_INET, SOCK_STREAM, 0)) == -1)
+ {
+ if (setError) wpi_setErrnoErrorWithContext("Failed to create the camera socket");
+ return -1;
+ }
+
+ if (getaddrinfo(m_cameraHost.c_str(), "80", 0, &address) == -1)
+ {
+ if (setError) wpi_setErrnoErrorWithContext("Failed to create the camera socket");
+ return -1;
+ }
+
+ /* connect to server */
+ if (connect(camSocket, address->ai_addr, address->ai_addrlen) == -1)
+ {
+ if (setError) wpi_setErrnoErrorWithContext("Failed to connect to the camera");
+ close(camSocket);
+ return -1;
+ }
+
+ int sent = send(camSocket, requestString.c_str(), requestString.size(), 0);
+ if (sent == -1)
+ {
+ if (setError) wpi_setErrnoErrorWithContext("Failed to send a request to the camera");
+ close(camSocket);
+ return -1;
+ }
+
+ return camSocket;
+}
+
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/Vision/BaeUtilities.cpp b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/Vision/BaeUtilities.cpp
new file mode 100644
index 0000000..278fa59
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/Vision/BaeUtilities.cpp
@@ -0,0 +1,385 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2014. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <string.h>
+#include <math.h>
+#include <stdlib.h>
+#include <stdarg.h>
+
+#include "Vision/BaeUtilities.h"
+#include "Servo.h"
+#include "Timer.h"
+
+/** @file
+ * Utility functions
+ */
+
+/**
+ * debug output flag options:
+ * DEBUG_OFF, DEBUG_MOSTLY_OFF, DEBUG_SCREEN_ONLY, DEBUG_FILE_ONLY, DEBUG_SCREEN_AND_FILE
+ */
+static DebugOutputType dprintfFlag = DEBUG_OFF;
+
+/**
+ * Set the debug flag to print to screen, file on cRIO, both or neither
+ * @param tempString The format string.
+ */
+void SetDebugFlag ( DebugOutputType flag )
+{ dprintfFlag = flag; }
+
+/**
+ * Debug print to a file and/or a terminal window.
+ * Call like you would call printf.
+ * Set functionName in the function if you want the correct function name to print out.
+ * The file line number will also be printed.
+ * @param tempString The format string.
+ */
+void dprintf ( const char * tempString, ... ) /* Variable argument list */
+{
+ va_list args; /* Input argument list */
+ int line_number; /* Line number passed in argument */
+ int type;
+ const char *functionName; /* Format passed in argument */
+ const char *fmt; /* Format passed in argument */
+ char text[512]; /* Text string */
+ char outtext[512]; /* Text string */
+ FILE *outfile_fd; /* Output file pointer */
+ char filepath[128]; /* Text string */
+ int fatalFlag=0;
+ const char *filename;
+ int index;
+ int tempStringLen;
+
+ if (dprintfFlag == DEBUG_OFF) { return; }
+
+ va_start (args, tempString);
+
+ tempStringLen = strlen(tempString);
+ filename = tempString;
+ for (index=0;index<tempStringLen;index++){
+ if (tempString[index] == ' ') {
+ printf( "ERROR in dprintf: malformed calling sequence (%s)\n",tempString);return;
+ }
+ if (tempString[index] == '\\' || tempString[index] == '/')
+ filename = tempString + index + 1;
+ }
+
+ /* Extract function name */
+ functionName = va_arg (args, const char *);
+
+ /* Extract line number from argument list */
+ line_number = va_arg (args, int);
+
+ /* Extract information type from argument list */
+ type = va_arg (args, int);
+
+ /* Extract format from argument list */
+ fmt = va_arg (args, const char *);
+
+ vsprintf (text, fmt, args);
+
+ va_end (args);
+
+ /* Format output statement */
+ switch (type)
+ {
+ case DEBUG_TYPE:
+ sprintf (outtext, "[%s:%s@%04d] DEBUG %s\n",
+ filename, functionName, line_number, text);
+ break;
+ case INFO_TYPE:
+ sprintf (outtext, "[%s:%s@%04d] INFO %s\n",
+ filename, functionName, line_number, text);
+ break;
+ case ERROR_TYPE:
+ sprintf (outtext, "[%s:%s@%04d] ERROR %s\n",
+ filename, functionName, line_number, text);
+ break;
+ case CRITICAL_TYPE:
+ sprintf (outtext, "[%s:%s@%04d] CRITICAL %s\n",
+ filename, functionName, line_number, text);
+ break;
+ case FATAL_TYPE:
+ fatalFlag = 1;
+ sprintf (outtext, "[%s:%s@%04d] FATAL %s\n",
+ filename, functionName, line_number, text);
+ break;
+ default:
+ printf( "ERROR in dprintf: malformed calling sequence\n");
+ return;
+ break;
+ }
+
+ sprintf (filepath, "%s.debug", filename);
+
+ /* Write output statement */
+ switch (dprintfFlag)
+ {
+ default:
+ case DEBUG_OFF:
+ break;
+ case DEBUG_MOSTLY_OFF:
+ if (fatalFlag) {
+ if ((outfile_fd = fopen (filepath, "a+")) != NULL) {
+ fwrite (outtext, sizeof (char), strlen (outtext), outfile_fd);
+ fclose (outfile_fd);
+ }
+ }
+ break;
+ case DEBUG_SCREEN_ONLY:
+ printf ("%s", outtext);
+ break;
+ case DEBUG_FILE_ONLY:
+ if ((outfile_fd = fopen (filepath, "a+")) != NULL) {
+ fwrite (outtext, sizeof (char), strlen (outtext), outfile_fd);
+ fclose (outfile_fd);
+ }
+ break;
+ case DEBUG_SCREEN_AND_FILE: // BOTH
+ printf ("%s", outtext);
+ if ((outfile_fd = fopen (filepath, "a+")) != NULL) {
+ fwrite (outtext, sizeof (char), strlen (outtext), outfile_fd);
+ fclose (outfile_fd);
+ }
+ break;
+ }
+}
+
+/**
+ * @brief Normalizes a value in a range, used for drive input
+ * @param position The position in the range, starting at 0
+ * @param range The size of the range that position is in
+ * @return The normalized position from -1 to +1
+ */
+double RangeToNormalized(double position, int range){
+ return(((position*2.0)/(double)range)-1.0);
+}
+
+/**
+ * @brief Convert a normalized value to the corresponding value in a range.
+ * This is used to convert normalized values to the servo command range.
+ * @param normalizedValue The normalized value (in the -1 to +1 range)
+ * @param minRange The minimum of the range (0 is default)
+ * @param maxRange The maximum of the range (1 is default)
+ * @return The value in the range corresponding to the input normalized value
+ */
+float NormalizeToRange(float normalizedValue, float minRange, float maxRange) {
+ float range = maxRange-minRange;
+ float temp = (float)((normalizedValue / 2.0)+ 0.5)*range;
+ return (temp + minRange);
+}
+float NormalizeToRange(float normalizedValue) {
+ return (float)((normalizedValue / 2.0) + 0.5);
+}
+
+/**
+ * @brief Displays an activity indicator to console.
+ * Call this function like you would call printf.
+ * @param fmt The format string
+*/
+void ShowActivity (char *fmt, ...)
+{
+ static char activity_indication_string[] = "|/-\\";
+ static int ai = 3;
+ va_list args;
+ char text[1024];
+
+ va_start (args, fmt);
+
+ vsprintf (text, fmt, args);
+
+ ai = ai == 3 ? 0 : ai + 1;
+
+ printf ("%c %s \r", activity_indication_string[ai], text);
+ fflush (stdout);
+
+ va_end (args);
+}
+
+#define PI 3.14159265358979
+/**
+ * @brief Calculate sine wave increments (-1.0 to 1.0).
+ * The first time this is called, it sets up the time increment. Subsequent calls
+ * will give values along the sine wave depending on current time. If the wave is
+ * stopped and restarted, it must be reinitialized with a new "first call".
+ *
+ * @param period length of time to complete a complete wave
+ * @param sinStart Where to start the sine wave (0.0 = 2 pi, pi/2 = 1.0, etc.)
+ */
+double SinPosition (double *period, double sinStart)
+{
+ double rtnVal;
+ static double sinePeriod=0.0;
+ static double timestamp;
+ double sinArg;
+
+ //1st call
+ if (period != NULL) {
+ sinePeriod = *period;
+ timestamp = GetTime();
+ return 0.0;
+ }
+
+ //Multiplying by 2*pi to the time difference makes sinePeriod work if it's measured in seconds.
+ //Adding sinStart to the part multiplied by PI, but not by 2, allows it to work as described in the comments.
+ sinArg = PI *((2.0 * (GetTime() - timestamp)) + sinStart) / sinePeriod;
+ rtnVal = sin (sinArg);
+ return (rtnVal);
+}
+
+
+/**
+ * @brief Find the elapsed time since a specified time.
+ * @param startTime The starting time
+ * @return How long it has been since the starting time
+ */
+double ElapsedTime ( double startTime )
+{
+ double realTime = GetTime();
+ return (realTime-startTime);
+}
+
+/**
+ * @brief Initialize pan parameters
+ * @param period The number of seconds to complete one pan
+ */
+void panInit() {
+ double period = 3.0; // number of seconds for one complete pan
+ SinPosition(&period, 0.0); // initial call to set up time
+}
+
+void panInit(double period) {
+ if (period < 0.0) period=3.0;
+ SinPosition(&period, 0.0); // initial call to set up time
+}
+
+/**
+ * @brief Move the horizontal servo back and forth.
+ * @param panServo The servo object to move
+ * @param sinStart The position on the sine wave to begin the pan
+ */
+void panForTarget(Servo *panServo) { panForTarget(panServo, 0.0); }
+
+void panForTarget(Servo *panServo, double sinStart) {
+ float normalizedSinPosition = (float)SinPosition(NULL, sinStart);
+ float newServoPosition = NormalizeToRange(normalizedSinPosition);
+ panServo->Set( newServoPosition );
+ //ShowActivity ("pan x: normalized %f newServoPosition = %f" ,
+ // normalizedSinPosition, newServoPosition );
+}
+
+
+/** @brief Read a file and return non-comment output string
+
+Call the first time with 0 lineNumber to get the number of lines to read
+Then call with each lineNumber to get one camera parameter. There should
+be one property=value entry on each line, i.e. "exposure=auto"
+
+ * @param inputFile filename to read
+ * @param outputString one string
+ * @param lineNumber if 0, return number of lines; else return that line number
+ * @return int number of lines or -1 if error
+ **/
+int processFile(char *inputFile, char *outputString, int lineNumber)
+{
+ FILE *infile;
+ int stringSize = 80; // max size of one line in file
+ char inputStr[stringSize];
+ int lineCount=0;
+
+ if (lineNumber < 0)
+ return (-1);
+
+ if ((infile = fopen (inputFile, "r")) == NULL) {
+ printf ("Fatal error opening file %s\n",inputFile);
+ return (0);
+ }
+
+ while (!feof(infile)) {
+ if (fgets (inputStr, stringSize, infile) != NULL) {
+ // Skip empty lines
+ if (emptyString(inputStr))
+ continue;
+ // Skip comment lines
+ if (inputStr[0] == '#' || inputStr[0] == '!')
+ continue;
+
+ lineCount++;
+ if (lineNumber == 0)
+ continue;
+ else
+ {
+ if (lineCount == lineNumber)
+ break;
+ }
+ }
+ }
+
+ // close file
+ fclose (infile);
+ // if number lines requested return the count
+ if (lineNumber == 0)
+ return (lineCount);
+ // check for input out of range
+ if (lineNumber > lineCount)
+ return (-1);
+ // return the line selected
+ if (lineCount) {
+ stripString(inputStr);
+ strcpy(outputString, inputStr);
+ return(lineCount);
+ }
+ else {
+ return(-1);
+ }
+}
+
+/** Ignore empty string
+ * @param string to check if empty
+ **/
+int emptyString(char *string)
+{
+ int i,len;
+
+ if(string == NULL)
+ return(1);
+
+ len = strlen(string);
+ for(i=0; i<len; i++) {
+ // Ignore the following characters
+ if (string[i] == '\n' || string[i] == '\r' ||
+ string[i] == '\t' || string[i] == ' ')
+ continue;
+ return(0);
+ }
+ return(1);
+}
+
+/** Remove special characters from string
+ * @param string to process
+ **/
+void stripString(char *string)
+{
+ int i,j,len;
+
+ if(string == NULL)
+ return;
+
+ len = strlen(string);
+ for(i=0,j=0; i<len; i++) {
+ // Remove the following characters from the string
+ if (string[i] == '\n' || string[i] == '\r' || string[i] == '\"')
+ continue;
+ // Copy anything else
+ string[j++] = string[i];
+ }
+ string[j] = '\0';
+}
+
+
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/Vision/BinaryImage.cpp b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/Vision/BinaryImage.cpp
new file mode 100644
index 0000000..b6b0ced
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/Vision/BinaryImage.cpp
@@ -0,0 +1,222 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2014. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+
+#include "Vision/BinaryImage.h"
+#include "WPIErrors.h"
+#include <cstring>
+
+using namespace std;
+
+
+BinaryImage::BinaryImage() : MonoImage()
+{
+}
+
+BinaryImage::~BinaryImage()
+{
+}
+
+/**
+ * Get then number of particles for the image.
+ * @returns the number of particles found for the image.
+ */
+int BinaryImage::GetNumberParticles()
+{
+ int numParticles = 0;
+ int success = imaqCountParticles(m_imaqImage, 1, &numParticles);
+ wpi_setImaqErrorWithContext(success, "Error counting particles");
+ return numParticles;
+}
+
+/**
+ * Get a single particle analysis report.
+ * Get one (of possibly many) particle analysis reports for an image.
+ * @param particleNumber Which particle analysis report to return.
+ * @returns the selected particle analysis report
+ */
+ParticleAnalysisReport BinaryImage::GetParticleAnalysisReport(int particleNumber)
+{
+ ParticleAnalysisReport par;
+ GetParticleAnalysisReport(particleNumber, &par);
+ return par;
+}
+
+/**
+ * Get a single particle analysis report.
+ * Get one (of possibly many) particle analysis reports for an image.
+ * This version could be more efficient when copying many reports.
+ * @param particleNumber Which particle analysis report to return.
+ * @param par the selected particle analysis report
+ */
+void BinaryImage::GetParticleAnalysisReport(int particleNumber, ParticleAnalysisReport *par)
+{
+ int success;
+ int numParticles = 0;
+
+ success = imaqGetImageSize(m_imaqImage, &par->imageWidth, &par->imageHeight);
+ wpi_setImaqErrorWithContext(success, "Error getting image size");
+ if (StatusIsFatal())
+ return;
+
+ success = imaqCountParticles(m_imaqImage, 1, &numParticles);
+ wpi_setImaqErrorWithContext(success, "Error counting particles");
+ if (StatusIsFatal())
+ return;
+
+ if (particleNumber >= numParticles)
+ {
+ wpi_setWPIErrorWithContext(ParameterOutOfRange, "particleNumber");
+ return;
+ }
+
+ par->particleIndex = particleNumber;
+ // Don't bother measuring the rest of the particle if one fails
+ bool good = ParticleMeasurement(particleNumber, IMAQ_MT_CENTER_OF_MASS_X, &par->center_mass_x);
+ good = good && ParticleMeasurement(particleNumber, IMAQ_MT_CENTER_OF_MASS_Y, &par->center_mass_y);
+ good = good && ParticleMeasurement(particleNumber, IMAQ_MT_AREA, &par->particleArea);
+ good = good && ParticleMeasurement(particleNumber, IMAQ_MT_BOUNDING_RECT_TOP, &par->boundingRect.top);
+ good = good && ParticleMeasurement(particleNumber, IMAQ_MT_BOUNDING_RECT_LEFT, &par->boundingRect.left);
+ good = good && ParticleMeasurement(particleNumber, IMAQ_MT_BOUNDING_RECT_HEIGHT, &par->boundingRect.height);
+ good = good && ParticleMeasurement(particleNumber, IMAQ_MT_BOUNDING_RECT_WIDTH, &par->boundingRect.width);
+ good = good && ParticleMeasurement(particleNumber, IMAQ_MT_AREA_BY_IMAGE_AREA, &par->particleToImagePercent);
+ good = good && ParticleMeasurement(particleNumber, IMAQ_MT_AREA_BY_PARTICLE_AND_HOLES_AREA, &par->particleQuality);
+
+ if (good)
+ {
+ /* normalized position (-1 to 1) */
+ par->center_mass_x_normalized = NormalizeFromRange(par->center_mass_x, par->imageWidth);
+ par->center_mass_y_normalized = NormalizeFromRange(par->center_mass_y, par->imageHeight);
+ }
+}
+
+
+/**
+ * Get an ordered vector of particles for the image.
+ * Create a vector of particle analysis reports sorted by size for an image.
+ * The vector contains the actual report structures.
+ * @returns a pointer to the vector of particle analysis reports. The caller must delete the
+ * vector when finished using it.
+ */
+vector<ParticleAnalysisReport>* BinaryImage::GetOrderedParticleAnalysisReports()
+{
+ vector<ParticleAnalysisReport>* particles = new vector<ParticleAnalysisReport>;
+ int particleCount = GetNumberParticles();
+ for(int particleIndex = 0; particleIndex < particleCount; particleIndex++)
+ {
+ particles->push_back(GetParticleAnalysisReport(particleIndex));
+ }
+ // TODO: This is pretty inefficient since each compare in the sort copies
+ // both reports being compared... do it manually instead... while we're
+ // at it, we should provide a version that allows a preallocated buffer of
+ // ParticleAnalysisReport structures
+ sort(particles->begin(), particles->end(), CompareParticleSizes);
+ return particles;
+}
+
+/**
+ * Write a binary image to flash.
+ * Writes the binary image to flash on the cRIO for later inspection.
+ * @param fileName the name of the image file written to the flash.
+ */
+void BinaryImage::Write(const char *fileName)
+{
+ RGBValue colorTable[256];
+ memset(colorTable, 0, sizeof(colorTable));
+ colorTable[0].R = 0;
+ colorTable[1].R = 255;
+ colorTable[0].G = colorTable[1].G = 0;
+ colorTable[0].B = colorTable[1].B = 0;
+ colorTable[0].alpha = colorTable[1].alpha = 0;
+ imaqWriteFile(m_imaqImage, fileName, colorTable);
+}
+
+/**
+ * Measure a single parameter for an image.
+ * Get the measurement for a single parameter about an image by calling the imaqMeasureParticle
+ * function for the selected parameter.
+ * @param particleNumber which particle in the set of particles
+ * @param whatToMeasure the imaq MeasurementType (what to measure)
+ * @param result the value of the measurement
+ * @returns false on failure, true on success
+ */
+bool BinaryImage::ParticleMeasurement(int particleNumber, MeasurementType whatToMeasure, int *result)
+{
+ double resultDouble;
+ bool success = ParticleMeasurement(particleNumber, whatToMeasure, &resultDouble);
+ *result = (int)resultDouble;
+ return success;
+}
+
+/**
+ * Measure a single parameter for an image.
+ * Get the measurement for a single parameter about an image by calling the imaqMeasureParticle
+ * function for the selected parameter.
+ * @param particleNumber which particle in the set of particles
+ * @param whatToMeasure the imaq MeasurementType (what to measure)
+ * @param result the value of the measurement
+ * @returns true on failure, false on success
+ */
+bool BinaryImage::ParticleMeasurement(int particleNumber, MeasurementType whatToMeasure, double *result)
+{
+ int success;
+ success = imaqMeasureParticle(m_imaqImage, particleNumber, 0, whatToMeasure, result);
+ wpi_setImaqErrorWithContext(success, "Error measuring particle");
+ return !StatusIsFatal();
+}
+
+//Normalizes to [-1,1]
+double BinaryImage::NormalizeFromRange(double position, int range)
+{
+ return (position * 2.0 / (double)range) - 1.0;
+}
+
+/**
+ * The compare helper function for sort.
+ * This function compares two particle analysis reports as a helper for the sort function.
+ * @param particle1 The first particle to compare
+ * @param particle2 the second particle to compare
+ * @returns true if particle1 is greater than particle2
+ */
+bool BinaryImage::CompareParticleSizes(ParticleAnalysisReport particle1, ParticleAnalysisReport particle2)
+{
+ //we want descending sort order
+ return particle1.particleToImagePercent > particle2.particleToImagePercent;
+}
+
+BinaryImage *BinaryImage::RemoveSmallObjects(bool connectivity8, int erosions)
+{
+ BinaryImage *result = new BinaryImage();
+ int success = imaqSizeFilter(result->GetImaqImage(), m_imaqImage, connectivity8, erosions, IMAQ_KEEP_LARGE, NULL);
+ wpi_setImaqErrorWithContext(success, "Error in RemoveSmallObjects");
+ return result;
+}
+
+BinaryImage *BinaryImage::RemoveLargeObjects(bool connectivity8, int erosions)
+{
+ BinaryImage *result = new BinaryImage();
+ int success = imaqSizeFilter(result->GetImaqImage(), m_imaqImage, connectivity8, erosions, IMAQ_KEEP_SMALL, NULL);
+ wpi_setImaqErrorWithContext(success, "Error in RemoveLargeObjects");
+ return result;
+}
+
+BinaryImage *BinaryImage::ConvexHull(bool connectivity8)
+{
+ BinaryImage *result = new BinaryImage();
+ int success = imaqConvexHull(result->GetImaqImage(), m_imaqImage, connectivity8);
+ wpi_setImaqErrorWithContext(success, "Error in convex hull operation");
+ return result;
+}
+
+BinaryImage *BinaryImage::ParticleFilter(ParticleFilterCriteria2 *criteria, int criteriaCount)
+{
+ BinaryImage *result = new BinaryImage();
+ int numParticles;
+ ParticleFilterOptions2 filterOptions = {0, 0, 0, 1};
+ int success = imaqParticleFilter4(result->GetImaqImage(), m_imaqImage, criteria, criteriaCount, &filterOptions, NULL, &numParticles);
+ wpi_setImaqErrorWithContext(success, "Error in particle filter operation");
+ return result;
+}
+
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/Vision/ColorImage.cpp b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/Vision/ColorImage.cpp
new file mode 100644
index 0000000..aa72da3
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/Vision/ColorImage.cpp
@@ -0,0 +1,465 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2014. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+
+#include "Vision/ColorImage.h"
+
+#include "WPIErrors.h"
+
+ColorImage::ColorImage(ImageType type) : ImageBase(type)
+{
+}
+
+ColorImage::~ColorImage()
+{
+}
+
+/**
+ * Perform a threshold operation on a ColorImage.
+ * Perform a threshold operation on a ColorImage using the ColorMode supplied
+ * as a parameter.
+ * @param colorMode The type of colorspace this operation should be performed in
+ * @returns a pointer to a binary image
+ */
+BinaryImage *ColorImage::ComputeThreshold(ColorMode colorMode,
+ int low1, int high1,
+ int low2, int high2,
+ int low3, int high3)
+{
+ BinaryImage *result = new BinaryImage();
+ Range range1 = {low1, high1},
+ range2 = {low2, high2},
+ range3 = {low3, high3};
+
+ int success = imaqColorThreshold(result->GetImaqImage(), m_imaqImage, 1, colorMode, &range1, &range2, &range3);
+ wpi_setImaqErrorWithContext(success, "ImaqThreshold error");
+ return result;
+}
+
+/**
+ * Perform a threshold in RGB space.
+ * @param redLow Red low value
+ * @param redHigh Red high value
+ * @param greenLow Green low value
+ * @param greenHigh Green high value
+ * @param blueLow Blue low value
+ * @param blueHigh Blue high value
+ * @returns A pointer to a BinaryImage that represents the result of the threshold operation.
+ */
+BinaryImage *ColorImage::ThresholdRGB(int redLow, int redHigh, int greenLow, int greenHigh, int blueLow, int blueHigh)
+{
+ return ComputeThreshold(IMAQ_RGB, redLow, redHigh, greenLow, greenHigh, blueLow, blueHigh);
+}
+
+/**
+ * Perform a threshold in RGB space.
+ * @param threshold a reference to the Threshold object to use.
+ * @returns A pointer to a BinaryImage that represents the result of the threshold operation.
+ */
+BinaryImage *ColorImage::ThresholdRGB(Threshold &t)
+{
+ return ComputeThreshold(IMAQ_RGB, t.plane1Low, t.plane1High,
+ t.plane2Low, t.plane2High,
+ t.plane3Low, t.plane3High);
+}
+
+/**
+ * Perform a threshold in HSL space.
+ * @param hueLow Low value for hue
+ * @param hueHigh High value for hue
+ * @param saturationLow Low value for saturation
+ * @param saturationHigh High value for saturation
+ * @param luminenceLow Low value for luminence
+ * @param luminenceHigh High value for luminence
+ * @returns a pointer to a BinaryImage that represents the result of the threshold operation.
+ */
+BinaryImage *ColorImage::ThresholdHSL(int hueLow, int hueHigh, int saturationLow, int saturationHigh, int luminenceLow, int luminenceHigh)
+{
+ return ComputeThreshold(IMAQ_HSL, hueLow, hueHigh, saturationLow, saturationHigh, luminenceLow, luminenceHigh);
+}
+
+/**
+ * Perform a threshold in HSL space.
+ * @param threshold a reference to the Threshold object to use.
+ * @returns A pointer to a BinaryImage that represents the result of the threshold operation.
+ */
+BinaryImage *ColorImage::ThresholdHSL(Threshold &t)
+{
+ return ComputeThreshold(IMAQ_HSL, t.plane1Low, t.plane1High,
+ t.plane2Low, t.plane2High,
+ t.plane3Low, t.plane3High);
+}
+
+/**
+ * Perform a threshold in HSV space.
+ * @param hueLow Low value for hue
+ * @param hueHigh High value for hue
+ * @param saturationLow Low value for saturation
+ * @param saturationHigh High value for saturation
+ * @param valueLow Low value
+ * @param valueHigh High value
+ * @returns a pointer to a BinaryImage that represents the result of the threshold operation.
+ */
+BinaryImage *ColorImage::ThresholdHSV(int hueLow, int hueHigh, int saturationLow, int saturationHigh, int valueLow, int valueHigh)
+{
+ return ComputeThreshold(IMAQ_HSV, hueLow, hueHigh, saturationLow, saturationHigh, valueLow, valueHigh);
+}
+
+/**
+ * Perform a threshold in HSV space.
+ * @param threshold a reference to the Threshold object to use.
+ * @returns A pointer to a BinaryImage that represents the result of the threshold operation.
+ */
+BinaryImage *ColorImage::ThresholdHSV(Threshold &t)
+{
+ return ComputeThreshold(IMAQ_HSV, t.plane1Low, t.plane1High,
+ t.plane2Low, t.plane2High,
+ t.plane3Low, t.plane3High);
+}
+
+/**
+ * Perform a threshold in HSI space.
+ * @param hueLow Low value for hue
+ * @param hueHigh High value for hue
+ * @param saturationLow Low value for saturation
+ * @param saturationHigh High value for saturation
+ * @param valueLow Low intensity
+ * @param valueHigh High intensity
+ * @returns a pointer to a BinaryImage that represents the result of the threshold operation.
+ */
+BinaryImage *ColorImage::ThresholdHSI(int hueLow, int hueHigh, int saturationLow, int saturationHigh, int intensityLow, int intensityHigh)
+{
+ return ComputeThreshold(IMAQ_HSI, hueLow, hueHigh, saturationLow, saturationHigh, intensityLow, intensityHigh);
+}
+
+/**
+ * Perform a threshold in HSI space.
+ * @param threshold a reference to the Threshold object to use.
+ * @returns A pointer to a BinaryImage that represents the result of the threshold operation.
+ */
+BinaryImage *ColorImage::ThresholdHSI(Threshold &t)
+{
+ return ComputeThreshold(IMAQ_HSI, t.plane1Low, t.plane1High,
+ t.plane2Low, t.plane2High,
+ t.plane3Low, t.plane3High);
+}
+
+/**
+ * Extract a color plane from the image
+ * @param mode The ColorMode to use for the plane extraction
+ * @param planeNumber Which plane is to be extracted
+ * @returns A pointer to a MonoImage that represents the extracted plane.
+ */
+MonoImage *ColorImage::ExtractColorPlane(ColorMode mode, int planeNumber)
+{
+ MonoImage *result = new MonoImage();
+ if (m_imaqImage == NULL)
+ wpi_setWPIError(NullParameter);
+ int success = imaqExtractColorPlanes(m_imaqImage,
+ mode,
+ (planeNumber == 1) ? result->GetImaqImage() : NULL,
+ (planeNumber == 2) ? result->GetImaqImage() : NULL,
+ (planeNumber == 3) ? result->GetImaqImage() : NULL);
+ wpi_setImaqErrorWithContext(success, "Imaq ExtractColorPlanes failed");
+ return result;
+}
+
+/*
+ * Extract the first color plane for an image.
+ * @param mode The color mode in which to operate
+ * @returns a pointer to a MonoImage that is the extracted plane.
+ */
+MonoImage *ColorImage::ExtractFirstColorPlane(ColorMode mode)
+{
+ return ExtractColorPlane(mode, 1);
+}
+
+/*
+ * Extract the second color plane for an image.
+ * @param mode The color mode in which to operate
+ * @returns a pointer to a MonoImage that is the extracted plane.
+ */
+MonoImage *ColorImage::ExtractSecondColorPlane(ColorMode mode)
+{
+ return ExtractColorPlane(mode, 2);
+}
+
+/*
+ * Extract the third color plane for an image.
+ * @param mode The color mode in which to operate
+ * @returns a pointer to a MonoImage that is the extracted plane.
+ */
+MonoImage *ColorImage::ExtractThirdColorPlane(ColorMode mode)
+{
+ return ExtractColorPlane(mode, 3);
+}
+
+/*
+ * Extract the red plane from an RGB image.
+ * @returns a pointer to a MonoImage that is the extraced plane.
+ */
+MonoImage *ColorImage::GetRedPlane()
+{
+ return ExtractFirstColorPlane(IMAQ_RGB);
+}
+
+/*
+ * Extract the green plane from an RGB image.
+ * @returns a pointer to a MonoImage that is the extraced plane.
+ */
+MonoImage *ColorImage::GetGreenPlane()
+{
+ return ExtractSecondColorPlane(IMAQ_RGB);
+}
+
+/*
+ * Extract the blue plane from an RGB image.
+ * @returns a pointer to a MonoImage that is the extraced plane.
+ */
+MonoImage *ColorImage::GetBluePlane()
+{
+ return ExtractThirdColorPlane(IMAQ_RGB);
+}
+
+/*
+ * Extract the Hue plane from an HSL image.
+ * @returns a pointer to a MonoImage that is the extraced plane.
+ */
+MonoImage *ColorImage::GetHSLHuePlane()
+{
+ return ExtractFirstColorPlane(IMAQ_HSL);
+}
+
+/*
+ * Extract the Hue plane from an HSV image.
+ * @returns a pointer to a MonoImage that is the extraced plane.
+ */
+MonoImage *ColorImage::GetHSVHuePlane()
+{
+ return ExtractFirstColorPlane(IMAQ_HSV);
+}
+
+/*
+ * Extract the Hue plane from an HSI image.
+ * @returns a pointer to a MonoImage that is the extraced plane.
+ */
+MonoImage *ColorImage::GetHSIHuePlane()
+{
+ return ExtractFirstColorPlane(IMAQ_HSI);
+}
+
+/*
+ * Extract the Luminance plane from an HSL image.
+ * @returns a pointer to a MonoImage that is the extraced plane.
+ */
+MonoImage *ColorImage::GetLuminancePlane()
+{
+ return ExtractThirdColorPlane(IMAQ_HSL);
+}
+
+/*
+ * Extract the Value plane from an HSV image.
+ * @returns a pointer to a MonoImage that is the extraced plane.
+ */
+MonoImage *ColorImage::GetValuePlane()
+{
+ return ExtractThirdColorPlane(IMAQ_HSV);
+}
+
+/*
+ * Extract the Intensity plane from an HSI image.
+ * @returns a pointer to a MonoImage that is the extraced plane.
+ */
+MonoImage *ColorImage::GetIntensityPlane()
+{
+ return ExtractThirdColorPlane(IMAQ_HSI);
+}
+
+/**
+ * Replace a plane in the ColorImage with a MonoImage
+ * Replaces a single plane in the image with a MonoImage
+ * @param mode The ColorMode in which to operate
+ * @param plane The pointer to the replacement plane as a MonoImage
+ * @param planeNumber The plane number (1, 2, 3) to replace
+ */
+void ColorImage::ReplacePlane(ColorMode mode, MonoImage *plane, int planeNumber) {
+ int success = imaqReplaceColorPlanes(m_imaqImage,
+ (const Image*) m_imaqImage,
+ mode,
+ (planeNumber == 1) ? plane->GetImaqImage() : NULL,
+ (planeNumber == 2) ? plane->GetImaqImage() : NULL,
+ (planeNumber == 3) ? plane->GetImaqImage() : NULL);
+ wpi_setImaqErrorWithContext(success, "Imaq ReplaceColorPlanes failed");
+}
+
+/**
+ * Replace the first color plane with a MonoImage.
+ * @param mode The color mode in which to operate.
+ * @param plane A pointer to a MonoImage that will replace the specified color plane.
+ */
+void ColorImage::ReplaceFirstColorPlane(ColorMode mode, MonoImage *plane)
+{
+ ReplacePlane(mode, plane, 1);
+}
+
+/**
+ * Replace the second color plane with a MonoImage.
+ * @param mode The color mode in which to operate.
+ * @param plane A pointer to a MonoImage that will replace the specified color plane.
+ */
+void ColorImage::ReplaceSecondColorPlane(ColorMode mode, MonoImage *plane)
+{
+ ReplacePlane(mode, plane, 2);
+}
+
+/**
+ * Replace the third color plane with a MonoImage.
+ * @param mode The color mode in which to operate.
+ * @param plane A pointer to a MonoImage that will replace the specified color plane.
+ */
+void ColorImage::ReplaceThirdColorPlane(ColorMode mode, MonoImage *plane)
+{
+ ReplacePlane(mode, plane, 3);
+}
+
+/**
+ * Replace the red color plane with a MonoImage.
+ * @param mode The color mode in which to operate.
+ * @param plane A pointer to a MonoImage that will replace the specified color plane.
+ */
+void ColorImage::ReplaceRedPlane(MonoImage *plane)
+{
+ ReplaceFirstColorPlane(IMAQ_RGB, plane);
+}
+
+/**
+ * Replace the green color plane with a MonoImage.
+ * @param mode The color mode in which to operate.
+ * @param plane A pointer to a MonoImage that will replace the specified color plane.
+ */
+void ColorImage::ReplaceGreenPlane(MonoImage *plane)
+{
+ ReplaceSecondColorPlane(IMAQ_RGB, plane);
+}
+
+/**
+ * Replace the blue color plane with a MonoImage.
+ * @param mode The color mode in which to operate.
+ * @param plane A pointer to a MonoImage that will replace the specified color plane.
+ */
+void ColorImage::ReplaceBluePlane(MonoImage *plane)
+{
+ ReplaceThirdColorPlane(IMAQ_RGB, plane);
+}
+
+
+/**
+ * Replace the Hue color plane in a HSL image with a MonoImage.
+ * @param mode The color mode in which to operate.
+ * @param plane A pointer to a MonoImage that will replace the specified color plane.
+ */
+void ColorImage::ReplaceHSLHuePlane(MonoImage *plane)
+{
+ return ReplaceFirstColorPlane(IMAQ_HSL, plane);
+}
+
+/**
+ * Replace the Hue color plane in a HSV image with a MonoImage.
+ * @param mode The color mode in which to operate.
+ * @param plane A pointer to a MonoImage that will replace the specified color plane.
+ */
+void ColorImage::ReplaceHSVHuePlane(MonoImage *plane)
+{
+ return ReplaceFirstColorPlane(IMAQ_HSV, plane);
+}
+
+/**
+ * Replace the first Hue plane in a HSI image with a MonoImage.
+ * @param mode The color mode in which to operate.
+ * @param plane A pointer to a MonoImage that will replace the specified color plane.
+ */
+void ColorImage::ReplaceHSIHuePlane(MonoImage *plane)
+{
+ return ReplaceFirstColorPlane(IMAQ_HSI, plane);
+}
+
+/**
+ * Replace the Saturation color plane in an HSL image with a MonoImage.
+ * @param mode The color mode in which to operate.
+ * @param plane A pointer to a MonoImage that will replace the specified color plane.
+ */
+void ColorImage::ReplaceHSLSaturationPlane(MonoImage *plane)
+{
+ return ReplaceSecondColorPlane(IMAQ_HSL, plane);
+}
+
+/**
+ * Replace the Saturation color plane in a HSV image with a MonoImage.
+ * @param mode The color mode in which to operate.
+ * @param plane A pointer to a MonoImage that will replace the specified color plane.
+ */
+void ColorImage::ReplaceHSVSaturationPlane(MonoImage *plane)
+{
+ return ReplaceSecondColorPlane(IMAQ_HSV, plane);
+}
+
+/**
+ * Replace the Saturation color plane in a HSI image with a MonoImage.
+ * @param mode The color mode in which to operate.
+ * @param plane A pointer to a MonoImage that will replace the specified color plane.
+ */
+void ColorImage::ReplaceHSISaturationPlane(MonoImage *plane)
+{
+ return ReplaceSecondColorPlane(IMAQ_HSI, plane);
+}
+
+/**
+ * Replace the Luminance color plane in an HSL image with a MonoImage.
+ * @param mode The color mode in which to operate.
+ * @param plane A pointer to a MonoImage that will replace the specified color plane.
+ */
+void ColorImage::ReplaceLuminancePlane(MonoImage *plane)
+{
+ return ReplaceThirdColorPlane(IMAQ_HSL, plane);
+}
+
+/**
+ * Replace the Value color plane in an HSV with a MonoImage.
+ * @param mode The color mode in which to operate.
+ * @param plane A pointer to a MonoImage that will replace the specified color plane.
+ */
+void ColorImage::ReplaceValuePlane(MonoImage *plane)
+{
+ return ReplaceThirdColorPlane(IMAQ_HSV, plane);
+}
+
+/**
+ * Replace the Intensity color plane in a HSI image with a MonoImage.
+ * @param mode The color mode in which to operate.
+ * @param plane A pointer to a MonoImage that will replace the specified color plane.
+ */
+void ColorImage::ReplaceIntensityPlane(MonoImage *plane)
+{
+ return ReplaceThirdColorPlane(IMAQ_HSI, plane);
+}
+
+//TODO: frcColorEqualize(Image* dest, const Image* source, int colorEqualization) needs to be modified
+//The colorEqualization parameter is discarded and is set to TRUE in the call to imaqColorEqualize.
+void ColorImage::Equalize(bool allPlanes)
+{
+ // Note that this call uses NI-defined TRUE and FALSE
+ int success = imaqColorEqualize(m_imaqImage, (const Image*) m_imaqImage, (allPlanes) ? TRUE : FALSE);
+ wpi_setImaqErrorWithContext(success, "Imaq ColorEqualize error");
+}
+
+void ColorImage::ColorEqualize()
+{
+ Equalize(true);
+}
+
+void ColorImage::LuminanceEqualize()
+{
+ Equalize(false);
+}
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/Vision/FrcError.cpp b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/Vision/FrcError.cpp
new file mode 100644
index 0000000..109dea6
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/Vision/FrcError.cpp
@@ -0,0 +1,1227 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2014. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+
+#include "nivision.h"
+#include "Vision/FrcError.h"
+
+/**
+ * Get the error code returned from the NI Vision library
+ * @return The last error code.
+ */
+int GetLastVisionError()
+{
+ //int errorCode = imaqGetLastVisionError(); // error code: 0 = no error
+ //char* errorText = GetVisionErrorText(errorCode);
+ //dprintf (LOG_DEBUG, "Error = %i %s ", errorCode, errorText);
+ return imaqGetLastError();
+}
+
+/**
+* Get the error text for an NI Vision error code.
+* Note: imaqGetErrorText() is not supported on real time system, so
+* so relevant strings are hardcoded here - the maintained version is
+* in the LabWindows/CVI help file.
+* @param errorCode The error code to find the text for.
+* @return The error text
+*/
+const char* GetVisionErrorText(int errorCode)
+{
+ const char* errorText;
+
+ switch (errorCode)
+ {
+ default:
+ { errorText = "UNKNOWN_ERROR";break;}
+ case -1074395138:
+ { errorText = "ERR_OCR_REGION_TOO_SMALL";break;}
+ case -1074395139:
+ { errorText = "ERR_IMAQ_QR_DIMENSION_INVALID";break;}
+ case -1074395140:
+ { errorText = "ERR_OCR_CHAR_REPORT_CORRUPTED";break;}
+ case -1074395141:
+ { errorText = "ERR_OCR_NO_TEXT_FOUND";break;}
+ case -1074395142:
+ { errorText = "ERR_QR_DETECTION_MODELTYPE";break;}
+ case -1074395143:
+ { errorText = "ERR_QR_DETECTION_MODE";break;}
+ case -1074395144:
+ { errorText = "ERR_QR_INVALID_BARCODE";break;}
+ case -1074395145:
+ { errorText = "ERR_QR_INVALID_READ";break;}
+ case -1074395146:
+ { errorText = "ERR_QR_DETECTION_VERSION";break;}
+ case -1074395147:
+ { errorText = "ERR_BARCODE_RSSLIMITED";break;}
+ case -1074395148:
+ { errorText = "ERR_OVERLAY_GROUP_NOT_FOUND";break;}
+ case -1074395149:
+ { errorText = "ERR_DUPLICATE_TRANSFORM_TYPE";break;}
+ case -1074395151:
+ { errorText = "ERR_OCR_CORRECTION_FAILED";break;}
+ case -1074395155:
+ { errorText = "ERR_OCR_ORIENT_DETECT_FAILED";break;}
+ case -1074395156:
+ { errorText = "ERR_OCR_SKEW_DETECT_FAILED";break;}
+ case -1074395158:
+ { errorText = "ERR_OCR_INVALID_CONTRASTMODE";break;}
+ case -1074395159:
+ { errorText = "ERR_OCR_INVALID_TOLERANCE";break;}
+ case -1074395160:
+ { errorText = "ERR_OCR_INVALID_MAXPOINTSIZE";break;}
+ case -1074395161:
+ { errorText = "ERR_OCR_INVALID_CORRECTIONLEVEL";break;}
+ case -1074395162:
+ { errorText = "ERR_OCR_INVALID_CORRECTIONMODE";break;}
+ case -1074395163:
+ { errorText = "ERR_OCR_INVALID_CHARACTERPREFERENCE";break;}
+ case -1074395164:
+ { errorText = "ERR_OCR_ADD_WORD_FAILED";break;}
+ case -1074395165:
+ { errorText = "ERR_OCR_WTS_DIR_NOT_FOUND";break;}
+ case -1074395166:
+ { errorText = "ERR_OCR_BIN_DIR_NOT_FOUND";break;}
+ case -1074395167:
+ { errorText = "ERR_OCR_INVALID_OUTPUTDELIMITER";break;}
+ case -1074395168:
+ { errorText = "ERR_OCR_INVALID_AUTOCORRECTIONMODE";break;}
+ case -1074395169:
+ { errorText = "ERR_OCR_INVALID_RECOGNITIONMODE";break;}
+ case -1074395170:
+ { errorText = "ERR_OCR_INVALID_CHARACTERTYPE";break;}
+ case -1074395171:
+ { errorText = "ERR_OCR_INI_FILE_NOT_FOUND";break;}
+ case -1074395172:
+ { errorText = "ERR_OCR_INVALID_CHARACTERSET";break;}
+ case -1074395173:
+ { errorText = "ERR_OCR_INVALID_LANGUAGE";break;}
+ case -1074395174:
+ { errorText = "ERR_OCR_INVALID_AUTOORIENTMODE";break;}
+ case -1074395175:
+ { errorText = "ERR_OCR_BAD_USER_DICTIONARY";break;}
+ case -1074395178:
+ { errorText = "ERR_OCR_RECOGNITION_FAILED";break;}
+ case -1074395179:
+ { errorText = "ERR_OCR_PREPROCESSING_FAILED";break;}
+ case -1074395200:
+ { errorText = "ERR_OCR_INVALID_PARAMETER";break;}
+ case -1074395201:
+ { errorText = "ERR_OCR_LOAD_LIBRARY";break;}
+ case -1074395203:
+ { errorText = "ERR_OCR_LIB_INIT";break;}
+ case -1074395210:
+ { errorText = "ERR_OCR_CANNOT_MATCH_TEXT_TEMPLATE";break;}
+ case -1074395211:
+ { errorText = "ERR_OCR_BAD_TEXT_TEMPLATE";break;}
+ case -1074395212:
+ { errorText = "ERR_OCR_TEMPLATE_WRONG_SIZE";break;}
+ case -1074395233:
+ { errorText = "ERR_TEMPLATE_IMAGE_TOO_LARGE";break;}
+ case -1074395234:
+ { errorText = "ERR_TEMPLATE_IMAGE_TOO_SMALL";break;}
+ case -1074395235:
+ { errorText = "ERR_TEMPLATE_IMAGE_CONTRAST_TOO_LOW";break;}
+ case -1074395237:
+ { errorText = "ERR_TEMPLATE_DESCRIPTOR_SHIFT_1";break;}
+ case -1074395238:
+ { errorText = "ERR_TEMPLATE_DESCRIPTOR_NOSHIFT";break;}
+ case -1074395239:
+ { errorText = "ERR_TEMPLATE_DESCRIPTOR_SHIFT";break;}
+ case -1074395240:
+ { errorText = "ERR_TEMPLATE_DESCRIPTOR_ROTATION_1";break;}
+ case -1074395241:
+ { errorText = "ERR_TEMPLATE_DESCRIPTOR_NOROTATION";break;}
+ case -1074395242:
+ { errorText = "ERR_TEMPLATE_DESCRIPTOR_ROTATION";break;}
+ case -1074395243:
+ { errorText = "ERR_TEMPLATE_DESCRIPTOR_4";break;}
+ case -1074395244:
+ { errorText = "ERR_TEMPLATE_DESCRIPTOR_3";break;}
+ case -1074395245:
+ { errorText = "ERR_TEMPLATE_DESCRIPTOR_2";break;}
+ case -1074395246:
+ { errorText = "ERR_TEMPLATE_DESCRIPTOR_1";break;}
+ case -1074395247:
+ { errorText = "ERR_TEMPLATE_DESCRIPTOR";break;}
+ case -1074395248:
+ { errorText = "ERR_TOO_MANY_ROTATION_ANGLE_RANGES";break;}
+ case -1074395249:
+ { errorText = "ERR_ROTATION_ANGLE_RANGE_TOO_LARGE";break;}
+ case -1074395250:
+ { errorText = "ERR_MATCH_SETUP_DATA";break;}
+ case -1074395251:
+ { errorText = "ERR_INVALID_MATCH_MODE";break;}
+ case -1074395252:
+ { errorText = "ERR_LEARN_SETUP_DATA";break;}
+ case -1074395253:
+ { errorText = "ERR_INVALID_LEARN_MODE";break;}
+ case -1074395256:
+ { errorText = "ERR_EVEN_WINDOW_SIZE";break;}
+ case -1074395257:
+ { errorText = "ERR_INVALID_EDGE_DIR";break;}
+ case -1074395258:
+ { errorText = "ERR_BAD_FILTER_WIDTH";break;}
+ case -1074395260:
+ { errorText = "ERR_HEAP_TRASHED";break;}
+ case -1074395261:
+ { errorText = "ERR_GIP_RANGE";break;}
+ case -1074395262:
+ { errorText = "ERR_LCD_BAD_MATCH";break;}
+ case -1074395263:
+ { errorText = "ERR_LCD_NO_SEGMENTS";break;}
+ case -1074395265:
+ { errorText = "ERR_BARCODE";break;}
+ case -1074395267:
+ { errorText = "ERR_COMPLEX_ROOT";break;}
+ case -1074395268:
+ { errorText = "ERR_LINEAR_COEFF";break;}
+ case -1074395269:
+ { errorText = "ERR_NULL_POINTER";break;}
+ case -1074395270:
+ { errorText = "ERR_DIV_BY_ZERO";break;}
+ case -1074395275:
+ { errorText = "ERR_INVALID_BROWSER_IMAGE";break;}
+ case -1074395276:
+ { errorText = "ERR_LINES_PARALLEL";break;}
+ case -1074395277:
+ { errorText = "ERR_BARCODE_CHECKSUM";break;}
+ case -1074395278:
+ { errorText = "ERR_LCD_NOT_NUMERIC";break;}
+ case -1074395279:
+ { errorText = "ERR_ROI_NOT_POLYGON";break;}
+ case -1074395280:
+ { errorText = "ERR_ROI_NOT_RECT";break;}
+ case -1074395281:
+ { errorText = "ERR_IMAGE_SMALLER_THAN_BORDER";break;}
+ case -1074395282:
+ { errorText = "ERR_CANT_DRAW_INTO_VIEWER";break;}
+ case -1074395283:
+ { errorText = "ERR_INVALID_RAKE_DIRECTION";break;}
+ case -1074395284:
+ { errorText = "ERR_INVALID_EDGE_PROCESS";break;}
+ case -1074395285:
+ { errorText = "ERR_INVALID_SPOKE_DIRECTION";break;}
+ case -1074395286:
+ { errorText = "ERR_INVALID_CONCENTRIC_RAKE_DIRECTION";break;}
+ case -1074395287:
+ { errorText = "ERR_INVALID_LINE";break;}
+ case -1074395290:
+ { errorText = "ERR_SHAPEMATCH_BADTEMPLATE";break;}
+ case -1074395291:
+ { errorText = "ERR_SHAPEMATCH_BADIMAGEDATA";break;}
+ case -1074395292:
+ { errorText = "ERR_POINTS_ARE_COLLINEAR";break;}
+ case -1074395293:
+ { errorText = "ERR_CONTOURID_NOT_FOUND";break;}
+ case -1074395294:
+ { errorText = "ERR_CONTOUR_INDEX_OUT_OF_RANGE";break;}
+ case -1074395295:
+ { errorText = "ERR_INVALID_INTERPOLATIONMETHOD_INTERPOLATEPOINTS";break;}
+ case -1074395296:
+ { errorText = "ERR_INVALID_BARCODETYPE";break;}
+ case -1074395297:
+ { errorText = "ERR_INVALID_PARTICLEINFOMODE";break;}
+ case -1074395298:
+ { errorText = "ERR_COMPLEXPLANE_NOT_REAL_OR_IMAGINARY";break;}
+ case -1074395299:
+ { errorText = "ERR_INVALID_COMPLEXPLANE";break;}
+ case -1074395300:
+ { errorText = "ERR_INVALID_METERARCMODE";break;}
+ case -1074395301:
+ { errorText = "ERR_ROI_NOT_2_LINES";break;}
+ case -1074395302:
+ { errorText = "ERR_INVALID_THRESHOLDMETHOD";break;}
+ case -1074395303:
+ { errorText = "ERR_INVALID_NUM_OF_CLASSES";break;}
+ case -1074395304:
+ { errorText = "ERR_INVALID_MATHTRANSFORMMETHOD";break;}
+ case -1074395305:
+ { errorText = "ERR_INVALID_REFERENCEMODE";break;}
+ case -1074395306:
+ { errorText = "ERR_INVALID_TOOL";break;}
+ case -1074395307:
+ { errorText = "ERR_PRECISION_NOT_GTR_THAN_0";break;}
+ case -1074395308:
+ { errorText = "ERR_INVALID_COLORSENSITIVITY";break;}
+ case -1074395309:
+ { errorText = "ERR_INVALID_WINDOW_THREAD_POLICY";break;}
+ case -1074395310:
+ { errorText = "ERR_INVALID_PALETTE_TYPE";break;}
+ case -1074395311:
+ { errorText = "ERR_INVALID_COLOR_SPECTRUM";break;}
+ case -1074395312:
+ { errorText = "ERR_LCD_CALIBRATE";break;}
+ case -1074395313:
+ { errorText = "ERR_WRITE_FILE_NOT_SUPPORTED";break;}
+ case -1074395316:
+ { errorText = "ERR_INVALID_KERNEL_CODE";break;}
+ case -1074395317:
+ { errorText = "ERR_UNDEF_POINT";break;}
+ case -1074395318:
+ { errorText = "ERR_INSF_POINTS";break;}
+ case -1074395319:
+ { errorText = "ERR_INVALID_SUBPIX_TYPE";break;}
+ case -1074395320:
+ { errorText = "ERR_TEMPLATE_EMPTY";break;}
+ case -1074395321:
+ { errorText = "ERR_INVALID_MORPHOLOGYMETHOD";break;}
+ case -1074395322:
+ { errorText = "ERR_INVALID_TEXTALIGNMENT";break;}
+ case -1074395323:
+ { errorText = "ERR_INVALID_FONTCOLOR";break;}
+ case -1074395324:
+ { errorText = "ERR_INVALID_SHAPEMODE";break;}
+ case -1074395325:
+ { errorText = "ERR_INVALID_DRAWMODE";break;}
+ case -1074395326:
+ { errorText = "ERR_INVALID_DRAWMODE_FOR_LINE";break;}
+ case -1074395327:
+ { errorText = "ERR_INVALID_SCALINGMODE";break;}
+ case -1074395328:
+ { errorText = "ERR_INVALID_INTERPOLATIONMETHOD";break;}
+ case -1074395329:
+ { errorText = "ERR_INVALID_OUTLINEMETHOD";break;}
+ case -1074395330:
+ { errorText = "ERR_INVALID_BORDER_SIZE";break;}
+ case -1074395331:
+ { errorText = "ERR_INVALID_BORDERMETHOD";break;}
+ case -1074395332:
+ { errorText = "ERR_INVALID_COMPAREFUNCTION";break;}
+ case -1074395333:
+ { errorText = "ERR_INVALID_VERTICAL_TEXT_ALIGNMENT";break;}
+ case -1074395334:
+ { errorText = "ERR_INVALID_CONVERSIONSTYLE";break;}
+ case -1074395335:
+ { errorText = "ERR_DISPATCH_STATUS_CONFLICT";break;}
+ case -1074395336:
+ { errorText = "ERR_UNKNOWN_ALGORITHM";break;}
+ case -1074395340:
+ { errorText = "ERR_INVALID_SIZETYPE";break;}
+ case -1074395343:
+ { errorText = "ERR_FILE_FILENAME_NULL";break;}
+ case -1074395345:
+ { errorText = "ERR_INVALID_FLIPAXIS";break;}
+ case -1074395346:
+ { errorText = "ERR_INVALID_INTERPOLATIONMETHOD_FOR_ROTATE";break;}
+ case -1074395347:
+ { errorText = "ERR_INVALID_3DDIRECTION";break;}
+ case -1074395348:
+ { errorText = "ERR_INVALID_3DPLANE";break;}
+ case -1074395349:
+ { errorText = "ERR_INVALID_SKELETONMETHOD";break;}
+ case -1074395350:
+ { errorText = "ERR_INVALID_VISION_INFO";break;}
+ case -1074395351:
+ { errorText = "ERR_INVALID_RECT";break;}
+ case -1074395352:
+ { errorText = "ERR_INVALID_FEATURE_MODE";break;}
+ case -1074395353:
+ { errorText = "ERR_INVALID_SEARCH_STRATEGY";break;}
+ case -1074395354:
+ { errorText = "ERR_INVALID_COLOR_WEIGHT";break;}
+ case -1074395355:
+ { errorText = "ERR_INVALID_NUM_MATCHES_REQUESTED";break;}
+ case -1074395356:
+ { errorText = "ERR_INVALID_MIN_MATCH_SCORE";break;}
+ case -1074395357:
+ { errorText = "ERR_INVALID_COLOR_IGNORE_MODE";break;}
+ case -1074395360:
+ { errorText = "ERR_COMPLEX_PLANE";break;}
+ case -1074395361:
+ { errorText = "ERR_INVALID_STEEPNESS";break;}
+ case -1074395362:
+ { errorText = "ERR_INVALID_WIDTH";break;}
+ case -1074395363:
+ { errorText = "ERR_INVALID_SUBSAMPLING_RATIO";break;}
+ case -1074395364:
+ { errorText = "ERR_IGNORE_COLOR_SPECTRUM_SET";break;}
+ case -1074395365:
+ { errorText = "ERR_COLOR_TEMPLATE_DESCRIPTOR_NOSPECTRUM";break;}
+ case -1074395366:
+ { errorText = "ERR_COLOR_TEMPLATE_DESCRIPTOR_NOSHAPE";break;}
+ case -1074395367:
+ { errorText = "ERR_COLOR_TEMPLATE_DESCRIPTOR_ROTATION_5";break;}
+ case -1074395368:
+ { errorText = "ERR_COLOR_TEMPLATE_DESCRIPTOR_ROTATION_4";break;}
+ case -1074395369:
+ { errorText = "ERR_COLOR_TEMPLATE_DESCRIPTOR_ROTATION_3";break;}
+ case -1074395370:
+ { errorText = "ERR_COLOR_TEMPLATE_DESCRIPTOR_ROTATION_2";break;}
+ case -1074395371:
+ { errorText = "ERR_COLOR_TEMPLATE_DESCRIPTOR_ROTATION_1";break;}
+ case -1074395372:
+ { errorText = "ERR_COLOR_TEMPLATE_DESCRIPTOR_NOROTATION";break;}
+ case -1074395373:
+ { errorText = "ERR_COLOR_TEMPLATE_DESCRIPTOR_ROTATION";break;}
+ case -1074395374:
+ { errorText = "ERR_COLOR_TEMPLATE_DESCRIPTOR_SHIFT_2";break;}
+ case -1074395375:
+ { errorText = "ERR_COLOR_TEMPLATE_DESCRIPTOR_SHIFT_1";break;}
+ case -1074395376:
+ { errorText = "ERR_COLOR_TEMPLATE_DESCRIPTOR_NOSHIFT";break;}
+ case -1074395377:
+ { errorText = "ERR_COLOR_TEMPLATE_DESCRIPTOR_SHIFT";break;}
+ case -1074395378:
+ { errorText = "ERR_COLOR_TEMPLATE_DESCRIPTOR_6";break;}
+ case -1074395379:
+ { errorText = "ERR_COLOR_TEMPLATE_DESCRIPTOR_5";break;}
+ case -1074395380:
+ { errorText = "ERR_COLOR_TEMPLATE_DESCRIPTOR_4";break;}
+ case -1074395381:
+ { errorText = "ERR_COLOR_TEMPLATE_DESCRIPTOR_3";break;}
+ case -1074395382:
+ { errorText = "ERR_COLOR_TEMPLATE_DESCRIPTOR_2";break;}
+ case -1074395383:
+ { errorText = "ERR_COLOR_TEMPLATE_DESCRIPTOR_1";break;}
+ case -1074395384:
+ { errorText = "ERR_COLOR_TEMPLATE_DESCRIPTOR";break;}
+ case -1074395385:
+ { errorText = "ERR_COLOR_ROTATION_REQUIRES_SHAPE_FEATURE";break;}
+ case -1074395386:
+ { errorText = "ERR_COLOR_MATCH_SETUP_DATA_SHAPE";break;}
+ case -1074395387:
+ { errorText = "ERR_COLOR_MATCH_SETUP_DATA";break;}
+ case -1074395388:
+ { errorText = "ERR_COLOR_LEARN_SETUP_DATA_SHAPE";break;}
+ case -1074395389:
+ { errorText = "ERR_COLOR_LEARN_SETUP_DATA";break;}
+ case -1074395390:
+ { errorText = "ERR_COLOR_TEMPLATE_IMAGE_LUMINANCE_CONTRAST_TOO_LOW";break;}
+ case -1074395391:
+ { errorText = "ERR_COLOR_TEMPLATE_IMAGE_HUE_CONTRAST_TOO_LOW";break;}
+ case -1074395392:
+ { errorText = "ERR_COLOR_TEMPLATE_IMAGE_TOO_LARGE";break;}
+ case -1074395393:
+ { errorText = "ERR_COLOR_TEMPLATE_IMAGE_TOO_SMALL";break;}
+ case -1074395394:
+ { errorText = "ERR_COLOR_SPECTRUM_MASK";break;}
+ case -1074395395:
+ { errorText = "ERR_COLOR_IMAGE_REQUIRED";break;}
+ case -1074395397:
+ { errorText = "ERR_COMPLEX_IMAGE_REQUIRED";break;}
+ case -1074395399:
+ { errorText = "ERR_MULTICORE_INVALID_ARGUMENT";break;}
+ case -1074395400:
+ { errorText = "ERR_MULTICORE_OPERATION";break;}
+ case -1074395401:
+ { errorText = "ERR_INVALID_MATCHFACTOR";break;}
+ case -1074395402:
+ { errorText = "ERR_INVALID_MAXPOINTS";break;}
+ case -1074395403:
+ { errorText = "ERR_EXTRAINFO_VERSION";break;}
+ case -1074395404:
+ { errorText = "ERR_INVALID_INTERPOLATIONMETHOD_FOR_UNWRAP";break;}
+ case -1074395405:
+ { errorText = "ERR_INVALID_TEXTORIENTATION";break;}
+ case -1074395406:
+ { errorText = "ERR_COORDSYS_NOT_FOUND";break;}
+ case -1074395407:
+ { errorText = "ERR_INVALID_CONTRAST";break;}
+ case -1074395408:
+ { errorText = "ERR_INVALID_DETECTION_MODE";break;}
+ case -1074395409:
+ { errorText = "ERR_INVALID_SUBPIXEL_DIVISIONS";break;}
+ case -1074395410:
+ { errorText = "ERR_INVALID_ICONS_PER_LINE";break;}
+ case -1074395549:
+ { errorText = "ERR_NIOCR_INVALID_NUMBER_OF_OBJECTS_TO_VERIFY";break;}
+ case -1074395550:
+ { errorText = "ERR_NIOCR_INVALID_CHARACTER_VALUE";break;}
+ case -1074395551:
+ { errorText = "ERR_NIOCR_RENAME_REFCHAR";break;}
+ case -1074395552:
+ { errorText = "ERR_NIOCR_NOT_A_VALID_CHARACTER_SET";break;}
+ case -1074395553:
+ { errorText = "ERR_NIOCR_INVALID_MIN_BOUNDING_RECT_HEIGHT";break;}
+ case -1074395554:
+ { errorText = "ERR_NIOCR_INVALID_READ_RESOLUTION";break;}
+ case -1074395555:
+ { errorText = "ERR_NIOCR_INVALID_SPACING_RANGE";break;}
+ case -1074395556:
+ { errorText = "ERR_NIOCR_INVALID_BOUNDING_RECT_HEIGHT_RANGE";break;}
+ case -1074395557:
+ { errorText = "ERR_NIOCR_INVALID_BOUNDING_RECT_WIDTH_RANGE";break;}
+ case -1074395558:
+ { errorText = "ERR_NIOCR_INVALID_CHARACTER_SIZE_RANGE";break;}
+ case -1074395559:
+ { errorText = "ERR_NIOCR_INVALID_READ_OPTION";break;}
+ case -1074395560:
+ { errorText = "ERR_NIOCR_INVALID_OBJECT_INDEX";break;}
+ case -1074395561:
+ { errorText = "ERR_NIOCR_INVALID_NUMBER_OF_CHARACTERS";break;}
+ case -1074395562:
+ { errorText = "ERR_NIOCR_BOOLEAN_VALUE_FOR_STRING_ATTRIBUTE";break;}
+ case -1074395563:
+ { errorText = "ERR_NIOCR_UNLICENSED";break;}
+ case -1074395564:
+ { errorText = "ERR_NIOCR_INVALID_PREDEFINED_CHARACTER";break;}
+ case -1074395565:
+ { errorText = "ERR_NIOCR_MUST_BE_SINGLE_CHARACTER";break;}
+ case -1074395566:
+ { errorText = "ERR_NIOCR_BOOLEAN_VALUE_FOR_INTEGER_ATTRIBUTE";break;}
+ case -1074395567:
+ { errorText = "ERR_NIOCR_STRING_VALUE_FOR_BOOLEAN_ATTRIBUTE";break;}
+ case -1074395568:
+ { errorText = "ERR_NIOCR_STRING_VALUE_FOR_INTEGER_ATTRIBUTE";break;}
+ case -1074395569:
+ { errorText = "ERR_NIOCR_INVALID_ATTRIBUTE";break;}
+ case -1074395570:
+ { errorText = "ERR_NIOCR_INTEGER_VALUE_FOR_BOOLEAN_ATTRIBUTE";break;}
+ case -1074395571:
+ { errorText = "ERR_NIOCR_GET_ONLY_ATTRIBUTE";break;}
+ case -1074395572:
+ { errorText = "ERR_NIOCR_INTEGER_VALUE_FOR_STRING_ATTRIBUTE";break;}
+ case -1074395573:
+ { errorText = "ERR_NIOCR_INVALID_CHARACTER_SET_FILE_VERSION";break;}
+ case -1074395574:
+ { errorText = "ERR_NIOCR_CHARACTER_SET_DESCRIPTION_TOO_LONG";break;}
+ case -1074395575:
+ { errorText = "ERR_NIOCR_INVALID_NUMBER_OF_EROSIONS";break;}
+ case -1074395576:
+ { errorText = "ERR_NIOCR_CHARACTER_VALUE_TOO_LONG";break;}
+ case -1074395577:
+ { errorText = "ERR_NIOCR_CHARACTER_VALUE_CANNOT_BE_EMPTYSTRING";break;}
+ case -1074395578:
+ { errorText = "ERR_NIOCR_INVALID_CHARACTER_SET_FILE";break;}
+ case -1074395579:
+ { errorText = "ERR_NIOCR_INVALID_ASPECT_RATIO";break;}
+ case -1074395580:
+ { errorText = "ERR_NIOCR_INVALID_MIN_BOUNDING_RECT_WIDTH";break;}
+ case -1074395581:
+ { errorText = "ERR_NIOCR_INVALID_MAX_VERT_ELEMENT_SPACING";break;}
+ case -1074395582:
+ { errorText = "ERR_NIOCR_INVALID_MAX_HORIZ_ELEMENT_SPACING";break;}
+ case -1074395583:
+ { errorText = "ERR_NIOCR_INVALID_MIN_CHAR_SPACING";break;}
+ case -1074395584:
+ { errorText = "ERR_NIOCR_INVALID_THRESHOLD_LIMITS";break;}
+ case -1074395585:
+ { errorText = "ERR_NIOCR_INVALID_UPPER_THRESHOLD_LIMIT";break;}
+ case -1074395586:
+ { errorText = "ERR_NIOCR_INVALID_LOWER_THRESHOLD_LIMIT";break;}
+ case -1074395587:
+ { errorText = "ERR_NIOCR_INVALID_THRESHOLD_RANGE";break;}
+ case -1074395588:
+ { errorText = "ERR_NIOCR_INVALID_HIGH_THRESHOLD_VALUE";break;}
+ case -1074395589:
+ { errorText = "ERR_NIOCR_INVALID_LOW_THRESHOLD_VALUE";break;}
+ case -1074395590:
+ { errorText = "ERR_NIOCR_INVALID_NUMBER_OF_VALID_CHARACTER_POSITIONS";break;}
+ case -1074395591:
+ { errorText = "ERR_NIOCR_INVALID_CHARACTER_INDEX";break;}
+ case -1074395592:
+ { errorText = "ERR_NIOCR_INVALID_READ_STRATEGY";break;}
+ case -1074395593:
+ { errorText = "ERR_NIOCR_INVALID_NUMBER_OF_BLOCKS";break;}
+ case -1074395594:
+ { errorText = "ERR_NIOCR_INVALID_SUBSTITUTION_CHARACTER";break;}
+ case -1074395595:
+ { errorText = "ERR_NIOCR_INVALID_THRESHOLD_MODE";break;}
+ case -1074395596:
+ { errorText = "ERR_NIOCR_INVALID_CHARACTER_SIZE";break;}
+ case -1074395597:
+ { errorText = "ERR_NIOCR_NOT_A_VALID_SESSION";break;}
+ case -1074395598:
+ { errorText = "ERR_NIOCR_INVALID_ACCEPTANCE_LEVEL";break;}
+ case -1074395600:
+ { errorText = "ERR_INFO_NOT_FOUND";break;}
+ case -1074395601:
+ { errorText = "ERR_INVALID_EDGE_THRESHOLD";break;}
+ case -1074395602:
+ { errorText = "ERR_INVALID_MINIMUM_CURVE_LENGTH";break;}
+ case -1074395603:
+ { errorText = "ERR_INVALID_ROW_STEP";break;}
+ case -1074395604:
+ { errorText = "ERR_INVALID_COLUMN_STEP";break;}
+ case -1074395605:
+ { errorText = "ERR_INVALID_MAXIMUM_END_POINT_GAP";break;}
+ case -1074395606:
+ { errorText = "ERR_INVALID_MINIMUM_FEATURES_TO_MATCH";break;}
+ case -1074395607:
+ { errorText = "ERR_INVALID_MAXIMUM_FEATURES_PER_MATCH";break;}
+ case -1074395608:
+ { errorText = "ERR_INVALID_SUBPIXEL_ITERATIONS";break;}
+ case -1074395609:
+ { errorText = "ERR_INVALID_SUBPIXEL_TOLERANCE";break;}
+ case -1074395610:
+ { errorText = "ERR_INVALID_INITIAL_MATCH_LIST_LENGTH";break;}
+ case -1074395611:
+ { errorText = "ERR_INVALID_MINIMUM_RECTANGLE_DIMENSION";break;}
+ case -1074395612:
+ { errorText = "ERR_INVALID_MINIMUM_FEATURE_RADIUS";break;}
+ case -1074395613:
+ { errorText = "ERR_INVALID_MINIMUM_FEATURE_LENGTH";break;}
+ case -1074395614:
+ { errorText = "ERR_INVALID_MINIMUM_FEATURE_ASPECT_RATIO";break;}
+ case -1074395615:
+ { errorText = "ERR_INVALID_MINIMUM_FEATURE_STRENGTH";break;}
+ case -1074395616:
+ { errorText = "ERR_INVALID_EDGE_FILTER_SIZE";break;}
+ case -1074395617:
+ { errorText = "ERR_INVALID_NUMBER_OF_FEATURES_RANGE";break;}
+ case -1074395618:
+ { errorText = "ERR_TOO_MANY_SCALE_RANGES";break;}
+ case -1074395619:
+ { errorText = "ERR_TOO_MANY_OCCLUSION_RANGES";break;}
+ case -1074395620:
+ { errorText = "ERR_INVALID_CURVE_EXTRACTION_MODE";break;}
+ case -1074395621:
+ { errorText = "ERR_INVALID_LEARN_GEOMETRIC_PATTERN_SETUP_DATA";break;}
+ case -1074395622:
+ { errorText = "ERR_INVALID_MATCH_GEOMETRIC_PATTERN_SETUP_DATA";break;}
+ case -1074395623:
+ { errorText = "ERR_INVALID_SCALE_RANGE";break;}
+ case -1074395624:
+ { errorText = "ERR_INVALID_OCCLUSION_RANGE";break;}
+ case -1074395625:
+ { errorText = "ERR_INVALID_MATCH_CONSTRAINT_TYPE";break;}
+ case -1074395626:
+ { errorText = "ERR_NOT_ENOUGH_TEMPLATE_FEATURES";break;}
+ case -1074395627:
+ { errorText = "ERR_NOT_ENOUGH_TEMPLATE_FEATURES_1";break;}
+ case -1074395628:
+ { errorText = "ERR_INVALID_GEOMETRIC_MATCHING_TEMPLATE";break;}
+ case -1074395629:
+ { errorText = "ERR_INVALID_MAXIMUM_PIXEL_DISTANCE_FROM_LINE";break;}
+ case -1074395630:
+ { errorText = "ERR_INVALID_MAXIMUM_FEATURES_LEARNED";break;}
+ case -1074395631:
+ { errorText = "ERR_INVALID_MIN_MATCH_SEPARATION_DISTANCE";break;}
+ case -1074395632:
+ { errorText = "ERR_INVALID_MIN_MATCH_SEPARATION_ANGLE";break;}
+ case -1074395633:
+ { errorText = "ERR_INVALID_MIN_MATCH_SEPARATION_SCALE";break;}
+ case -1074395634:
+ { errorText = "ERR_INVALID_MAX_MATCH_OVERLAP";break;}
+ case -1074395635:
+ { errorText = "ERR_INVALID_SHAPE_DESCRIPTOR";break;}
+ case -1074395636:
+ { errorText = "ERR_DIRECTX_NOT_FOUND";break;}
+ case -1074395637:
+ { errorText = "ERR_HARDWARE_DOESNT_SUPPORT_NONTEARING";break;}
+ case -1074395638:
+ { errorText = "ERR_INVALID_FILL_STYLE";break;}
+ case -1074395639:
+ { errorText = "ERR_INVALID_HATCH_STYLE";break;}
+ case -1074395640:
+ { errorText = "ERR_TOO_MANY_ZONES";break;}
+ case -1074395641:
+ { errorText = "ERR_DUPLICATE_LABEL";break;}
+ case -1074395642:
+ { errorText = "ERR_LABEL_NOT_FOUND";break;}
+ case -1074395643:
+ { errorText = "ERR_INVALID_NUMBER_OF_MATCH_OPTIONS";break;}
+ case -1074395644:
+ { errorText = "ERR_LABEL_TOO_LONG";break;}
+ case -1074395645:
+ { errorText = "ERR_INVALID_NUMBER_OF_LABELS";break;}
+ case -1074395646:
+ { errorText = "ERR_NO_TEMPLATE_TO_LEARN";break;}
+ case -1074395647:
+ { errorText = "ERR_INVALID_MULTIPLE_GEOMETRIC_TEMPLATE";break;}
+ case -1074395648:
+ { errorText = "ERR_TEMPLATE_NOT_LEARNED";break;}
+ case -1074395649:
+ { errorText = "ERR_INVALID_GEOMETRIC_FEATURE_TYPE";break;}
+ case -1074395650:
+ { errorText = "ERR_CURVE_EXTRACTION_MODE_MUST_BE_SAME";break;}
+ case -1074395651:
+ { errorText = "ERR_EDGE_FILTER_SIZE_MUST_BE_SAME";break;}
+ case -1074395652:
+ { errorText = "ERR_OPENING_NEWER_GEOMETRIC_MATCHING_TEMPLATE";break;}
+ case -1074395653:
+ { errorText = "ERR_OPENING_NEWER_MULTIPLE_GEOMETRIC_TEMPLATE";break;}
+ case -1074395654:
+ { errorText = "ERR_GRADING_INFORMATION_NOT_FOUND";break;}
+ case -1074395655:
+ { errorText = "ERR_ENABLE_CALIBRATION_SUPPORT_MUST_BE_SAME";break;}
+ case -1074395656:
+ { errorText = "ERR_SMOOTH_CONTOURS_MUST_BE_SAME";break;}
+ case -1074395700:
+ { errorText = "ERR_REQUIRES_WIN2000_OR_NEWER";break;}
+ case -1074395701:
+ { errorText = "ERR_INVALID_MATRIX_SIZE_RANGE";break;}
+ case -1074395702:
+ { errorText = "ERR_INVALID_LENGTH";break;}
+ case -1074395703:
+ { errorText = "ERR_INVALID_TYPE_OF_FLATTEN";break;}
+ case -1074395704:
+ { errorText = "ERR_INVALID_COMPRESSION_TYPE";break;}
+ case -1074395705:
+ { errorText = "ERR_DATA_CORRUPTED";break;}
+ case -1074395706:
+ { errorText = "ERR_AVI_SESSION_ALREADY_OPEN";break;}
+ case -1074395707:
+ { errorText = "ERR_AVI_WRITE_SESSION_REQUIRED";break;}
+ case -1074395708:
+ { errorText = "ERR_AVI_READ_SESSION_REQUIRED";break;}
+ case -1074395709:
+ { errorText = "ERR_AVI_UNOPENED_SESSION";break;}
+ case -1074395710:
+ { errorText = "ERR_TOO_MANY_PARTICLES";break;}
+ case -1074395711:
+ { errorText = "ERR_NOT_ENOUGH_REGIONS";break;}
+ case -1074395712:
+ { errorText = "ERR_WRONG_REGION_TYPE";break;}
+ case -1074395713:
+ { errorText = "ERR_VALUE_NOT_IN_ENUM";break;}
+ case -1074395714:
+ { errorText = "ERR_INVALID_AXIS_ORIENTATION";break;}
+ case -1074395715:
+ { errorText = "ERR_INVALID_CALIBRATION_UNIT";break;}
+ case -1074395716:
+ { errorText = "ERR_INVALID_SCALING_METHOD";break;}
+ case -1074395717:
+ { errorText = "ERR_INVALID_RANGE";break;}
+ case -1074395718:
+ { errorText = "ERR_LAB_VERSION";break;}
+ case -1074395719:
+ { errorText = "ERR_BAD_ROI_BOX";break;}
+ case -1074395720:
+ { errorText = "ERR_BAD_ROI";break;}
+ case -1074395721:
+ { errorText = "ERR_INVALID_BIT_DEPTH";break;}
+ case -1074395722:
+ { errorText = "ERR_CLASSIFIER_CLASSIFY_IMAGE_WITH_CUSTOM_SESSION";break;}
+ case -1074395723:
+ { errorText = "ERR_CUSTOMDATA_KEY_NOT_FOUND";break;}
+ case -1074395724:
+ { errorText = "ERR_CUSTOMDATA_INVALID_SIZE";break;}
+ case -1074395725:
+ { errorText = "ERR_DATA_VERSION";break;}
+ case -1074395726:
+ { errorText = "ERR_MATCHFACTOR_OBSOLETE";break;}
+ case -1074395727:
+ { errorText = "ERR_UNSUPPORTED_2D_BARCODE_SEARCH_MODE";break;}
+ case -1074395728:
+ { errorText = "ERR_INVALID_2D_BARCODE_SEARCH_MODE";break;}
+ case -1074395754:
+ { errorText = "ERR_TRIG_TIMEOUT";break;}
+ case -1074395756:
+ { errorText = "ERR_DLL_FUNCTION_NOT_FOUND";break;}
+ case -1074395757:
+ { errorText = "ERR_DLL_NOT_FOUND";break;}
+ case -1074395758:
+ { errorText = "ERR_BOARD_NOT_OPEN";break;}
+ case -1074395760:
+ { errorText = "ERR_BOARD_NOT_FOUND";break;}
+ case -1074395762:
+ { errorText = "ERR_INVALID_NIBLACK_DEVIATION_FACTOR";break;}
+ case -1074395763:
+ { errorText = "ERR_INVALID_NORMALIZATION_METHOD";break;}
+ case -1074395766:
+ { errorText = "ERR_DEPRECATED_FUNCTION";break;}
+ case -1074395767:
+ { errorText = "ERR_INVALID_ALIGNMENT";break;}
+ case -1074395768:
+ { errorText = "ERR_INVALID_SCALE";break;}
+ case -1074395769:
+ { errorText = "ERR_INVALID_EDGE_THICKNESS";break;}
+ case -1074395770:
+ { errorText = "ERR_INVALID_INSPECTION_TEMPLATE";break;}
+ case -1074395771:
+ { errorText = "ERR_OPENING_NEWER_INSPECTION_TEMPLATE";break;}
+ case -1074395772:
+ { errorText = "ERR_INVALID_REGISTRATION_METHOD";break;}
+ case -1074395773:
+ { errorText = "ERR_NO_DEST_IMAGE";break;}
+ case -1074395774:
+ { errorText = "ERR_NO_LABEL";break;}
+ case -1074395775:
+ { errorText = "ERR_ROI_HAS_OPEN_CONTOURS";break;}
+ case -1074395776:
+ { errorText = "ERR_INVALID_USE_OF_COMPACT_SESSION_FILE";break;}
+ case -1074395777:
+ { errorText = "ERR_INCOMPATIBLE_CLASSIFIER_TYPES";break;}
+ case -1074395778:
+ { errorText = "ERR_INVALID_KERNEL_SIZE";break;}
+ case -1074395779:
+ { errorText = "ERR_CANNOT_COMPACT_UNTRAINED";break;}
+ case -1074395780:
+ { errorText = "ERR_INVALID_PARTICLE_TYPE";break;}
+ case -1074395781:
+ { errorText = "ERR_CLASSIFIER_INVALID_ENGINE_TYPE";break;}
+ case -1074395782:
+ { errorText = "ERR_DESCRIPTION_TOO_LONG";break;}
+ case -1074395783:
+ { errorText = "ERR_BAD_SAMPLE_INDEX";break;}
+ case -1074395784:
+ { errorText = "ERR_INVALID_LIMITS";break;}
+ case -1074395785:
+ { errorText = "ERR_NO_PARTICLE";break;}
+ case -1074395786:
+ { errorText = "ERR_INVALID_PARTICLE_OPTIONS";break;}
+ case -1074395787:
+ { errorText = "ERR_INVALID_CLASSIFIER_TYPE";break;}
+ case -1074395788:
+ { errorText = "ERR_NO_SAMPLES";break;}
+ case -1074395789:
+ { errorText = "ERR_OPENING_NEWER_CLASSIFIER_SESSION";break;}
+ case -1074395790:
+ { errorText = "ERR_INVALID_DISTANCE_METRIC";break;}
+ case -1074395791:
+ { errorText = "ERR_CLASSIFIER_INVALID_SESSION_TYPE";break;}
+ case -1074395792:
+ { errorText = "ERR_CLASSIFIER_SESSION_NOT_TRAINED";break;}
+ case -1074395793:
+ { errorText = "ERR_INVALID_OPERATION_ON_COMPACT_SESSION_ATTEMPTED";break;}
+ case -1074395794:
+ { errorText = "ERR_K_TOO_HIGH";break;}
+ case -1074395795:
+ { errorText = "ERR_K_TOO_LOW";break;}
+ case -1074395796:
+ { errorText = "ERR_INVALID_KNN_METHOD";break;}
+ case -1074395797:
+ { errorText = "ERR_INVALID_CLASSIFIER_SESSION";break;}
+ case -1074395798:
+ { errorText = "ERR_INVALID_CUSTOM_SAMPLE";break;}
+ case -1074395799:
+ { errorText = "ERR_INTERNAL";break;}
+ case -1074395800:
+ { errorText = "ERR_PROTECTION";break;}
+ case -1074395801:
+ { errorText = "ERR_TOO_MANY_CONTOURS";break;}
+ case -1074395837:
+ { errorText = "ERR_INVALID_COMPRESSION_RATIO";break;}
+ case -1074395840:
+ { errorText = "ERR_BAD_INDEX";break;}
+ case -1074395875:
+ { errorText = "ERR_BARCODE_PHARMACODE";break;}
+ case -1074395876:
+ { errorText = "ERR_UNSUPPORTED_COLOR_MODE";break;}
+ case -1074395877:
+ { errorText = "ERR_COLORMODE_REQUIRES_CHANGECOLORSPACE2";break;}
+ case -1074395878:
+ { errorText = "ERR_PROP_NODE_WRITE_NOT_SUPPORTED";break;}
+ case -1074395879:
+ { errorText = "ERR_BAD_MEASURE";break;}
+ case -1074395880:
+ { errorText = "ERR_PARTICLE";break;}
+ case -1074395920:
+ { errorText = "ERR_NUMBER_CLASS";break;}
+ case -1074395953:
+ { errorText = "ERR_INVALID_WAVELET_TRANSFORM_MODE";break;}
+ case -1074395954:
+ { errorText = "ERR_INVALID_QUANTIZATION_STEP_SIZE";break;}
+ case -1074395955:
+ { errorText = "ERR_INVALID_MAX_WAVELET_TRANSFORM_LEVEL";break;}
+ case -1074395956:
+ { errorText = "ERR_INVALID_QUALITY";break;}
+ case -1074395957:
+ { errorText = "ERR_ARRAY_SIZE_MISMATCH";break;}
+ case -1074395958:
+ { errorText = "ERR_WINDOW_ID";break;}
+ case -1074395959:
+ { errorText = "ERR_CREATE_WINDOW";break;}
+ case -1074395960:
+ { errorText = "ERR_INIT";break;}
+ case -1074395971:
+ { errorText = "ERR_INVALID_OFFSET";break;}
+ case -1074395972:
+ { errorText = "ERR_DIRECTX_ENUMERATE_FILTERS";break;}
+ case -1074395973:
+ { errorText = "ERR_JPEG2000_UNSUPPORTED_MULTIPLE_LAYERS";break;}
+ case -1074395974:
+ { errorText = "ERR_UNSUPPORTED_JPEG2000_COLORSPACE_METHOD";break;}
+ case -1074395975:
+ { errorText = "ERR_AVI_TIMEOUT";break;}
+ case -1074395976:
+ { errorText = "ERR_NUMBER_OF_PALETTE_COLORS";break;}
+ case -1074395977:
+ { errorText = "ERR_AVI_VERSION";break;}
+ case -1074395978:
+ { errorText = "ERR_INVALID_PARTICLE_NUMBER";break;}
+ case -1074395979:
+ { errorText = "ERR_INVALID_PARTICLE_INFO";break;}
+ case -1074395980:
+ { errorText = "ERR_COM_INITIALIZE";break;}
+ case -1074395981:
+ { errorText = "ERR_INSUFFICIENT_BUFFER_SIZE";break;}
+ case -1074395982:
+ { errorText = "ERR_INVALID_FRAMES_PER_SECOND";break;}
+ case -1074395983:
+ { errorText = "ERR_FILE_NO_SPACE";break;}
+ case -1074395984:
+ { errorText = "ERR_FILE_INVALID_DATA_TYPE";break;}
+ case -1074395985:
+ { errorText = "ERR_FILE_OPERATION";break;}
+ case -1074395986:
+ { errorText = "ERR_FILE_FORMAT";break;}
+ case -1074395987:
+ { errorText = "ERR_FILE_EOF";break;}
+ case -1074395988:
+ { errorText = "ERR_FILE_WRITE";break;}
+ case -1074395989:
+ { errorText = "ERR_FILE_READ";break;}
+ case -1074395990:
+ { errorText = "ERR_FILE_GET_INFO";break;}
+ case -1074395991:
+ { errorText = "ERR_FILE_INVALID_TYPE";break;}
+ case -1074395992:
+ { errorText = "ERR_FILE_PERMISSION";break;}
+ case -1074395993:
+ { errorText = "ERR_FILE_IO_ERR";break;}
+ case -1074395994:
+ { errorText = "ERR_FILE_TOO_MANY_OPEN";break;}
+ case -1074395995:
+ { errorText = "ERR_FILE_NOT_FOUND";break;}
+ case -1074395996:
+ { errorText = "ERR_FILE_OPEN";break;}
+ case -1074395997:
+ { errorText = "ERR_FILE_ARGERR";break;}
+ case -1074395998:
+ { errorText = "ERR_FILE_COLOR_TABLE";break;}
+ case -1074395999:
+ { errorText = "ERR_FILE_FILE_TYPE";break;}
+ case -1074396000:
+ { errorText = "ERR_FILE_FILE_HEADER";break;}
+ case -1074396001:
+ { errorText = "ERR_TOO_MANY_AVI_SESSIONS";break;}
+ case -1074396002:
+ { errorText = "ERR_INVALID_LINEGAUGEMETHOD";break;}
+ case -1074396003:
+ { errorText = "ERR_AVI_DATA_EXCEEDS_BUFFER_SIZE";break;}
+ case -1074396004:
+ { errorText = "ERR_DIRECTX_CERTIFICATION_FAILURE";break;}
+ case -1074396005:
+ { errorText = "ERR_INVALID_AVI_SESSION";break;}
+ case -1074396006:
+ { errorText = "ERR_DIRECTX_UNKNOWN_COMPRESSION_FILTER";break;}
+ case -1074396007:
+ { errorText = "ERR_DIRECTX_INCOMPATIBLE_COMPRESSION_FILTER";break;}
+ case -1074396008:
+ { errorText = "ERR_DIRECTX_NO_FILTER";break;}
+ case -1074396009:
+ { errorText = "ERR_DIRECTX";break;}
+ case -1074396010:
+ { errorText = "ERR_INVALID_FRAME_NUMBER";break;}
+ case -1074396011:
+ { errorText = "ERR_RPC_BIND";break;}
+ case -1074396012:
+ { errorText = "ERR_RPC_EXECUTE";break;}
+ case -1074396013:
+ { errorText = "ERR_INVALID_VIDEO_MODE";break;}
+ case -1074396014:
+ { errorText = "ERR_INVALID_VIDEO_BLIT";break;}
+ case -1074396015:
+ { errorText = "ERR_RPC_EXECUTE_IVB";break;}
+ case -1074396016:
+ { errorText = "ERR_NO_VIDEO_DRIVER";break;}
+ case -1074396017:
+ { errorText = "ERR_OPENING_NEWER_AIM_GRADING_DATA";break;}
+ case -1074396018:
+ { errorText = "ERR_INVALID_EDGE_POLARITY_SEARCH_MODE";break;}
+ case -1074396019:
+ { errorText = "ERR_INVALID_THRESHOLD_PERCENTAGE";break;}
+ case -1074396020:
+ { errorText = "ERR_INVALID_GRADING_MODE";break;}
+ case -1074396021:
+ { errorText = "ERR_INVALID_KERNEL_SIZE_FOR_EDGE_DETECTION";break;}
+ case -1074396022:
+ { errorText = "ERR_INVALID_SEARCH_MODE_FOR_STRAIGHT_EDGE";break;}
+ case -1074396023:
+ { errorText = "ERR_INVALID_ANGLE_TOL_FOR_STRAIGHT_EDGE";break;}
+ case -1074396024:
+ { errorText = "ERR_INVALID_MIN_COVERAGE_FOR_STRAIGHT_EDGE";break;}
+ case -1074396025:
+ { errorText = "ERR_INVALID_ANGLE_RANGE_FOR_STRAIGHT_EDGE";break;}
+ case -1074396026:
+ { errorText = "ERR_INVALID_PROCESS_TYPE_FOR_EDGE_DETECTION";break;}
+ case -1074396032:
+ { errorText = "ERR_TEMPLATEDESCRIPTOR_ROTATION_SEARCHSTRATEGY";break;}
+ case -1074396033:
+ { errorText = "ERR_TEMPLATEDESCRIPTOR_LEARNSETUPDATA";break;}
+ case -1074396034:
+ { errorText = "ERR_TEMPLATEIMAGE_EDGEINFO";break;}
+ case -1074396035:
+ { errorText = "ERR_TEMPLATEIMAGE_NOCIRCLE";break;}
+ case -1074396036:
+ { errorText = "ERR_INVALID_SKELETONMODE";break;}
+ case -1074396037:
+ { errorText = "ERR_TIMEOUT";break;}
+ case -1074396038:
+ { errorText = "ERR_FIND_COORDSYS_MORE_THAN_ONE_EDGE";break;}
+ case -1074396039:
+ { errorText = "ERR_IO_ERROR";break;}
+ case -1074396040:
+ { errorText = "ERR_DRIVER";break;}
+ case -1074396041:
+ { errorText = "ERR_INVALID_2D_BARCODE_TYPE";break;}
+ case -1074396042:
+ { errorText = "ERR_INVALID_2D_BARCODE_CONTRAST";break;}
+ case -1074396043:
+ { errorText = "ERR_INVALID_2D_BARCODE_CELL_SHAPE";break;}
+ case -1074396044:
+ { errorText = "ERR_INVALID_2D_BARCODE_SHAPE";break;}
+ case -1074396045:
+ { errorText = "ERR_INVALID_2D_BARCODE_SUBTYPE";break;}
+ case -1074396046:
+ { errorText = "ERR_INVALID_2D_BARCODE_CONTRAST_FOR_ROI";break;}
+ case -1074396047:
+ { errorText = "ERR_INVALID_LINEAR_AVERAGE_MODE";break;}
+ case -1074396048:
+ { errorText = "ERR_INVALID_CELL_SAMPLE_SIZE";break;}
+ case -1074396049:
+ { errorText = "ERR_INVALID_MATRIX_POLARITY";break;}
+ case -1074396050:
+ { errorText = "ERR_INVALID_ECC_TYPE";break;}
+ case -1074396051:
+ { errorText = "ERR_INVALID_CELL_FILTER_MODE";break;}
+ case -1074396052:
+ { errorText = "ERR_INVALID_DEMODULATION_MODE";break;}
+ case -1074396053:
+ { errorText = "ERR_INVALID_BORDER_INTEGRITY";break;}
+ case -1074396054:
+ { errorText = "ERR_INVALID_CELL_FILL_TYPE";break;}
+ case -1074396055:
+ { errorText = "ERR_INVALID_ASPECT_RATIO";break;}
+ case -1074396056:
+ { errorText = "ERR_INVALID_MATRIX_MIRROR_MODE";break;}
+ case -1074396057:
+ { errorText = "ERR_INVALID_SEARCH_VECTOR_WIDTH";break;}
+ case -1074396058:
+ { errorText = "ERR_INVALID_ROTATION_MODE";break;}
+ case -1074396059:
+ { errorText = "ERR_INVALID_MAX_ITERATIONS";break;}
+ case -1074396060:
+ { errorText = "ERR_JPEG2000_LOSSLESS_WITH_FLOATING_POINT";break;}
+ case -1074396061:
+ { errorText = "ERR_INVALID_WINDOW_SIZE";break;}
+ case -1074396062:
+ { errorText = "ERR_INVALID_TOLERANCE";break;}
+ case -1074396063:
+ { errorText = "ERR_EXTERNAL_ALIGNMENT";break;}
+ case -1074396064:
+ { errorText = "ERR_EXTERNAL_NOT_SUPPORTED";break;}
+ case -1074396065:
+ { errorText = "ERR_CANT_RESIZE_EXTERNAL";break;}
+ case -1074396066:
+ { errorText = "ERR_INVALID_POINTSYMBOL";break;}
+ case -1074396067:
+ { errorText = "ERR_IMAGES_NOT_DIFF";break;}
+ case -1074396068:
+ { errorText = "ERR_INVALID_ACTION";break;}
+ case -1074396069:
+ { errorText = "ERR_INVALID_COLOR_MODE";break;}
+ case -1074396070:
+ { errorText = "ERR_INVALID_FUNCTION";break;}
+ case -1074396071:
+ { errorText = "ERR_INVALID_SCAN_DIRECTION";break;}
+ case -1074396072:
+ { errorText = "ERR_INVALID_BORDER";break;}
+ case -1074396073:
+ { errorText = "ERR_MASK_OUTSIDE_IMAGE";break;}
+ case -1074396074:
+ { errorText = "ERR_INCOMP_SIZE";break;}
+ case -1074396075:
+ { errorText = "ERR_COORD_SYS_SECOND_AXIS";break;}
+ case -1074396076:
+ { errorText = "ERR_COORD_SYS_FIRST_AXIS";break;}
+ case -1074396077:
+ { errorText = "ERR_INCOMP_TYPE";break;}
+ case -1074396079:
+ { errorText = "ERR_INVALID_METAFILE_HANDLE";break;}
+ case -1074396080:
+ { errorText = "ERR_INVALID_IMAGE_TYPE";break;}
+ case -1074396081:
+ { errorText = "ERR_BAD_PASSWORD";break;}
+ case -1074396082:
+ { errorText = "ERR_PALETTE_NOT_SUPPORTED";break;}
+ case -1074396083:
+ { errorText = "ERR_ROLLBACK_TIMEOUT";break;}
+ case -1074396084:
+ { errorText = "ERR_ROLLBACK_DELETE_TIMER";break;}
+ case -1074396085:
+ { errorText = "ERR_ROLLBACK_INIT_TIMER";break;}
+ case -1074396086:
+ { errorText = "ERR_ROLLBACK_START_TIMER";break;}
+ case -1074396087:
+ { errorText = "ERR_ROLLBACK_STOP_TIMER";break;}
+ case -1074396088:
+ { errorText = "ERR_ROLLBACK_RESIZE";break;}
+ case -1074396089:
+ { errorText = "ERR_ROLLBACK_RESOURCE_REINITIALIZE";break;}
+ case -1074396090:
+ { errorText = "ERR_ROLLBACK_RESOURCE_ENABLED";break;}
+ case -1074396091:
+ { errorText = "ERR_ROLLBACK_RESOURCE_UNINITIALIZED_ENABLE";break;}
+ case -1074396092:
+ { errorText = "ERR_ROLLBACK_RESOURCE_NON_EMPTY_INITIALIZE";break;}
+ case -1074396093:
+ { errorText = "ERR_ROLLBACK_RESOURCE_LOCKED";break;}
+ case -1074396094:
+ { errorText = "ERR_ROLLBACK_RESOURCE_CANNOT_UNLOCK";break;}
+ case -1074396095:
+ { errorText = "ERR_CALIBRATION_DUPLICATE_REFERENCE_POINT";break;}
+ case -1074396096:
+ { errorText = "ERR_NOT_AN_OBJECT";break;}
+ case -1074396097:
+ { errorText = "ERR_INVALID_PARTICLE_PARAMETER_VALUE";break;}
+ case -1074396098:
+ { errorText = "ERR_RESERVED_MUST_BE_NULL";break;}
+ case -1074396099:
+ { errorText = "ERR_CALIBRATION_INFO_SIMPLE_TRANSFORM";break;}
+ case -1074396100:
+ { errorText = "ERR_CALIBRATION_INFO_PERSPECTIVE_PROJECTION";break;}
+ case -1074396101:
+ { errorText = "ERR_CALIBRATION_INFO_MICRO_PLANE";break;}
+ case -1074396102:
+ { errorText = "ERR_CALIBRATION_INFO_6";break;}
+ case -1074396103:
+ { errorText = "ERR_CALIBRATION_INFO_5";break;}
+ case -1074396104:
+ { errorText = "ERR_CALIBRATION_INFO_4";break;}
+ case -1074396105:
+ { errorText = "ERR_CALIBRATION_INFO_3";break;}
+ case -1074396106:
+ { errorText = "ERR_CALIBRATION_INFO_2";break;}
+ case -1074396107:
+ { errorText = "ERR_CALIBRATION_INFO_1";break;}
+ case -1074396108:
+ { errorText = "ERR_CALIBRATION_ERRORMAP";break;}
+ case -1074396109:
+ { errorText = "ERR_CALIBRATION_INVALID_SCALING_FACTOR";break;}
+ case -1074396110:
+ { errorText = "ERR_CALIBRATION_INFO_VERSION";break;}
+ case -1074396111:
+ { errorText = "ERR_CALIBRATION_FAILED_TO_FIND_GRID";break;}
+ case -1074396112:
+ { errorText = "ERR_INCOMP_MATRIX_SIZE";break;}
+ case -1074396113:
+ { errorText = "ERR_CALIBRATION_IMAGE_UNCALIBRATED";break;}
+ case -1074396114:
+ { errorText = "ERR_CALIBRATION_INVALID_ROI";break;}
+ case -1074396115:
+ { errorText = "ERR_CALIBRATION_IMAGE_CORRECTED";break;}
+ case -1074396116:
+ { errorText = "ERR_CALIBRATION_INSF_POINTS";break;}
+ case -1074396117:
+ { errorText = "ERR_MATRIX_SIZE";break;}
+ case -1074396118:
+ { errorText = "ERR_INVALID_STEP_SIZE";break;}
+ case -1074396119:
+ { errorText = "ERR_CUSTOMDATA_INVALID_KEY";break;}
+ case -1074396120:
+ { errorText = "ERR_NOT_IMAGE";break;}
+ case -1074396121:
+ { errorText = "ERR_SATURATION_THRESHOLD_OUT_OF_RANGE";break;}
+ case -1074396122:
+ { errorText = "ERR_DRAWTEXT_COLOR_MUST_BE_GRAYSCALE";break;}
+ case -1074396123:
+ { errorText = "ERR_INVALID_CALIBRATION_MODE";break;}
+ case -1074396124:
+ { errorText = "ERR_INVALID_CALIBRATION_ROI_MODE";break;}
+ case -1074396125:
+ { errorText = "ERR_INVALID_CONTRAST_THRESHOLD";break;}
+ case -1074396126:
+ { errorText = "ERR_ROLLBACK_RESOURCE_CONFLICT_1";break;}
+ case -1074396127:
+ { errorText = "ERR_ROLLBACK_RESOURCE_CONFLICT_2";break;}
+ case -1074396128:
+ { errorText = "ERR_ROLLBACK_RESOURCE_CONFLICT_3";break;}
+ case -1074396129:
+ { errorText = "ERR_ROLLBACK_UNBOUNDED_INTERFACE";break;}
+ case -1074396130:
+ { errorText = "ERR_NOT_RECT_OR_ROTATED_RECT";break;}
+ case -1074396132:
+ { errorText = "ERR_MASK_NOT_TEMPLATE_SIZE";break;}
+ case -1074396133:
+ { errorText = "ERR_THREAD_COULD_NOT_INITIALIZE";break;}
+ case -1074396134:
+ { errorText = "ERR_THREAD_INITIALIZING";break;}
+ case -1074396135:
+ { errorText = "ERR_INVALID_BUTTON_LABEL";break;}
+ case -1074396136:
+ { errorText = "ERR_DIRECTX_INVALID_FILTER_QUALITY";break;}
+ case -1074396137:
+ { errorText = "ERR_DIRECTX_DLL_NOT_FOUND";break;}
+ case -1074396138:
+ { errorText = "ERR_ROLLBACK_NOT_SUPPORTED";break;}
+ case -1074396139:
+ { errorText = "ERR_ROLLBACK_RESOURCE_OUT_OF_MEMORY";break;}
+ case -1074396140:
+ { errorText = "ERR_BARCODE_CODE128_SET";break;}
+ case -1074396141:
+ { errorText = "ERR_BARCODE_CODE128_FNC";break;}
+ case -1074396142:
+ { errorText = "ERR_BARCODE_INVALID";break;}
+ case -1074396143:
+ { errorText = "ERR_BARCODE_TYPE";break;}
+ case -1074396144:
+ { errorText = "ERR_BARCODE_CODE93_SHIFT";break;}
+ case -1074396145:
+ { errorText = "ERR_BARCODE_UPCA";break;}
+ case -1074396146:
+ { errorText = "ERR_BARCODE_MSI";break;}
+ case -1074396147:
+ { errorText = "ERR_BARCODE_I25";break;}
+ case -1074396148:
+ { errorText = "ERR_BARCODE_EAN13";break;}
+ case -1074396149:
+ { errorText = "ERR_BARCODE_EAN8";break;}
+ case -1074396150:
+ { errorText = "ERR_BARCODE_CODE128";break;}
+ case -1074396151:
+ { errorText = "ERR_BARCODE_CODE93";break;}
+ case -1074396152:
+ { errorText = "ERR_BARCODE_CODE39";break;}
+ case -1074396153:
+ { errorText = "ERR_BARCODE_CODABAR";break;}
+ case -1074396154:
+ { errorText = "ERR_IMAGE_TOO_SMALL";break;}
+ case -1074396155:
+ { errorText = "ERR_UNINIT";break;}
+ case -1074396156:
+ { errorText = "ERR_NEED_FULL_VERSION";break;}
+ case -1074396157:
+ { errorText = "ERR_UNREGISTERED";break;}
+ case -1074396158:
+ { errorText = "ERR_MEMORY_ERROR";break;}
+ case -1074396159:
+ { errorText = "ERR_OUT_OF_MEMORY";break;}
+ case -1074396160:
+ { errorText = "ERR_SYSTEM_ERROR";break;}
+ case 0:
+ { errorText = "ERR_SUCCESS";break;}
+ // end National Instruments defined errors
+
+ // begin BAE defined errors
+ case ERR_VISION_GENERAL_ERROR:
+ { errorText = "ERR_VISION_GENERAL_ERROR";break;}
+ case ERR_COLOR_NOT_FOUND:
+ { errorText = "ERR_COLOR_NOT_FOUND";break;}
+ case ERR_PARTICLE_TOO_SMALL:
+ { errorText = "ERR_PARTICLE_TOO_SMALL";break;}
+ case ERR_CAMERA_FAILURE:
+ { errorText = "ERR_CAMERA_FAILURE";break;}
+ case ERR_CAMERA_SOCKET_CREATE_FAILED:
+ { errorText = "ERR_CAMERA_SOCKET_CREATE_FAILED";break;}
+ case ERR_CAMERA_CONNECT_FAILED:
+ { errorText = "ERR_CAMERA_CONNECT_FAILED";break;}
+ case ERR_CAMERA_STALE_IMAGE:
+ { errorText = "ERR_CAMERA_STALE_IMAGE";break;}
+ case ERR_CAMERA_NOT_INITIALIZED:
+ { errorText = "ERR_CAMERA_NOT_INITIALIZED";break;}
+ case ERR_CAMERA_NO_BUFFER_AVAILABLE:
+ { errorText = "ERR_CAMERA_NO_BUFFER_AVAILABLE";break;}
+ case ERR_CAMERA_HEADER_ERROR:
+ { errorText = "ERR_CAMERA_HEADER_ERROR";break;}
+ case ERR_CAMERA_BLOCKING_TIMEOUT:
+ { errorText = "ERR_CAMERA_BLOCKING_TIMEOUT";break;}
+ case ERR_CAMERA_AUTHORIZATION_FAILED:
+ { errorText = "ERR_CAMERA_AUTHORIZATION_FAILED";break;}
+ case ERR_CAMERA_TASK_SPAWN_FAILED:
+ { errorText = "ERR_CAMERA_TASK_SPAWN_FAILED";break;}
+ case ERR_CAMERA_TASK_INPUT_OUT_OF_RANGE:
+ { errorText = "ERR_CAMERA_TASK_INPUT_OUT_OF_RANGE";break;}
+ case ERR_CAMERA_COMMAND_FAILURE:
+ { errorText = "ERR_CAMERA_COMMAND_FAILURE";break;}
+ }
+
+ return errorText;
+}
+
+
+
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/Vision/HSLImage.cpp b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/Vision/HSLImage.cpp
new file mode 100644
index 0000000..1a66949
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/Vision/HSLImage.cpp
@@ -0,0 +1,28 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2014. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+
+#include "Vision/HSLImage.h"
+
+/**
+ * Create a new image that uses the Hue, Saturation, and Luminance planes.
+ */
+HSLImage::HSLImage() : ColorImage(IMAQ_IMAGE_HSL)
+{
+}
+
+/**
+ * Create a new image by loading a file.
+ * @param fileName The path of the file to load.
+ */
+HSLImage::HSLImage(const char *fileName) : ColorImage(IMAQ_IMAGE_HSL)
+{
+ int success = imaqReadFile(m_imaqImage, fileName, NULL, NULL);
+ wpi_setImaqErrorWithContext(success, "Imaq ReadFile error");
+}
+
+HSLImage::~HSLImage()
+{
+}
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/Vision/ImageBase.cpp b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/Vision/ImageBase.cpp
new file mode 100644
index 0000000..ba549b6
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/Vision/ImageBase.cpp
@@ -0,0 +1,73 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2014. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+
+#include "Vision/ImageBase.h"
+#include "nivision.h"
+
+/**
+ * Create a new instance of an ImageBase.
+ * Imagebase is the base of all the other image classes. The constructor
+ * creates any type of image and stores the pointer to it in the class.
+ * @param type The type of image to create
+ */
+ImageBase::ImageBase(ImageType type)
+{
+ m_imaqImage = imaqCreateImage(type, DEFAULT_BORDER_SIZE);
+}
+
+/**
+ * Frees memory associated with an ImageBase.
+ * Destructor frees the imaq image allocated with the class.
+ */
+ImageBase::~ImageBase()
+{
+ if(m_imaqImage)
+ imaqDispose(m_imaqImage);
+}
+
+/**
+ * Writes an image to a file with the given filename.
+ * Write the image to a file in the flash on the cRIO.
+ * @param fileName The name of the file to write
+ */
+void ImageBase::Write(const char *fileName)
+{
+ int success = imaqWriteFile(m_imaqImage, fileName, NULL);
+ wpi_setImaqErrorWithContext(success, "Imaq Image writeFile error");
+}
+
+/**
+ * Gets the height of an image.
+ * @return The height of the image in pixels.
+ */
+int ImageBase::GetHeight()
+{
+ int height;
+ imaqGetImageSize(m_imaqImage, NULL, &height);
+ return height;
+}
+
+/**
+ * Gets the width of an image.
+ * @return The width of the image in pixels.
+ */
+int ImageBase::GetWidth()
+{
+ int width;
+ imaqGetImageSize(m_imaqImage, &width, NULL);
+ return width;
+}
+
+/**
+ * Access the internal IMAQ Image data structure.
+ *
+ * @return A pointer to the internal IMAQ Image data structure.
+ */
+Image *ImageBase::GetImaqImage()
+{
+ return m_imaqImage;
+}
+
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/Vision/MonoImage.cpp b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/Vision/MonoImage.cpp
new file mode 100644
index 0000000..17efe20
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/Vision/MonoImage.cpp
@@ -0,0 +1,55 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2014. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+
+#include "Vision/MonoImage.h"
+#include "nivision.h"
+
+using namespace std;
+
+MonoImage::MonoImage() : ImageBase(IMAQ_IMAGE_U8)
+{
+}
+
+MonoImage::~MonoImage()
+{
+}
+
+/**
+ * Look for ellipses in an image.
+ * Given some input parameters, look for any number of ellipses in an image.
+ * @param ellipseDescriptor Ellipse descriptor
+ * @param curveOptions Curve options
+ * @param shapeDetectionOptions Shape detection options
+ * @param roi Region of Interest
+ * @returns a vector of EllipseMatch structures (0 length vector on no match)
+ */
+vector<EllipseMatch> * MonoImage::DetectEllipses(
+ EllipseDescriptor *ellipseDescriptor, CurveOptions *curveOptions,
+ ShapeDetectionOptions *shapeDetectionOptions, ROI *roi)
+{
+ int numberOfMatches;
+ EllipseMatch *e = imaqDetectEllipses(m_imaqImage, ellipseDescriptor,
+ curveOptions, shapeDetectionOptions, roi, &numberOfMatches);
+ vector<EllipseMatch> *ellipses = new vector<EllipseMatch>;
+ if (e == NULL)
+ {
+ return ellipses;
+ }
+ for (int i = 0; i < numberOfMatches; i++)
+ {
+ ellipses->push_back(e[i]);
+ }
+ imaqDispose(e);
+ return ellipses;
+}
+
+vector<EllipseMatch> * MonoImage::DetectEllipses(
+ EllipseDescriptor *ellipseDescriptor)
+{
+ vector<EllipseMatch> *ellipses = DetectEllipses(ellipseDescriptor, NULL,
+ NULL, NULL);
+ return ellipses;
+}
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/Vision/RGBImage.cpp b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/Vision/RGBImage.cpp
new file mode 100644
index 0000000..a34212e
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/Vision/RGBImage.cpp
@@ -0,0 +1,28 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2014. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+
+#include "Vision/RGBImage.h"
+
+/**
+ * Create a new image that uses Red, Green, and Blue planes.
+ */
+RGBImage::RGBImage() : ColorImage(IMAQ_IMAGE_RGB)
+{
+}
+
+/**
+ * Create a new image by loading a file.
+ * @param fileName The path of the file to load.
+ */
+RGBImage::RGBImage(const char *fileName) : ColorImage(IMAQ_IMAGE_RGB)
+{
+ int success = imaqReadFile(m_imaqImage, fileName, NULL, NULL);
+ wpi_setImaqErrorWithContext(success, "Imaq ReadFile error");
+}
+
+RGBImage::~RGBImage()
+{
+}
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/Vision/Threshold.cpp b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/Vision/Threshold.cpp
new file mode 100644
index 0000000..1d81b38
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/Vision/Threshold.cpp
@@ -0,0 +1,18 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2014. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+
+#include "Vision/Threshold.h"
+
+Threshold::Threshold(int new_plane1Low, int new_plane1High, int new_plane2Low,
+ int new_plane2High, int new_plane3Low, int new_plane3High)
+{
+ plane1Low = new_plane1Low;
+ plane1High = new_plane1High;
+ plane2Low = new_plane2Low;
+ plane2High = new_plane2High;
+ plane3Low = new_plane3Low;
+ plane3High = new_plane3High;
+}
diff --git a/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/Vision/VisionAPI.cpp b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/Vision/VisionAPI.cpp
new file mode 100644
index 0000000..c72b321
--- /dev/null
+++ b/aos/externals/allwpilib/wpilibc/wpilibC++Devices/src/Vision/VisionAPI.cpp
@@ -0,0 +1,667 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2014. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+
+#include <stdlib.h>
+#include <stdarg.h>
+
+#include "Vision/BaeUtilities.h"
+#include "Vision/FrcError.h"
+#include "Vision/VisionAPI.h"
+
+int VisionAPI_debugFlag = 1;
+#define DPRINTF if(VisionAPI_debugFlag)dprintf
+
+/** @file
+ * Image Management functions
+ */
+
+/**
+* @brief Create an image object
+* Supports IMAQ_IMAGE_U8, IMAQ_IMAGE_I16, IMAQ_IMAGE_SGL, IMAQ_IMAGE_COMPLEX, IMAQ_IMAGE_RGB, IMAQ_IMAGE_HSL, IMAQ_IMAGE_RGB_U64
+* The border size is defaulted to 3 so that convolutional algorithms work at the edges.
+* When you are finished with the created image, dispose of it by calling frcDispose().
+* To get extended error information, call GetLastError().
+*
+* @param type Type of image to create
+* @return Image* On success, this function returns the created image. On failure, it returns NULL.
+*/
+Image* frcCreateImage(ImageType type) { return imaqCreateImage(type, DEFAULT_BORDER_SIZE); }
+
+/**
+* @brief Dispose of one object. Supports any object created on the heap.
+*
+* @param object object to dispose of
+* @return On success: 1. On failure: 0. To get extended error information, call GetLastError().
+*/
+int frcDispose(void* object) { return imaqDispose(object); }
+/**
+* @brief Dispose of a list of objects. Supports any object created on the heap.
+*
+* @param functionName The name of the function
+* @param ... A list of pointers to structures that need to be disposed of.
+* The last pointer in the list should always be set to NULL.
+*
+* @return On success: 1. On failure: 0. To get extended error information, call GetLastError().
+*/
+int frcDispose( const char* functionName, ... ) /* Variable argument list */
+{
+ va_list disposalPtrList; /* Input argument list */
+ void* disposalPtr; /* For iteration */
+ int success, returnValue = 1;
+
+ va_start( disposalPtrList, functionName ); /* start of variable list */
+ disposalPtr = va_arg( disposalPtrList, void* );
+ while( disposalPtr != NULL ) {
+ success = imaqDispose(disposalPtr);
+ if (!success) {returnValue = 0;}
+ disposalPtr = va_arg( disposalPtrList, void* );
+ }
+ return returnValue;
+}
+
+/**
+* @brief Copy an image object.
+* Supports IMAQ_IMAGE_U8, IMAQ_IMAGE_I16, IMAQ_IMAGE_SGL, IMAQ_IMAGE_RGB, IMAQ_IMAGE_HSL.
+*
+* @param dest Copy of image. On failure, dest is NULL. Must have already been created using frcCreateImage().
+* When you are finished with the created image, dispose of it by calling frcDispose().
+* @param source Image to copy
+*
+* @return On success: 1. On failure: 0. To get extended error information, call GetLastError().
+*/
+int frcCopyImage(Image* dest, const Image* source) { return imaqDuplicate(dest, source); }
+
+/**
+* @brief Crop image without changing the scale.
+* Supports IMAQ_IMAGE_U8, IMAQ_IMAGE_I16, IMAQ_IMAGE_SGL, IMAQ_IMAGE_RGB, IMAQ_IMAGE_HSL.
+*
+* @param dest Modified image
+* @param source Image to crop
+* @param rect region to process, or IMAQ_NO_RECT
+*
+* @return On success: 1. On failure: 0. To get extended error information, call GetLastError().
+*/
+int frcCrop(Image* dest, const Image* source, Rect rect)
+{
+ return imaqScale(dest, source, 1, 1, IMAQ_SCALE_LARGER, rect);
+}
+
+
+/**
+* @brief Scales the entire image larger or smaller.
+* Supports IMAQ_IMAGE_U8, IMAQ_IMAGE_I16, IMAQ_IMAGE_SGL, IMAQ_IMAGE_RGB, IMAQ_IMAGE_HSL.
+*
+* @param dest Modified image
+* @param source Image to scale
+* @param xScale the horizontal reduction ratio
+* @param yScale the vertical reduction ratio
+* @param scaleMode IMAQ_SCALE_LARGER or IMAQ_SCALE_SMALLER
+*
+* @return On success: 1. On failure: 0. To get extended error information, call GetLastError().
+*/
+int frcScale(Image* dest, const Image* source, int xScale, int yScale, ScalingMode scaleMode)
+{
+ Rect rect = IMAQ_NO_RECT;
+ return imaqScale(dest, source, xScale, yScale, scaleMode, rect);
+}
+
+/**
+ * @brief Creates image object from the information in a file. The file can be in one of the following formats:
+ * PNG, JPEG, JPEG2000, TIFF, AIPD, or BMP.
+ * Supports IMAQ_IMAGE_U8, IMAQ_IMAGE_I16, IMAQ_IMAGE_SGL, IMAQ_IMAGE_COMPLEX, IMAQ_IMAGE_RGB, IMAQ_IMAGE_HSL, IMAQ_IMAGE_RGB_U64.
+ *
+ * @param image Image read in
+ * @param fileName File to read. Cannot be NULL
+ *
+ * @return On success: 1. On failure: 0. To get extended error information, call GetLastError().
+ */
+ int frcReadImage(Image* image, const char* fileName)
+ {
+ return imaqReadFile(image, fileName, NULL, NULL);
+ }
+
+
+ /**
+ * @brief Write image to a file.
+ * Supports IMAQ_IMAGE_U8, IMAQ_IMAGE_I16, IMAQ_IMAGE_SGL, IMAQ_IMAGE_COMPLEX, IMAQ_IMAGE_RGB, IMAQ_IMAGE_HSL, IMAQ_IMAGE_RGB_U64.
+ *
+ * The file type is determined by the extension, as follows:
+ *
+ * Extension File Type
+ * aipd or .apd AIPD
+ * .bmp BMP
+ * .jpg or .jpeg JPEG
+ * .jp2 JPEG2000
+ * .png PNG
+ * .tif or .tiff TIFF
+ *
+ *
+ *The following are the supported image types for each file type:
+ *
+ * File Types Image Types
+ * AIPD all image types
+ * BMP, JPEG 8-bit, RGB
+ * PNG, TIFF, JPEG2000 8-bit, 16-bit, RGB, RGBU64
+ *
+ * @param image Image to write
+ * @param fileName File to read. Cannot be NULL. The extension determines the file format that is written.
+ *
+ * @return On success: 1. On failure: 0. To get extended error information, call GetLastError().
+ */
+int frcWriteImage(const Image* image, const char* fileName)
+{
+ RGBValue* colorTable = NULL;
+ return imaqWriteFile(image, fileName, colorTable);
+}
+
+
+/* Measure Intensity functions */
+
+/**
+* @brief Measures the pixel intensities in a rectangle of an image.
+* Outputs intensity based statistics about an image such as Max, Min, Mean and Std Dev of pixel value.
+* Supports IMAQ_IMAGE_U8, IMAQ_IMAGE_I16, IMAQ_IMAGE_SGL.
+*
+* Parameter Discussion :
+* Relevant parameters of the HistogramReport include:
+* min, max, mean and stdDev
+* min/max —Setting both min and max to 0 causes the function to set min to 0
+* and the max to 255 for 8-bit images and to the actual minimum value and
+* maximum value of the image for all other image types.
+* max—Setting both min and max to 0 causes the function to set max to 255
+* for 8-bit images and to the actual maximum value of the image for
+* all other image types.
+*
+* @param image Image whose histogram the function calculates.
+* @param numClasses The number of classes into which the function separates the pixels.
+* Determines the number of elements in the histogram array returned
+* @param min The minimum pixel value to consider for the histogram.
+* The function does not count pixels with values less than min.
+* @param max The maximum pixel value to consider for the histogram.
+* The function does not count pixels with values greater than max.
+* @param rect Region of interest in the image. If not included, the entire image is used.
+* @return On success, this function returns a report describing the pixel value classification.
+* When you are finished with the report, dispose of it by calling frcDispose().
+* On failure, this function returns NULL. To get extended error information, call GetLastError().
+*
+*/
+HistogramReport* frcHistogram(const Image* image, int numClasses, float min, float max)
+{
+ Rect rect = IMAQ_NO_RECT;
+ return frcHistogram(image, numClasses, min, max, rect);
+}
+HistogramReport* frcHistogram(const Image* image, int numClasses, float min, float max, Rect rect)
+{
+ int success;
+ int fillValue = 1;
+
+ /* create the region of interest */
+ ROI* pRoi = imaqCreateROI();
+ success = imaqAddRectContour(pRoi, rect);
+ if ( !success ) { GetLastVisionError(); return NULL; }
+
+ /* make a mask from the ROI */
+ Image* pMask = frcCreateImage(IMAQ_IMAGE_U8);
+ success = imaqROIToMask(pMask, pRoi, fillValue, NULL, NULL);
+ if ( !success ) {
+ GetLastVisionError();
+ frcDispose(__FUNCTION__, pRoi, NULL);
+ return NULL;
+ }
+
+ /* get a histogram report */
+ HistogramReport* pHr = NULL;
+ pHr = imaqHistogram(image, numClasses, min, max, pMask);
+
+ /* clean up */
+ frcDispose(__FUNCTION__, pRoi, pMask, NULL);
+
+ return pHr;
+}
+
+/**
+* @brief Calculates the histogram, or pixel distribution, of a color image.
+* Supports IMAQ_IMAGE_RGB, IMAQ_IMAGE_HSL.
+*
+* @param image Image whose histogram the function calculates.
+* @param numClasses The number of classes into which the function separates the pixels.
+* Determines the number of elements in the histogram array returned
+* @param mode The color space in which to perform the histogram. Possible values include IMAQ_RGB and IMAQ_HSL.
+* @param mask An optional mask image. This image must be an IMAQ_IMAGE_U8 image.
+* The function calculates the histogram using only those pixels in the image whose
+* corresponding pixels in the mask are non-zero. Set this parameter to NULL to calculate
+* the histogram of the entire image, or use the simplified call.
+*
+* @return On success, this function returns a report describing the classification
+* of each plane in a HistogramReport.
+* When you are finished with the report, dispose of it by calling frcDispose().
+* On failure, this function returns NULL.
+* To get extended error information, call imaqGetLastError().
+*/
+ColorHistogramReport* frcColorHistogram(const Image* image, int numClasses, ColorMode mode)
+{
+ return frcColorHistogram(image, numClasses, mode, NULL);
+}
+
+ColorHistogramReport* frcColorHistogram(const Image* image, int numClasses, ColorMode mode, Image* mask)
+{
+ return imaqColorHistogram2((Image*)image, numClasses, mode, NULL, mask);
+}
+
+
+/**
+* @brief Measures the pixel intensities in a rectangle of an image.
+* Outputs intensity based statistics about an image such as Max, Min, Mean and Std Dev of pixel value.
+* Supports IMAQ_IMAGE_U8 (grayscale) IMAQ_IMAGE_RGB (color) IMAQ_IMAGE_HSL (color-HSL).
+*
+* @param image The image whose pixel value the function queries
+* @param pixel The coordinates of the pixel that the function queries
+* @param value On return, the value of the specified image pixel. This parameter cannot be NULL.
+* This data structure contains either grayscale, RGB, HSL, Complex or RGBU64Value depending on the type of image.
+* @return On success: 1. On failure: 0. To get extended error information, call GetLastError().
+*/
+int frcGetPixelValue(const Image* image, Point pixel, PixelValue* value)
+{
+ return imaqGetPixel(image, pixel, value);
+}
+
+
+/* Particle Analysis functions */
+
+/**
+* @brief Filters particles out of an image based on their measurements.
+* Supports IMAQ_IMAGE_U8, IMAQ_IMAGE_I16, IMAQ_IMAGE_SGL.
+*
+* @param dest The destination image. If dest is used, it must be the same size as the Source image. It will contain only the filtered particles.
+* @param source The image containing the particles to filter.
+* @param criteria An array of criteria to apply to the particles in the source image. This array cannot be NULL.
+* See the NIVisionCVI.chm help file for definitions of criteria.
+* @param criteriaCount The number of elements in the criteria array.
+* @param options Binary filter options, including rejectMatches, rejectBorder, and connectivity8.
+* @param rect Area of image to filter. If omitted, the default is entire image.
+* @param numParticles On return, the number of particles left in the image
+* @return On success: 1. On failure: 0. To get extended error information, call GetLastError().
+*/
+int frcParticleFilter(Image* dest, Image* source, const ParticleFilterCriteria2* criteria,
+ int criteriaCount, const ParticleFilterOptions* options, int* numParticles)
+{
+ Rect rect = IMAQ_NO_RECT;
+ return frcParticleFilter(dest, source, criteria, criteriaCount, options, rect, numParticles);
+}
+
+int frcParticleFilter(Image* dest, Image* source, const ParticleFilterCriteria2* criteria,
+ int criteriaCount, const ParticleFilterOptions* options, Rect rect, int* numParticles)
+{
+ ROI* roi = imaqCreateROI();
+ imaqAddRectContour(roi, rect);
+ return imaqParticleFilter3(dest, source, criteria, criteriaCount, options, roi, numParticles);
+}
+
+
+/**
+* @brief Performs morphological transformations on binary images.
+* Supports IMAQ_IMAGE_U8.
+*
+* @param dest The destination image. The border size of the destination image is not important.
+* @param source The image on which the function performs the morphological operations. The calculation
+* modifies the border of the source image. The border must be at least half as large as the larger
+* dimension of the structuring element. The connected source image for a morphological transformation
+* must have been created with a border capable of supporting the size of the structuring element.
+* A 3 by 3 structuring element requires a minimal border of 1, a 5 by 5 structuring element requires a minimal border of 2, and so on.
+* @param method The morphological transform to apply.
+* @param structuringElement The structuring element used in the operation. Omit this parameter if you do not want a custom structuring element.
+* @return On success: 1. On failure: 0. To get extended error information, call GetLastError().
+*/
+int frcMorphology(Image* dest, Image* source, MorphologyMethod method)
+{
+ return imaqMorphology(dest, source, method, NULL);
+}
+
+int frcMorphology(Image* dest, Image* source, MorphologyMethod method, const StructuringElement* structuringElement)
+{
+ return imaqMorphology(dest, source, method, structuringElement);
+}
+
+/**
+* @brief Eliminates particles that touch the border of the image.
+* Supports IMAQ_IMAGE_U8.
+*
+* @param dest The destination image.
+* @param source The source image. If the image has a border, the function sets all border pixel values to 0.
+* @param connectivity8 specifies the type of connectivity used by the algorithm for particle detection.
+* The connectivity mode directly determines whether an adjacent pixel belongs to the same particle or a
+* different particle. Set to TRUE to use connectivity-8 to determine whether particles are touching
+* Set to FALSE to use connectivity-4 to determine whether particles are touching.
+* The default setting for the simplified call is TRUE
+* @return On success: 1. On failure: 0. To get extended error information, call GetLastError().
+*/
+int frcRejectBorder(Image* dest, Image* source)
+{ return imaqRejectBorder(dest, source, TRUE); }
+
+int frcRejectBorder(Image* dest, Image* source, int connectivity8)
+{
+ return imaqRejectBorder(dest, source, connectivity8);
+}
+
+
+/**
+* @brief Counts the number of particles in a binary image.
+* Supports IMAQ_IMAGE_U8, IMAQ_IMAGE_I16, IMAQ_IMAGE_SGL.
+* @param image binary (thresholded) image
+* @param numParticles On return, the number of particles.
+* @return On success: 1. On failure: 0. To get extended error information, call GetLastError().
+*/
+int frcCountParticles(Image* image, int* numParticles)
+{
+ return imaqCountParticles(image, 1, numParticles);
+}
+
+
+/**
+* @brief Conduct measurements for a single particle in an images.
+* Supports IMAQ_IMAGE_U8, IMAQ_IMAGE_I16, IMAQ_IMAGE_SGL.
+*
+* @param image image with the particle to analyze. This function modifies the source image.
+* If you need the original image, create a copy of the image using frcCopy() before using this function.
+* @param particleNumber The number of the particle to get information on
+* @param par on return, a particle analysis report containing information about the particle. This structure must be created by the caller.
+* @return On success: 1. On failure: 0. To get extended error information, call GetLastError().
+*/
+int frcParticleAnalysis(Image* image, int particleNumber, ParticleAnalysisReport* par)
+{
+ int success = 0;
+
+ /* image information */
+ int height, width;
+ if ( ! imaqGetImageSize(image, &width, &height) ) { return success; }
+ par->imageWidth = width;
+ par->imageHeight = height;
+ par->particleIndex = particleNumber;
+
+ /* center of mass point of the largest particle */
+ double returnDouble;
+ success = imaqMeasureParticle(image, particleNumber, 0, IMAQ_MT_CENTER_OF_MASS_X, &returnDouble);
+ if ( !success ) { return success; }
+ par->center_mass_x = (int)returnDouble; // pixel
+
+ success = imaqMeasureParticle(image, particleNumber, 0, IMAQ_MT_CENTER_OF_MASS_Y, &returnDouble);
+ if ( !success ) { return success; }
+ par->center_mass_y = (int)returnDouble; // pixel
+
+ /* particle size statistics */
+ success = imaqMeasureParticle(image, particleNumber, 0, IMAQ_MT_AREA, &returnDouble);
+ if ( !success ) { return success; }
+ par->particleArea = returnDouble;
+
+ success = imaqMeasureParticle(image, particleNumber, 0, IMAQ_MT_BOUNDING_RECT_TOP, &returnDouble);
+ if ( !success ) { return success; }
+ par->boundingRect.top = (int)returnDouble;
+
+ success = imaqMeasureParticle(image, particleNumber, 0, IMAQ_MT_BOUNDING_RECT_LEFT, &returnDouble);
+ if ( !success ) { return success; }
+ par->boundingRect.left = (int)returnDouble;
+
+ success = imaqMeasureParticle(image, particleNumber, 0, IMAQ_MT_BOUNDING_RECT_HEIGHT, &returnDouble);
+ if ( !success ) { return success; }
+ par->boundingRect.height = (int)returnDouble;
+
+ success = imaqMeasureParticle(image, particleNumber, 0, IMAQ_MT_BOUNDING_RECT_WIDTH, &returnDouble);
+ if ( !success ) { return success; }
+ par->boundingRect.width = (int)returnDouble;
+
+ /* particle quality statistics */
+ success = imaqMeasureParticle(image, particleNumber, 0, IMAQ_MT_AREA_BY_IMAGE_AREA, &returnDouble);
+ if ( !success ) { return success; }
+ par->particleToImagePercent = returnDouble;
+
+ success = imaqMeasureParticle(image, particleNumber, 0, IMAQ_MT_AREA_BY_PARTICLE_AND_HOLES_AREA, &returnDouble);
+ if ( !success ) { return success; }
+ par->particleQuality = returnDouble;
+
+ /* normalized position (-1 to 1) */
+ par->center_mass_x_normalized = RangeToNormalized(par->center_mass_x, width);
+ par->center_mass_y_normalized = RangeToNormalized(par->center_mass_y, height);
+
+ return success;
+}
+
+
+/* Image Enhancement functions */
+
+/**
+* @brief Improves contrast on a grayscale image.
+* Supports IMAQ_IMAGE_U8, IMAQ_IMAGE_I16.
+* @param dest The destination image.
+* @param source The image to equalize
+* @param min the smallest value used for processing. After processing, all pixel values that are less than or equal to the Minimum in the original image are set to 0 for an 8-bit image. In 16-bit and floating-point images, these pixel values are set to the smallest pixel value found in the original image.
+* @param max the largest value used for processing. After processing, all pixel values that are greater than or equal to the Maximum in the original image are set to 255 for an 8-bit image. In 16-bit and floating-point images, these pixel values are set to the largest pixel value found in the original image.
+* @param mask an 8-bit image that specifies the region of the small image that will be copied. Only those pixels in the Image Src (Small) image that correspond to an equivalent non-zero pixel in the mask image are copied. All other pixels keep their original values. The entire image is processed if Image Mask is NULL or this parameter is omitted.
+* @return On success: 1. On failure: 0. To get extended error information, call GetLastError().
+*
+* option defaults:
+* searchRect = IMAQ_NO_RECT
+* minMatchScore = DEFAULT_MINMAX_SCORE (800)
+*/
+int frcEqualize(Image* dest, const Image* source, float min, float max)
+{ return frcEqualize(dest, source, min, max, NULL); }
+
+int frcEqualize(Image* dest, const Image* source, float min, float max, const Image* mask)
+{
+ return imaqEqualize(dest, source, min, max, mask);
+}
+
+/**
+* @brief Improves contrast on a color image.
+* Supports IMAQ_IMAGE_RGB, IMAQ_IMAGE_HSL
+*
+* option defaults: colorEqualization = TRUE to equalize all three planes of the image
+* @return On success: 1. On failure: 0. To get extended error information, call GetLastError().
+* @param dest The destination image.
+* @param source The image to equalize
+* @param colorEqualization Set this parameter to TRUE to equalize all three planes of the image (the default). Set this parameter to FALSE to equalize only the luminance plane.
+*/
+int frcColorEqualize(Image* dest, const Image* source)
+{
+ return imaqColorEqualize(dest, source, TRUE);
+}
+
+int frcColorEqualize(Image* dest, const Image* source, int colorEqualization)
+{
+ return imaqColorEqualize(dest, source, TRUE);
+}
+
+/* Image Conversion functions */
+
+/**
+* @brief Automatically thresholds a grayscale image into a binary image for Particle Analysis based on a smart threshold.
+* Supports IMAQ_IMAGE_RGB, IMAQ_IMAGE_I16
+* @param dest The destination image.
+* @param source The image to threshold
+* @param windowWidth The width of the rectangular window around the pixel on which the function
+* performs the local threshold. This number must be at least 3 and cannot be larger than the width of source
+* @param windowHeight The height of the rectangular window around the pixel on which the function
+* performs the local threshold. This number must be at least 3 and cannot be larger than the height of source
+* @param method Specifies the local thresholding method the function uses. Value can be IMAQ_NIBLACK
+* (which computes thresholds for each pixel based on its local statistics using the Niblack local thresholding
+* algorithm.), or IMAQ_BACKGROUND_CORRECTION (which does background correction first to eliminate non-uniform
+* lighting effects, then performs thresholding using the Otsu thresholding algorithm)
+* @param deviationWeight Specifies the k constant used in the Niblack local thresholding algorithm, which
+* determines the weight applied to the variance calculation. Valid k constants range from 0 to 1. Setting
+* this value to 0 will increase the performance of the function because the function will not calculate the
+* variance for any of the pixels. The function ignores this value if method is not set to IMAQ_NIBLACK
+* @param type Specifies the type of objects for which you want to look. Values can be IMAQ_BRIGHT_OBJECTS
+* or IMAQ_DARK_OBJECTS.
+* @param replaceValue Specifies the replacement value the function uses for the pixels of the kept objects
+* in the destination image.
+* @return On success: 1. On failure: 0. To get extended error information, call GetLastError().
+*/
+int frcSmartThreshold(Image* dest, const Image* source,
+ unsigned int windowWidth, unsigned int windowHeight, LocalThresholdMethod method,
+ double deviationWeight, ObjectType type)
+{
+ float replaceValue = 1.0;
+ return imaqLocalThreshold(dest, source, windowWidth, windowHeight, method,
+ deviationWeight, type, replaceValue);
+}
+
+int frcSmartThreshold(Image* dest, const Image* source,
+ unsigned int windowWidth, unsigned int windowHeight, LocalThresholdMethod method,
+ double deviationWeight, ObjectType type, float replaceValue)
+{
+ return imaqLocalThreshold(dest, source, windowWidth, windowHeight, method,
+ deviationWeight, type, replaceValue);
+}
+
+/**
+* @brief Converts a grayscale image to a binary image for Particle Analysis based on a fixed threshold.
+* The function sets pixels values outside of the given range to 0. The function sets pixel values
+* within the range to a given value or leaves the values unchanged.
+* Use the simplified call to leave pixel values unchanged.
+* Supports IMAQ_IMAGE_RGB, IMAQ_IMAGE_I16.
+*
+* @param dest The destination image.
+* @param source The image to threshold
+* @param rangeMin The lower boundary of the range of pixel values to keep
+* @param rangeMax The upper boundary of the range of pixel values to keep.
+*
+* @return int - error code: 0 = error. To get extended error information, call GetLastError().
+*/
+int frcSimpleThreshold(Image* dest, const Image* source, float rangeMin, float rangeMax)
+{
+ int newValue = 255;
+ return frcSimpleThreshold(dest, source, rangeMin, rangeMax, newValue);
+}
+
+/**
+* @brief Converts a grayscale image to a binary image for Particle Analysis based on a fixed threshold.
+* The function sets pixels values outside of the given range to 0. The function sets
+* pixel values within the range to the given value.
+* Supports IMAQ_IMAGE_RGB, IMAQ_IMAGE_I16.
+*
+* @param dest The destination image.
+* @param source The image to threshold
+* @param rangeMin The lower boundary of the range of pixel values to keep
+* @param rangeMax The upper boundary of the range of pixel values to keep.
+* @param newValue The replacement value for pixels within the range. Use the simplified call to leave the pixel values unchanged
+*
+* @return int - error code: 0 = error. To get extended error information, call GetLastError().
+*/
+int frcSimpleThreshold(Image* dest, const Image* source, float rangeMin, float rangeMax, float newValue)
+{
+ int useNewValue = TRUE;
+ return imaqThreshold(dest, source, rangeMin, rangeMax, useNewValue, newValue);
+}
+
+/**
+* @brief Applies a threshold to the Red, Green, and Blue values of a RGB image or the Hue,
+* Saturation, Luminance values for a HSL image.
+* Supports IMAQ_IMAGE_RGB, IMAQ_IMAGE_HSL.
+* This simpler version filters based on a hue range in the HSL mode.
+*
+* @param dest The destination image. This must be a IMAQ_IMAGE_U8 image.
+* @param source The image to threshold
+* @param mode The color space to perform the threshold in. valid values are: IMAQ_RGB, IMAQ_HSL.
+* @param plane1Range The selection range for the first plane of the image. Set this parameter to NULL to use a selection range from 0 to 255.
+* @param plane2Range The selection range for the second plane of the image. Set this parameter to NULL to use a selection range from 0 to 255.
+* @param plane3Range The selection range for the third plane of the image. Set this parameter to NULL to use a selection range from 0 to 255.
+*
+* @return On success: 1. On failure: 0. To get extended error information, call GetLastError().
+* */
+int frcColorThreshold(Image* dest, const Image* source, ColorMode mode,
+ const Range* plane1Range, const Range* plane2Range, const Range* plane3Range)
+{
+ int replaceValue = 1;
+ return imaqColorThreshold(dest, source, replaceValue, mode, plane1Range, plane2Range, plane3Range);
+}
+
+/**
+* @brief Applies a threshold to the Red, Green, and Blue values of a RGB image or the Hue,
+* Saturation, Luminance values for a HSL image.
+* Supports IMAQ_IMAGE_RGB, IMAQ_IMAGE_HSL.
+* The simpler version filters based on a hue range in the HSL mode.
+*
+* @param dest The destination image. This must be a IMAQ_IMAGE_U8 image.
+* @param source The image to threshold
+* @param replaceValue Value to assign to selected pixels. Defaults to 1 if simplified call is used.
+* @param mode The color space to perform the threshold in. valid values are: IMAQ_RGB, IMAQ_HSL.
+* @param plane1Range The selection range for the first plane of the image. Set this parameter to NULL to use a selection range from 0 to 255.
+* @param plane2Range The selection range for the second plane of the image. Set this parameter to NULL to use a selection range from 0 to 255.
+* @param plane3Range The selection range for the third plane of the image. Set this parameter to NULL to use a selection range from 0 to 255.
+*
+* @return On success: 1. On failure: 0. To get extended error information, call GetLastError().
+*/
+int frcColorThreshold(Image* dest, const Image* source, int replaceValue, ColorMode mode,
+ const Range* plane1Range, const Range* plane2Range, const Range* plane3Range)
+{ return imaqColorThreshold(dest, source, replaceValue, mode, plane1Range, plane2Range, plane3Range);}
+
+
+/**
+* @brief A simpler version of ColorThreshold that thresholds hue range in the HSL mode. Supports IMAQ_IMAGE_RGB, IMAQ_IMAGE_HSL.
+* @param dest The destination image.
+* @param source The image to threshold
+* @param hueRange The selection range for the hue (color).
+* @param minSaturation The minimum saturation value (1-255). If not used, DEFAULT_SATURATION_THRESHOLD is the default.
+*
+* @return On success: 1. On failure: 0. To get extended error information, call GetLastError().
+*/
+int frcHueThreshold(Image* dest, const Image* source, const Range* hueRange)
+{ return frcHueThreshold(dest, source, hueRange, DEFAULT_SATURATION_THRESHOLD); }
+
+int frcHueThreshold(Image* dest, const Image* source, const Range* hueRange, int minSaturation)
+{
+ // assume HSL mode
+ ColorMode mode = IMAQ_HSL;
+ // Set saturation 100 - 255
+ Range satRange; satRange.minValue = minSaturation; satRange.maxValue = 255;
+ // Set luminance 100 - 255
+ Range lumRange; lumRange.minValue = 100; lumRange.maxValue = 255;
+ // Replace pixels with 1 if pass threshold filter
+ int replaceValue = 1;
+ return imaqColorThreshold(dest, source, replaceValue, mode, hueRange, &satRange, &lumRange);
+}
+
+/**
+* @brief Extracts the Red, Green, Blue, or Hue, Saturation or Luminance information from a color image.
+* Supports IMAQ_IMAGE_RGB, IMAQ_IMAGE_HSL, IMAQ_IMAGE_RGB_U64.
+*
+* @param image The source image that the function extracts the planes from.
+* @param mode The color space that the function extracts the planes from. Valid values are IMAQ_RGB, IMAQ_HSL, IMAQ_HSV, IMAQ_HSI.
+* @param plane1 On return, the first extracted plane. Set this parameter to NULL if you do not need this information. RGB-Red, HSL/HSV/HSI-Hue.
+* @param plane2 On return, the second extracted plane. Set this parameter to NULL if you do not need this information. RGB-Green, HSL/HSV/HSI-Saturation.
+* @param plane3 On return, the third extracted plane. Set this parameter to NULL if you do not need this information. RGB-Blue, HSL-Luminance, HSV-Value, HSI-Intensity.
+*
+* @return error code: 0 = error. To get extended error information, call GetLastError().
+*/
+int frcExtractColorPlanes(const Image* image, ColorMode mode,
+ Image* plane1, Image* plane2, Image* plane3)
+{ return imaqExtractColorPlanes(image, mode, plane1, plane2, plane3); }
+
+/**
+* @brief Extracts the Hue information from a color image. Supports IMAQ_IMAGE_RGB, IMAQ_IMAGE_HSL, IMAQ_IMAGE_RGB_U64
+*
+* @param image The source image that the function extracts the plane from.
+* @param huePlane On return, the extracted hue plane.
+* @param minSaturation the minimum saturation level required 0-255 (try 50)
+*
+* @return On success: 1. On failure: 0. To get extended error information, call GetLastError().
+*/
+int frcExtractHuePlane(const Image* image, Image* huePlane)
+{
+ return frcExtractHuePlane(image, huePlane, DEFAULT_SATURATION_THRESHOLD);
+}
+
+int frcExtractHuePlane(const Image* image, Image* huePlane, int minSaturation)
+{
+ return frcExtractColorPlanes(image, IMAQ_HSL, huePlane, NULL, NULL);
+}
+
+
+
+
+
+
+
+
diff --git a/aos/externals/forwpilib/README b/aos/externals/forwpilib/README
new file mode 100644
index 0000000..faa1a78
--- /dev/null
+++ b/aos/externals/forwpilib/README
@@ -0,0 +1,4 @@
+This directory contains files that we plan to eventually push to upstream
+WPILib but haven't yet.
+
+The canonical location for dma.* is robotics.mvla.net:/www/https/git/frc971/jerry/dma.git.
diff --git a/aos/externals/forwpilib/dma.cc b/aos/externals/forwpilib/dma.cc
new file mode 100644
index 0000000..148ff29
--- /dev/null
+++ b/aos/externals/forwpilib/dma.cc
@@ -0,0 +1,458 @@
+#include "dma.h"
+
+#include <string.h>
+
+#include <algorithm>
+#include <type_traits>
+
+#include "DigitalSource.h"
+#include "AnalogInput.h"
+#include "Encoder.h"
+
+
+// Interface to the roboRIO FPGA's DMA features.
+
+// Like tEncoder::tOutput with the bitfields reversed.
+typedef union {
+ struct {
+ unsigned Direction: 1;
+ signed Value: 31;
+ };
+ struct {
+ unsigned value: 32;
+ };
+} t1Output;
+
+static const uint32_t kNumHeaders = 10;
+
+static constexpr ssize_t kChannelSize[18] = {2, 2, 4, 4, 2, 2, 4, 4, 3,
+ 3, 2, 1, 4, 4, 4, 4, 4, 4};
+
+enum DMAOffsetConstants {
+ kEnable_AI0_Low = 0,
+ kEnable_AI0_High = 1,
+ kEnable_AIAveraged0_Low = 2,
+ kEnable_AIAveraged0_High = 3,
+ kEnable_AI1_Low = 4,
+ kEnable_AI1_High = 5,
+ kEnable_AIAveraged1_Low = 6,
+ kEnable_AIAveraged1_High = 7,
+ kEnable_Accumulator0 = 8,
+ kEnable_Accumulator1 = 9,
+ kEnable_DI = 10,
+ kEnable_AnalogTriggers = 11,
+ kEnable_Counters_Low = 12,
+ kEnable_Counters_High = 13,
+ kEnable_CounterTimers_Low = 14,
+ kEnable_CounterTimers_High = 15,
+ kEnable_Encoders = 16,
+ kEnable_EncoderTimers = 17,
+};
+
+DMA::DMA() {
+ tRioStatusCode status = 0;
+ tdma_config_ = tDMA::create(&status);
+ tdma_config_->writeConfig_ExternalClock(false, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ NiFpga_WriteU32(0x10000, 0x1832c, 0x0);
+ if (status != 0) {
+ return;
+ }
+ SetRate(1);
+ SetPause(false);
+}
+
+DMA::~DMA() {
+ tRioStatusCode status = 0;
+
+ manager_->stop(&status);
+ delete tdma_config_;
+}
+
+void DMA::SetPause(bool pause) {
+ tRioStatusCode status = 0;
+ tdma_config_->writeConfig_Pause(pause, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+}
+
+void DMA::SetRate(uint32_t cycles) {
+ if (cycles < 1) {
+ cycles = 1;
+ }
+ tRioStatusCode status = 0;
+ tdma_config_->writeRate(cycles, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+}
+
+void DMA::Add(Encoder *encoder) {
+ tRioStatusCode status = 0;
+
+ if (manager_) {
+ wpi_setErrorWithContext(NiFpga_Status_InvalidParameter,
+ "DMA::Add() only works before DMA::Start()");
+ return;
+ }
+ if (encoder->GetFPGAIndex() >= 4) {
+ wpi_setErrorWithContext(
+ NiFpga_Status_InvalidParameter,
+ "FPGA encoder index is not in the 4 that get logged.");
+ }
+
+ // TODO(austin): Encoder uses a Counter for 1x or 2x; quad for 4x...
+ tdma_config_->writeConfig_Enable_Encoders(true, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+}
+
+void DMA::Add(DigitalSource * /*input*/) {
+ tRioStatusCode status = 0;
+
+ if (manager_) {
+ wpi_setErrorWithContext(NiFpga_Status_InvalidParameter,
+ "DMA::Add() only works before DMA::Start()");
+ return;
+ }
+
+ tdma_config_->writeConfig_Enable_DI(true, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+}
+
+void DMA::Add(AnalogInput *input) {
+ tRioStatusCode status = 0;
+
+ if (manager_) {
+ wpi_setErrorWithContext(NiFpga_Status_InvalidParameter,
+ "DMA::Add() only works before DMA::Start()");
+ return;
+ }
+
+ if (input->GetChannel() <= 3) {
+ tdma_config_->writeConfig_Enable_AI0_Low(true, &status);
+ } else {
+ tdma_config_->writeConfig_Enable_AI0_High(true, &status);
+ }
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+}
+
+void DMA::SetExternalTrigger(DigitalSource *input, bool rising, bool falling) {
+ tRioStatusCode status = 0;
+
+ if (manager_) {
+ wpi_setErrorWithContext(NiFpga_Status_InvalidParameter,
+ "DMA::SetExternalTrigger() only works before DMA::Start()");
+ return;
+ }
+
+ auto index =
+ ::std::find(trigger_channels_.begin(), trigger_channels_.end(), false);
+ if (index == trigger_channels_.end()) {
+ wpi_setErrorWithContext(NiFpga_Status_InvalidParameter,
+ "DMA: No channels left");
+ return;
+ }
+ *index = true;
+
+ const int channel_index = index - trigger_channels_.begin();
+ /*
+ printf(
+ "Allocating trigger on index %d, routing module %d, routing channel %d, "
+ "is analog %d\n",
+ channel_index, input->GetModuleForRouting(),
+ input->GetChannelForRouting(), input->GetAnalogTriggerForRouting());
+ */
+
+ const bool is_external_clock =
+ tdma_config_->readConfig_ExternalClock(&status);
+ if (status == 0) {
+ if (!is_external_clock) {
+ tdma_config_->writeConfig_ExternalClock(true, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ if (status != 0) {
+ return;
+ }
+ }
+ } else {
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ return;
+ }
+
+ nFPGA::nFRC_2015_1_0_A::tDMA::tExternalTriggers new_trigger;
+
+ new_trigger.FallingEdge = falling;
+ new_trigger.RisingEdge = rising;
+ new_trigger.ExternalClockSource_AnalogTrigger =
+ input->GetAnalogTriggerForRouting();
+ new_trigger.ExternalClockSource_AnalogTrigger = false;
+ new_trigger.ExternalClockSource_Module = input->GetModuleForRouting();
+ new_trigger.ExternalClockSource_Channel = input->GetChannelForRouting();
+
+ // Configures the trigger to be external, not off the FPGA clock.
+ /*
+ tdma_config_->writeExternalTriggers(channel_index, new_trigger, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ */
+
+ uint32_t current_triggers;
+ tRioStatusCode register_status = NiFpga_ReadU32(0x10000, 0x1832c, ¤t_triggers);
+ if (register_status != 0) {
+ wpi_setErrorWithContext(register_status, getHALErrorMessage(status));
+ return;
+ }
+ current_triggers = (current_triggers & ~(0xff << (channel_index * 8))) |
+ (new_trigger.value << (channel_index * 8));
+ register_status = NiFpga_WriteU32(0x10000, 0x1832c, current_triggers);
+ if (register_status != 0) {
+ wpi_setErrorWithContext(register_status, getHALErrorMessage(status));
+ return;
+ }
+}
+
+DMA::ReadStatus DMA::Read(DMASample *sample, uint32_t timeout_ms,
+ size_t *remaining_out) {
+ tRioStatusCode status = 0;
+ size_t remainingBytes = 0;
+ *remaining_out = 0;
+
+ if (!manager_.get()) {
+ wpi_setErrorWithContext(NiFpga_Status_InvalidParameter,
+ "DMA::Read() only works after DMA::Start()");
+ return STATUS_ERROR;
+ }
+
+ sample->dma_ = this;
+ // memset(&sample->read_buffer_, 0, sizeof(sample->read_buffer_));
+ manager_->read(sample->read_buffer_, capture_size_, timeout_ms,
+ &remainingBytes, &status);
+
+ if (0) { // DEBUG
+ printf("buf[] = ");
+ for (size_t i = 0;
+ i < sizeof(sample->read_buffer_) / sizeof(sample->read_buffer_[0]);
+ ++i) {
+ if (i != 0) {
+ printf(" ");
+ }
+ printf("0x%.8x", sample->read_buffer_[i]);
+ }
+ printf("\n");
+ }
+
+ // TODO(jerry): Do this only if status == 0?
+ *remaining_out = remainingBytes / capture_size_;
+
+ if (0) { // DEBUG
+ printf("Remaining samples = %d\n", *remaining_out);
+ }
+
+ // TODO(austin): Check that *remainingBytes % capture_size_ == 0 and deal
+ // with it if it isn't. Probably meant that we overflowed?
+ if (status == 0) {
+ return STATUS_OK;
+ } else if (status == NiFpga_Status_FifoTimeout) {
+ return STATUS_TIMEOUT;
+ } else {
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ return STATUS_ERROR;
+ }
+}
+
+const char *DMA::NameOfReadStatus(ReadStatus s) {
+ switch (s) {
+ case STATUS_OK: return "OK";
+ case STATUS_TIMEOUT: return "TIMEOUT";
+ case STATUS_ERROR: return "ERROR";
+ default: return "(bad ReadStatus code)";
+ }
+}
+
+void DMA::Start(size_t queue_depth) {
+ tRioStatusCode status = 0;
+ tconfig_ = tdma_config_->readConfig(&status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ if (status != 0) {
+ return;
+ }
+
+ {
+ size_t accum_size = 0;
+#define SET_SIZE(bit) \
+ if (tconfig_.bit) { \
+ channel_offsets_[k##bit] = accum_size; \
+ accum_size += kChannelSize[k##bit]; \
+ } else { \
+ channel_offsets_[k##bit] = -1; \
+ }
+
+ SET_SIZE(Enable_AI0_Low);
+ SET_SIZE(Enable_AI0_High);
+ SET_SIZE(Enable_AIAveraged0_Low);
+ SET_SIZE(Enable_AIAveraged0_High);
+ SET_SIZE(Enable_AI1_Low);
+ SET_SIZE(Enable_AI1_High);
+ SET_SIZE(Enable_AIAveraged1_Low);
+ SET_SIZE(Enable_AIAveraged1_High);
+ SET_SIZE(Enable_Accumulator0);
+ SET_SIZE(Enable_Accumulator1);
+ SET_SIZE(Enable_DI);
+ SET_SIZE(Enable_AnalogTriggers);
+ SET_SIZE(Enable_Counters_Low);
+ SET_SIZE(Enable_Counters_High);
+ SET_SIZE(Enable_CounterTimers_Low);
+ SET_SIZE(Enable_CounterTimers_High);
+ SET_SIZE(Enable_Encoders);
+ SET_SIZE(Enable_EncoderTimers);
+#undef SET_SIZE
+ capture_size_ = accum_size + 1;
+ }
+
+ manager_.reset(
+ new nFPGA::tDMAManager(0, queue_depth * capture_size_, &status));
+
+ if (0) {
+ for (int i = 0; i < 4; ++i) {
+ tRioStatusCode status = 0;
+ auto x = tdma_config_->readExternalTriggers(i, &status);
+ printf(
+ "index %d, FallingEdge: %d, RisingEdge: %d, "
+ "ExternalClockSource_AnalogTrigger: %d, ExternalClockSource_Module: "
+ "%d, ExternalClockSource_Channel: %d\n",
+ i, x.FallingEdge, x.RisingEdge, x.ExternalClockSource_AnalogTrigger,
+ x.ExternalClockSource_Module, x.ExternalClockSource_Channel);
+ if (status != 0) {
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ }
+ }
+ }
+
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ if (status != 0) {
+ return;
+ }
+ // Start, stop, start to clear the buffer.
+ manager_->start(&status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ if (status != 0) {
+ return;
+ }
+ manager_->stop(&status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ if (status != 0) {
+ return;
+ }
+ manager_->start(&status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ if (status != 0) {
+ return;
+ }
+
+}
+
+static_assert(::std::is_pod<DMASample>::value, "DMASample needs to be POD");
+
+ssize_t DMASample::offset(int index) const { return dma_->channel_offsets_[index]; }
+
+double DMASample::GetTimestamp() const {
+ return static_cast<double>(read_buffer_[dma_->capture_size_ - 1]) * 0.000001;
+}
+
+bool DMASample::Get(DigitalSource *input) const {
+ if (offset(kEnable_DI) == -1) {
+ wpi_setStaticErrorWithContext(dma_,
+ NiFpga_Status_ResourceNotFound,
+ getHALErrorMessage(NiFpga_Status_ResourceNotFound));
+ return false;
+ }
+ if (input->GetChannelForRouting() < kNumHeaders) {
+ return (read_buffer_[offset(kEnable_DI)] >>
+ input->GetChannelForRouting()) &
+ 0x1;
+ } else {
+ return (read_buffer_[offset(kEnable_DI)] >>
+ (input->GetChannelForRouting() + 6)) &
+ 0x1;
+ }
+}
+
+int32_t DMASample::GetRaw(Encoder *input) const {
+ if (offset(kEnable_Encoders) == -1) {
+ wpi_setStaticErrorWithContext(dma_,
+ NiFpga_Status_ResourceNotFound,
+ getHALErrorMessage(NiFpga_Status_ResourceNotFound));
+ return -1;
+ }
+
+ if (input->GetFPGAIndex() >= 4) {
+ wpi_setStaticErrorWithContext(dma_,
+ NiFpga_Status_ResourceNotFound,
+ getHALErrorMessage(NiFpga_Status_ResourceNotFound));
+ }
+
+ uint32_t dmaWord =
+ read_buffer_[offset(kEnable_Encoders) + input->GetFPGAIndex()];
+ int32_t result = 0;
+
+ if (1) {
+ // Extract the 31-bit signed tEncoder::tOutput Value using a struct with the
+ // reverse packed field order of tOutput. This gets Value from the high
+ // order 31 bits of output on little-endian ARM using gcc. This works
+ // even though C/C++ doesn't guarantee bitfield order.
+ t1Output output;
+
+ output.value = dmaWord;
+ result = output.Value;
+ } else if (1) {
+ // Extract the 31-bit signed tEncoder::tOutput Value using right-shift.
+ // This works even though C/C++ doesn't guarantee whether signed >> does
+ // arithmetic or logical shift. (dmaWord / 2) would fix that but it rounds.
+ result = static_cast<int32_t>(dmaWord) >> 1;
+ }
+#if 0 // This approach was recommended but it doesn't return the right value.
+ else {
+ // Byte-reverse the DMA word (big-endian value from the FPGA) then extract
+ // the 31-bit tEncoder::tOutput. This does not return the right Value.
+ tEncoder::tOutput encoderData;
+
+ encoderData.value = __builtin_bswap32(dmaWord);
+ result = encoderData.Value;
+ }
+#endif
+
+ return result;
+}
+
+int32_t DMASample::Get(Encoder *input) const {
+ int32_t raw = GetRaw(input);
+
+ return raw / input->GetEncodingScale();
+}
+
+uint16_t DMASample::GetValue(AnalogInput *input) const {
+ if (offset(kEnable_Encoders) == -1) {
+ wpi_setStaticErrorWithContext(dma_,
+ NiFpga_Status_ResourceNotFound,
+ getHALErrorMessage(NiFpga_Status_ResourceNotFound));
+ return 0xffff;
+ }
+
+ uint32_t dmaWord;
+ uint32_t channel = input->GetChannel();
+ if (channel <= 3) {
+ dmaWord = read_buffer_[offset(kEnable_AI0_Low) + channel / 2];
+ } else {
+ dmaWord = read_buffer_[offset(kEnable_AI0_High) + (channel - 4) / 2];
+ }
+ if (channel % 2) {
+ return (dmaWord >> 16) & 0xffff;
+ } else {
+ return (dmaWord) & 0xffff;
+ }
+ return static_cast<int16_t>(dmaWord);
+}
+
+float DMASample::GetVoltage(AnalogInput *input) const {
+ uint16_t value = GetValue(input);
+ if (value == 0xffff) return 0.0;
+ uint32_t lsb_weight = input->GetLSBWeight();
+ int32_t offset = input->GetOffset();
+ float voltage = lsb_weight * 1.0e-9 * value - offset * 1.0e-9;
+ return voltage;
+}
diff --git a/aos/externals/forwpilib/dma.h b/aos/externals/forwpilib/dma.h
new file mode 100644
index 0000000..754b0a5
--- /dev/null
+++ b/aos/externals/forwpilib/dma.h
@@ -0,0 +1,121 @@
+#ifndef _DMA_H_
+#define _DMA_H_
+
+// Interface to the roboRIO FPGA's DMA features.
+
+#include <stdint.h>
+
+#include <array>
+#include <memory>
+
+#include "ChipObject.h"
+#include "ErrorBase.h"
+
+class DMA;
+class DigitalSource;
+class AnalogInput;
+class Encoder;
+
+// A POD class which stores the data from a DMA sample and provides safe ways to
+// access it.
+class DMASample {
+ public:
+ DMASample() = default;
+
+ // Returns the FPGA timestamp of the sample.
+ double GetTimestamp() const;
+
+ // All Get methods either return the requested value, or set the Error.
+
+ // Returns the value of the digital input in the sample.
+ bool Get(DigitalSource *input) const;
+ // Returns the raw value of the encoder in the sample.
+ int32_t GetRaw(Encoder *input) const;
+ // Returns the {1, 2, or 4} X scaled value of the encoder in the sample.
+ int32_t Get(Encoder *input) const;
+ // Returns the raw 12-bit value from the ADC.
+ uint16_t GetValue(AnalogInput *input) const;
+ // Returns the scaled value of an analog input.
+ float GetVoltage(AnalogInput *input) const;
+
+ private:
+ friend DMA;
+
+ // Returns the offset of the sample type in the buffer, or -1 if it isn't in
+ // the sample.
+ ssize_t offset(int index) const;
+
+ // TODO(austin): This should be re-used from WPILib... Once I merge this back
+ // into WPILib.
+
+ DMA *dma_;
+ uint32_t read_buffer_[64];
+};
+
+// TODO(austin): ErrorBase...
+class DMA : public ErrorBase {
+ public:
+ DMA();
+ virtual ~DMA();
+
+ // Sets whether or not DMA is paused.
+ void SetPause(bool pause);
+
+ // Sets the number of triggers that need to occur before a sample is saved.
+ void SetRate(uint32_t cycles);
+
+ // Adds the input signal to the state to snapshot on the trigger event.
+ // It is safe to add the same input multiple times, but there is currently
+ // no way to remove one once it has been added.
+ // Call Add() and SetExternalTrigger() before Start().
+ void Add(Encoder *encoder);
+ void Add(DigitalSource *input);
+ void Add(AnalogInput *input);
+
+ // Configures DMA to trigger on an external trigger. There can only be 4
+ // external triggers.
+ // Call Add() and SetExternalTrigger() before Start().
+ void SetExternalTrigger(DigitalSource *input, bool rising, bool falling);
+
+ // Starts reading samples into the buffer. Clears all previous samples before
+ // starting.
+ // Call Start() before Read().
+ void Start(size_t queue_depth);
+
+ enum ReadStatus {
+ STATUS_OK = 0,
+ STATUS_TIMEOUT = 1,
+ STATUS_ERROR = 2,
+ };
+
+ // Reads a sample from the DMA buffer, waiting up to timeout_ms for it.
+ // Returns a status code indicating whether the read worked, timed out, or
+ // failed.
+ // Returns in *remaining_out the number of DMA samples still queued after this
+ // Read().
+ // Call Add() and SetExternalTrigger() then Start() before Read().
+ // The sample is only usable while this DMA object is left started.
+ ReadStatus Read(DMASample *sample, uint32_t timeout_ms,
+ size_t *remaining_out);
+
+ // Translates a ReadStatus code to a string name.
+ static const char *NameOfReadStatus(ReadStatus s);
+
+ private:
+ ::std::unique_ptr<nFPGA::tDMAManager> manager_; // set by Start()
+ typedef nFPGA::nRoboRIO_FPGANamespace::tDMA tDMA;
+ friend DMASample;
+
+ // The offsets into the sample structure for each DMA type, or -1 if it isn't
+ // in the set of values.
+ ssize_t channel_offsets_[18];
+
+ // The size of the data to read to get a sample.
+ size_t capture_size_ = 0;
+ tDMA::tConfig tconfig_;
+ tDMA *tdma_config_;
+
+ ::std::array<bool, 4> trigger_channels_ = {{false, false, false, false}};
+};
+
+#endif // _DMA_H_
diff --git a/aos/externals/gtest.patch b/aos/externals/gtest.patch
new file mode 100644
index 0000000..f0ee200
--- /dev/null
+++ b/aos/externals/gtest.patch
@@ -0,0 +1,59 @@
+diff --git src/gtest-port.cc b/src/gtest-port.cc
+index b860d48..acb459b 100644
+--- a/src/gtest-port.cc
++++ b/src/gtest-port.cc
+@@ -35,6 +35,8 @@
+ #include <stdlib.h>
+ #include <stdio.h>
+ #include <string.h>
++#include <sys/types.h>
++#include <dirent.h>
+
+ #if GTEST_OS_WINDOWS_MOBILE
+ # include <windows.h> // For TerminateProcess()
+@@ -98,6 +98,21 @@ size_t GetThreadCount() {
+ }
+ }
+
++#elif GTEST_OS_LINUX
++
++size_t GetThreadCount() {
++ size_t thread_count = 0;
++ if (DIR *dir = opendir("/proc/self/task")) {
++ while (dirent *entry = readdir(dir)) {
++ if (strcmp(entry->d_name, ".") != 0 && strcmp(entry->d_name, "..") != 0) {
++ ++thread_count;
++ }
++ }
++ closedir(dir);
++ }
++ return thread_count;
++}
++
+ #else
+
+ size_t GetThreadCount() {
+diff --git a/src/gtest-death-test.cc b/src/gtest-death-test.cc
+index 8b2e413..faad3a4 100644
+--- a/src/gtest-death-test.cc
++++ b/src/gtest-death-test.cc
+@@ -44,6 +44,8 @@
+ # include <fcntl.h>
+ # include <limits.h>
+ # include <stdarg.h>
++# include <sys/time.h>
++# include <sys/resource.h>
+
+ # if GTEST_OS_WINDOWS
+ # include <windows.h>
+@@ -898,6 +900,11 @@ inline char** GetEnviron() { return environ; }
+ // This function is called in a clone()-ed process and thus must avoid
+ // any potentially unsafe operations like malloc or libc functions.
+ static int ExecDeathTestChildMain(void* child_arg) {
++ rlimit core_rlimit;
++ core_rlimit.rlim_cur = 0;
++ core_rlimit.rlim_max = 0;
++ GTEST_DEATH_TEST_CHECK_SYSCALL_(setrlimit(RLIMIT_CORE, &core_rlimit));
++
+ ExecDeathTestArgs* const args = static_cast<ExecDeathTestArgs*>(child_arg);
+ GTEST_DEATH_TEST_CHECK_SYSCALL_(close(args->close_fd));
diff --git a/aos/externals/gtest/gtest_main.cc b/aos/externals/gtest/gtest_main.cc
new file mode 100644
index 0000000..db403bb
--- /dev/null
+++ b/aos/externals/gtest/gtest_main.cc
@@ -0,0 +1,67 @@
+#include <iostream>
+#include <getopt.h>
+
+#include "gtest/gtest.h"
+
+namespace aos {
+namespace common {
+namespace testing {
+
+// Actually declared/defined in aos/common/queue_testutils.
+void SetLogFileName(const char* filename) __attribute__((weak));
+void ForcePrintLogsDuringTests() __attribute__((weak));
+
+} // namespace testing
+} // namespace common
+} // namespace aos
+
+GTEST_API_ int main(int argc, char **argv) {
+ static const struct option long_options[] = {
+ {"help", no_argument, 0, 'h'},
+ {"print-logs", no_argument, 0, 'p'},
+ {"log-file", required_argument, 0, 'o'},
+ {0, 0, 0, 0},
+ };
+
+ testing::InitGoogleTest(&argc, argv);
+
+ // The gtest library modifies argc and argv to remove all of its own command
+ // line switches etc. So after calling InitGoogleTest() we can parse our own
+ // command line options.
+ while (true) {
+ int c = getopt_long(argc, argv, "pho:", long_options, nullptr);
+
+ if (c == -1) {
+ break;
+ }
+
+ switch (c) {
+ case 'h':
+ printf(
+ "\nFRC971 options:\n"
+ " -p, --print-logs\n"
+ " Print the log messages as they are being generated.\n"
+ " -o, --log-file=FILE\n"
+ " Print all log messages to FILE instead of standard output\n"
+ );
+ break;
+
+ case 'p':
+ if (::aos::common::testing::ForcePrintLogsDuringTests) {
+ ::aos::common::testing::ForcePrintLogsDuringTests();
+ }
+ break;
+
+ case 'o':
+ if (::aos::common::testing::SetLogFileName) {
+ ::aos::common::testing::SetLogFileName(optarg);
+ }
+ break;
+
+ case '?':
+ abort();
+ }
+ }
+
+ return RUN_ALL_TESTS();
+}
diff --git a/aos/externals/gyp.patch b/aos/externals/gyp.patch
new file mode 100644
index 0000000..b09b67d
--- /dev/null
+++ b/aos/externals/gyp.patch
@@ -0,0 +1,21 @@
+diff -rupN before/pylib/gyp/input.py after/pylib/gyp/input.py
+--- before/pylib/gyp/input.py 2012-11-20 16:38:09.394784918 -0800
++++ after/pylib/gyp/input.py 2012-11-20 16:39:10.527105964 -0800
+@@ -2412,17 +2412,6 @@ def ValidateSourcesInTarget(target, targ
+ basename = os.path.basename(name) # Don't include extension.
+ basenames.setdefault(basename, []).append(source)
+
+- error = ''
+- for basename, files in basenames.iteritems():
+- if len(files) > 1:
+- error += ' %s: %s\n' % (basename, ' '.join(files))
+-
+- if error:
+- print('static library %s has several files with the same basename:\n' %
+- target + error + 'Some build systems, e.g. MSVC08, '
+- 'cannot handle that.')
+- raise GypError('Duplicate basenames in sources section, see list above')
+-
+
+ def ValidateRulesInTarget(target, target_dict, extra_sources_for_rules):
+ """Ensures that the rules sections in target_dict are valid and consistent,
diff --git a/aos/externals/seasocks.patch b/aos/externals/seasocks.patch
new file mode 100644
index 0000000..e98c65d
--- /dev/null
+++ b/aos/externals/seasocks.patch
@@ -0,0 +1,40 @@
+diff --git a/src/main/c/HybiPacketDecoder.cpp b/src/main/c/HybiPacketDecoder.cpp
+index f0fdefe..c2971b2 100644
+--- a/src/main/c/HybiPacketDecoder.cpp
++++ b/src/main/c/HybiPacketDecoder.cpp
+@@ -27,6 +27,7 @@
+ #include "internal/LogStream.h"
+
+ #include <arpa/inet.h>
++#include <string.h>
+
+ namespace seasocks {
+
+@@ -56,18 +57,24 @@ HybiPacketDecoder::MessageState HybiPacketDecoder::decodeNextMessage(std::vector
+ auto ptr = _messageStart + 2;
+ if (payloadLength == 126) {
+ if (_buffer.size() < 4) { return NoMessage; }
+- payloadLength = htons(*reinterpret_cast<const uint16_t*>(&_buffer[ptr]));
++ uint16_t raw_length;
++ memcpy(&raw_length, &_buffer[ptr], sizeof(raw_length));
++ payloadLength = htons(raw_length);
+ ptr += 2;
+ } else if (payloadLength == 127) {
+ if (_buffer.size() < 10) { return NoMessage; }
+- payloadLength = __bswap_64(*reinterpret_cast<const uint64_t*>(&_buffer[ptr]));
++ uint64_t raw_length;
++ memcpy(&raw_length, &_buffer[ptr], sizeof(raw_length));
++ payloadLength = __bswap_64(raw_length);
+ ptr += 8;
+ }
+ uint32_t mask = 0;
+ if (maskBit) {
+ // MASK is set.
+ if (_buffer.size() < ptr + 4) { return NoMessage; }
+- mask = htonl(*reinterpret_cast<const uint32_t*>(&_buffer[ptr]));
++ uint32_t raw_length;
++ memcpy(&raw_length, &_buffer[ptr], sizeof(raw_length));
++ mask = htonl(raw_length);
+ ptr += 4;
+ }
+ auto bytesLeftInBuffer = _buffer.size() - ptr;
diff --git a/aos/externals/seasocks/gen_embedded.py b/aos/externals/seasocks/gen_embedded.py
new file mode 100755
index 0000000..d0fd603
--- /dev/null
+++ b/aos/externals/seasocks/gen_embedded.py
@@ -0,0 +1,60 @@
+#!/usr/bin/env python3
+
+# This file is a modified version of the gen_embedded script included in the
+# scripts directory of seasocks (version 1.1.2). It has been modified to
+# recursively find the web files itself, which was originally done by piping
+# in the results from a "find" shell command.
+
+# The embedded files includes only those that are required for the server to run
+# (including 404 files, a default index page, favicon, etc.)
+
+import os, os.path, sys
+
+output = sys.argv[1]
+assert output[0] == '"'
+assert output[-1] == '"'
+output = output[1:-1]
+
+if not os.path.exists(os.path.dirname(output)):
+ os.makedirs(os.path.dirname(output))
+o = open(output, 'w')
+
+web = []
+for root, dirs, files in os.walk("./www_defaults", topdown=False):
+ for name in files:
+ web.append(os.path.join(root, name))
+ for name in dirs:
+ web.append(os.path.join(root, name))
+
+o.write("""
+#include "internal/Embedded.h"
+
+#include <string>
+#include <unordered_map>
+
+namespace {
+
+std::unordered_map<std::string, EmbeddedContent> embedded = {
+""")
+
+for f in web:
+ bytes = open(f, 'rb').read()
+ o.write('{"/%s", {' % os.path.basename(f))
+ o.write('"' + "".join(['\\x%02x' % ord(x) for x in bytes]) + '"')
+ o.write(',%d }},' % len(bytes))
+
+o.write("""
+};
+
+} // namespace
+
+const EmbeddedContent* findEmbeddedContent(const std::string& name) {
+ auto found = embedded.find(name);
+ if (found == embedded.end()) {
+ return NULL;
+ }
+ return &found->second;
+}
+""")
+
+o.close()
diff --git a/aos/externals/seasocks/internal/Config.h b/aos/externals/seasocks/internal/Config.h
new file mode 100644
index 0000000..e99d356
--- /dev/null
+++ b/aos/externals/seasocks/internal/Config.h
@@ -0,0 +1,221 @@
+// Generated by Comran from version 1.1.2 with the autoconf scripts from upstream
+/* src/main/c/internal/Config.h. Generated from Config.h.in by configure. */
+/* src/main/c/internal/Config.h.in. Generated from configure.ac by autoheader. */
+
+/* Define to 1 if you have the <arpa/inet.h> header file. */
+#define HAVE_ARPA_INET_H 1
+
+/* define if the compiler supports basic C++11 syntax */
+/* #undef HAVE_CXX11 */
+
+/* Define to 1 if you have the declaration of `strerror_r', and to 0 if you
+ don't. */
+#define HAVE_DECL_STRERROR_R 1
+
+/* Define to 1 if you have the `dup2' function. */
+#define HAVE_DUP2 1
+
+/* Define to 1 if you have the `eventfd' function. */
+#define HAVE_EVENTFD 1
+
+/* Define to 1 if you have the <fcntl.h> header file. */
+#define HAVE_FCNTL_H 1
+
+/* Define to 1 if you have the `fork' function. */
+#define HAVE_FORK 1
+
+/* Define to 1 if you have the `gethostname' function. */
+#define HAVE_GETHOSTNAME 1
+
+/* Define to 1 if you have the `getopt' function. */
+#define HAVE_GETOPT 1
+
+/* Define to 1 if you have the <getopt.h> header file. */
+#define HAVE_GETOPT_H 1
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#define HAVE_INTTYPES_H 1
+
+/* Define to 1 if you have the <limits.h> header file. */
+#define HAVE_LIMITS_H 1
+
+/* Define to 1 if your system has a GNU libc compatible `malloc' function, and
+ to 0 otherwise. */
+/* #undef HAVE_MALLOC */
+
+/* Define to 1 if you have the <memory.h> header file. */
+#define HAVE_MEMORY_H 1
+
+/* Define to 1 if you have the `memset' function. */
+#define HAVE_MEMSET 1
+
+/* Define to 1 if you have the <netinet/in.h> header file. */
+#define HAVE_NETINET_IN_H 1
+
+/* Define to 1 if the system has the type `ptrdiff_t'. */
+#define HAVE_PTRDIFF_T 1
+
+/* Define to 1 if you have the `rmdir' function. */
+#define HAVE_RMDIR 1
+
+/* Define to 1 if you have the `socket' function. */
+#define HAVE_SOCKET 1
+
+/* Define to 1 if you have the `sqrt' function. */
+#define HAVE_SQRT 1
+
+/* Define to 1 if stdbool.h conforms to C99. */
+#define HAVE_STDBOOL_H 1
+
+/* Define to 1 if you have the <stddef.h> header file. */
+#define HAVE_STDDEF_H 1
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#define HAVE_STDINT_H 1
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define to 1 if you have the `strcasecmp' function. */
+#define HAVE_STRCASECMP 1
+
+/* Define to 1 if you have the `strchr' function. */
+#define HAVE_STRCHR 1
+
+/* Define to 1 if you have the `strdup' function. */
+#define HAVE_STRDUP 1
+
+/* Define to 1 if you have the `strerror' function. */
+#define HAVE_STRERROR 1
+
+/* Define to 1 if you have the `strerror_r' function. */
+#define HAVE_STRERROR_R 1
+
+/* Define to 1 if you have the <strings.h> header file. */
+#define HAVE_STRINGS_H 1
+
+/* Define to 1 if you have the <string.h> header file. */
+#define HAVE_STRING_H 1
+
+/* Define to 1 if you have the `syscall' function. */
+#define HAVE_SYSCALL 1
+
+/* Define to 1 if you have the <sys/ioctl.h> header file. */
+#define HAVE_SYS_IOCTL_H 1
+
+/* Define to 1 if you have the <sys/socket.h> header file. */
+#define HAVE_SYS_SOCKET_H 1
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#define HAVE_SYS_STAT_H 1
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#define HAVE_SYS_TYPES_H 1
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#define HAVE_UNISTD_H 1
+
+/* define if unordered_map supports emplace */
+#define HAVE_UNORDERED_MAP_EMPLACE 1
+
+/* Define to 1 if you have the `vfork' function. */
+#define HAVE_VFORK 1
+
+/* Define to 1 if you have the <vfork.h> header file. */
+/* #undef HAVE_VFORK_H */
+
+/* Define to 1 if `fork' works. */
+#define HAVE_WORKING_FORK 1
+
+/* Define to 1 if `vfork' works. */
+#define HAVE_WORKING_VFORK 1
+
+/* Define to 1 if the system has the type `_Bool'. */
+/* #undef HAVE__BOOL */
+
+/* Define to the address where bug reports for this package should be sent. */
+#define PACKAGE_BUGREPORT "matt@godbolt.org"
+
+/* Define to the full name of this package. */
+#define PACKAGE_NAME "SeaSocks"
+
+/* Define to the full name and version of this package. */
+#define PACKAGE_STRING "SeaSocks 0.1"
+
+/* Define to the one symbol short name of this package. */
+#define PACKAGE_TARNAME "seasocks"
+
+/* Define to the home page for this package. */
+#define PACKAGE_URL "https://github.com/mattgodbolt/seasocks"
+
+/* Define to the version of this package. */
+#define PACKAGE_VERSION "0.1"
+
+/* Define to 1 if you have the ANSI C header files. */
+#define STDC_HEADERS 1
+
+/* Define to 1 if strerror_r returns char *. */
+#define STRERROR_R_CHAR_P 1
+
+/* Define for Solaris 2.5.1 so the uint32_t typedef from <sys/synch.h>,
+ <pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the
+ #define below would cause a syntax error. */
+/* #undef _UINT32_T */
+
+/* Define for Solaris 2.5.1 so the uint64_t typedef from <sys/synch.h>,
+ <pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the
+ #define below would cause a syntax error. */
+/* #undef _UINT64_T */
+
+/* Define for Solaris 2.5.1 so the uint8_t typedef from <sys/synch.h>,
+ <pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the
+ #define below would cause a syntax error. */
+/* #undef _UINT8_T */
+
+/* Define to `__inline__' or `__inline' if that's what the C compiler
+ calls it, or to nothing if 'inline' is not supported under any name. */
+#ifndef __cplusplus
+/* #undef inline */
+#endif
+
+/* Define to rpl_malloc if the replacement function should be used. */
+/* #undef malloc */
+
+/* Define to `int' if <sys/types.h> does not define. */
+/* #undef pid_t */
+
+/* Define to the equivalent of the C99 'restrict' keyword, or to
+ nothing if this is not supported. Do not define if restrict is
+ supported directly. */
+#define restrict __restrict
+/* Work around a bug in Sun C++: it does not support _Restrict or
+ __restrict__, even though the corresponding Sun C compiler ends up with
+ "#define restrict _Restrict" or "#define restrict __restrict__" in the
+ previous line. Perhaps some future version of Sun C++ will work with
+ restrict; if so, hopefully it defines __RESTRICT like Sun C does. */
+#if defined __SUNPRO_CC && !defined __RESTRICT
+# define _Restrict
+# define __restrict__
+#endif
+
+/* Define to `unsigned int' if <sys/types.h> does not define. */
+/* #undef size_t */
+
+/* Define to the type of an unsigned integer type of width exactly 16 bits if
+ such a type exists and the standard includes do not define it. */
+/* #undef uint16_t */
+
+/* Define to the type of an unsigned integer type of width exactly 32 bits if
+ such a type exists and the standard includes do not define it. */
+/* #undef uint32_t */
+
+/* Define to the type of an unsigned integer type of width exactly 64 bits if
+ such a type exists and the standard includes do not define it. */
+/* #undef uint64_t */
+
+/* Define to the type of an unsigned integer type of width exactly 8 bits if
+ such a type exists and the standard includes do not define it. */
+/* #undef uint8_t */
+
+/* Define as `fork' if `vfork' does not work. */
+/* #undef vfork */
diff --git a/aos/linux_code/README.txt b/aos/linux_code/README.txt
new file mode 100644
index 0000000..65feb21
--- /dev/null
+++ b/aos/linux_code/README.txt
@@ -0,0 +1,10 @@
+see ../README.txt for stuff affecting all code
+
+The folder is called linux_code because it mainly deals with code that uses the queue system, which only works under GNU/Linux for a variety of reasons, some fundamental (futexes) and some because nobody bothers to fix them.
+The layout is designed with multiple linux boxes in mind.
+
+The code for the linux box that sends motor outputs etc is in ../prime/.
+
+[NOTES]
+Any code should call aos::Init() (or aos::InitNRT() for processes that don't need to be realtime) before making any calls to any of the aos functions.
+Making calls to any of the aos functions (including aos::Init()) from more than 1 thread per process is not supported, but using fork(2) after some aos functions have been called and then continuing to make aos function calls (without calling one of the exec(3) functions) in both processes is supported.
diff --git a/aos/linux_code/complex_thread_local.cc b/aos/linux_code/complex_thread_local.cc
new file mode 100644
index 0000000..d57323e
--- /dev/null
+++ b/aos/linux_code/complex_thread_local.cc
@@ -0,0 +1,69 @@
+#include "aos/linux_code/complex_thread_local.h"
+
+#include <pthread.h>
+
+#include "aos/common/once.h"
+#include "aos/common/die.h"
+
+#define SIMPLE_CHECK(call) \
+ do { \
+ const int value = call; \
+ if (value != 0) { \
+ PRDie(value, "%s failed", #call); \
+ } \
+ } while (false)
+
+namespace aos {
+namespace {
+
+void ExecuteDestructorList(void *v) {
+ for (const ComplexThreadLocalDestructor *c =
+ static_cast<ComplexThreadLocalDestructor *>(v);
+ c != nullptr; c = c->next) {
+ c->function(c->param);
+ }
+}
+
+pthread_key_t *CreateKey() {
+ static pthread_key_t r;
+ SIMPLE_CHECK(pthread_key_create(&r, ExecuteDestructorList));
+ return &r;
+}
+
+::aos::Once<pthread_key_t> key_once(CreateKey);
+
+} // namespace
+
+void ComplexThreadLocalDestructor::Add() {
+ static_assert(
+ ::std::is_pod<ComplexThreadLocalDestructor>::value,
+ "ComplexThreadLocalDestructor might not be safe to pass through void*");
+ pthread_key_t *const key = key_once.Get();
+
+ next = static_cast<ComplexThreadLocalDestructor *>(pthread_getspecific(*key));
+ SIMPLE_CHECK(pthread_setspecific(*key, this));
+}
+
+void ComplexThreadLocalDestructor::Remove() {
+ pthread_key_t *const key = key_once.Get();
+
+ ComplexThreadLocalDestructor *previous = nullptr;
+ for (ComplexThreadLocalDestructor *c =
+ static_cast<ComplexThreadLocalDestructor *>(
+ pthread_getspecific(*key));
+ c != nullptr; c = c->next) {
+ if (c == this) {
+ // If it's the first one.
+ if (previous == nullptr) {
+ SIMPLE_CHECK(pthread_setspecific(*key, next));
+ } else {
+ previous->next = next;
+ }
+ return;
+ }
+ previous = c;
+ }
+ ::aos::Die("%p is not in the destructor list\n", this);
+}
+
+} // namespace aos
diff --git a/aos/linux_code/complex_thread_local.h b/aos/linux_code/complex_thread_local.h
new file mode 100644
index 0000000..7ada875
--- /dev/null
+++ b/aos/linux_code/complex_thread_local.h
@@ -0,0 +1,131 @@
+#ifndef AOS_LINUX_CODE_COMPLEX_THREAD_LOCAL_H_
+#define AOS_LINUX_CODE_COMPLEX_THREAD_LOCAL_H_
+
+#include <assert.h>
+
+#include <type_traits>
+#include <utility>
+
+namespace aos {
+
+// Instances form a (per-thread) list of destructor functions to call when the
+// thread exits.
+// Only ComplexThreadLocal should use this.
+struct ComplexThreadLocalDestructor {
+ // Adds this to the list of destructors in this thread.
+ void Add();
+ // Removes this from the list of destructors in this thread. ::aos::Dies if it
+ // is not there.
+ void Remove();
+
+ void (*function)(void *);
+ void *param;
+
+ ComplexThreadLocalDestructor *next;
+};
+
+// Handles creating a thread-local (per type) object with non-trivial
+// constructor and/or destructor. It will be correctly destroyed on thread exit.
+//
+// Each thread using an instantiation of this class has its own independent slot
+// for storing a T. An instance of T is not actually constructed until a thread
+// calls Create, after which a pointer to it will be returned from get() etc
+// until after Clear is called.
+//
+// Example usage:
+// class Something {
+// private:
+// class Data {
+// public:
+// Data(const ::std::string &value) : value_(value) {}
+//
+// int DoSomething() {
+// if (cached_result_ == 0) {
+// // Do something expensive with value_ and store it in
+// // cached_result_.
+// }
+// return cached_result_;
+// }
+//
+// private:
+// const ::std::string value_;
+// int cached_result_ = 0;
+// };
+// ComplexThreadLocal<Data> thread_local_;
+// ::std::string a_string_;
+//
+// int DoSomething() {
+// thread_local_.Create(a_string_);
+// return thread_local_->DoSomething();
+// }
+// };
+//
+// The current implementation is based on
+// <http://stackoverflow.com/questions/12049684/gcc-4-7-on-linux-pthreads-nontrivial-thread-local-workaround-using-thread-n>.
+// TODO(brians): Change this to just simple standard C++ thread_local once all
+// of our compilers have support.
+template <typename T>
+class ComplexThreadLocal {
+ public:
+ // Actually creates the object in this thread if there is not one there
+ // already.
+ // args are all perfectly forwarded to the constructor.
+ template <typename... Args>
+ void Create(Args &&... args) {
+ if (initialized) return;
+ new (&storage) T(::std::forward<Args>(args)...);
+ destructor.function = PlacementDelete;
+ destructor.param = &storage;
+ destructor.Add();
+ initialized = true;
+ }
+
+ // Removes the object in this thread (if any), including calling its
+ // destructor.
+ void Clear() {
+ if (!initialized) return;
+ destructor.Remove();
+ PlacementDelete(&storage);
+ initialized = false;
+ }
+
+ // Returns true if there is already an object in this thread.
+ bool created() const { return initialized; }
+
+ // Returns the object currently created in this thread or nullptr.
+ T *operator->() const {
+ return get();
+ }
+ T *get() const {
+ if (initialized) {
+ return static_cast<T *>(static_cast<void *>(&storage));
+ } else {
+ return nullptr;
+ }
+ }
+
+ private:
+ typedef typename ::std::aligned_storage<
+ sizeof(T), ::std::alignment_of<T>::value>::type Storage;
+
+ // Convenient helper for calling a destructor.
+ static void PlacementDelete(void *t) { static_cast<T *>(t)->~T(); }
+
+ // True iff this storage has been initialized.
+ static __thread bool initialized;
+ // Where we actually store the object for this thread (if any).
+ static __thread Storage storage;
+ // The linked list element representing this storage.
+ static __thread ComplexThreadLocalDestructor destructor;
+};
+
+template <typename T>
+__thread bool ComplexThreadLocal<T>::initialized;
+template <typename T>
+__thread typename ComplexThreadLocal<T>::Storage ComplexThreadLocal<T>::storage;
+template <typename T>
+__thread ComplexThreadLocalDestructor ComplexThreadLocal<T>::destructor;
+
+} // namespace aos
+
+#endif // AOS_LINUX_CODE_COMPLEX_THREAD_LOCAL_H_
diff --git a/aos/linux_code/complex_thread_local_test.cc b/aos/linux_code/complex_thread_local_test.cc
new file mode 100644
index 0000000..97f0568
--- /dev/null
+++ b/aos/linux_code/complex_thread_local_test.cc
@@ -0,0 +1,101 @@
+#include "aos/linux_code/complex_thread_local.h"
+
+#include <atomic>
+
+#include "gtest/gtest.h"
+
+#include "aos/common/util/thread.h"
+
+namespace aos {
+namespace testing {
+
+class ComplexThreadLocalTest : public ::testing::Test {
+ protected:
+ struct TraceableObject {
+ TraceableObject(int data = 0) : data(data) { ++constructions; }
+ ~TraceableObject() { ++destructions; }
+
+ static ::std::atomic<int> constructions, destructions;
+
+ int data;
+ };
+ ComplexThreadLocal<TraceableObject> local;
+
+ private:
+ void SetUp() override {
+ local.Clear();
+ EXPECT_EQ(TraceableObject::constructions, TraceableObject::destructions)
+ << "There should be no way to create and destroy different numbers.";
+ TraceableObject::constructions = TraceableObject::destructions = 0;
+ }
+};
+::std::atomic<int> ComplexThreadLocalTest::TraceableObject::constructions;
+::std::atomic<int> ComplexThreadLocalTest::TraceableObject::destructions;
+
+TEST_F(ComplexThreadLocalTest, Basic) {
+ EXPECT_EQ(0, TraceableObject::constructions);
+ EXPECT_EQ(0, TraceableObject::destructions);
+ EXPECT_FALSE(local.created());
+ EXPECT_EQ(nullptr, local.get());
+
+ local.Create(971);
+ EXPECT_EQ(1, TraceableObject::constructions);
+ EXPECT_EQ(0, TraceableObject::destructions);
+ EXPECT_TRUE(local.created());
+ EXPECT_EQ(971, local->data);
+
+ local.Create(254);
+ EXPECT_EQ(1, TraceableObject::constructions);
+ EXPECT_EQ(0, TraceableObject::destructions);
+ EXPECT_TRUE(local.created());
+ EXPECT_EQ(971, local->data);
+
+ local.Clear();
+ EXPECT_EQ(1, TraceableObject::constructions);
+ EXPECT_EQ(1, TraceableObject::destructions);
+ EXPECT_FALSE(local.created());
+ EXPECT_EQ(nullptr, local.get());
+
+ local.Create(973);
+ EXPECT_EQ(2, TraceableObject::constructions);
+ EXPECT_EQ(1, TraceableObject::destructions);
+ EXPECT_TRUE(local.created());
+ EXPECT_EQ(973, local->data);
+}
+
+TEST_F(ComplexThreadLocalTest, AnotherThread) {
+ EXPECT_FALSE(local.created());
+ util::FunctionThread::RunInOtherThread([this]() {
+ EXPECT_FALSE(local.created());
+ local.Create(971);
+ EXPECT_TRUE(local.created());
+ EXPECT_EQ(971, local->data);
+ EXPECT_EQ(1, TraceableObject::constructions);
+ EXPECT_EQ(0, TraceableObject::destructions);
+ });
+ EXPECT_EQ(1, TraceableObject::constructions);
+ EXPECT_EQ(1, TraceableObject::destructions);
+ EXPECT_FALSE(local.created());
+}
+
+TEST_F(ComplexThreadLocalTest, TwoThreads) {
+ util::FunctionThread thread([this](util::FunctionThread *) {
+ local.Create(971);
+ EXPECT_EQ(971, local->data);
+ EXPECT_EQ(0, TraceableObject::destructions);
+ });
+ thread.Start();
+ local.Create(973);
+ EXPECT_EQ(973, local->data);
+ thread.Join();
+ EXPECT_TRUE(local.created());
+ EXPECT_EQ(2, TraceableObject::constructions);
+ EXPECT_EQ(1, TraceableObject::destructions);
+ local.Clear();
+ EXPECT_EQ(2, TraceableObject::constructions);
+ EXPECT_EQ(2, TraceableObject::destructions);
+ EXPECT_FALSE(local.created());
+}
+
+} // namespace testing
+} // namespace aos
diff --git a/aos/linux_code/configuration.cc b/aos/linux_code/configuration.cc
new file mode 100644
index 0000000..e4d5d80
--- /dev/null
+++ b/aos/linux_code/configuration.cc
@@ -0,0 +1,116 @@
+#include "aos/linux_code/configuration.h"
+
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <ifaddrs.h>
+#include <unistd.h>
+
+#include "aos/common/logging/logging.h"
+#include "aos/common/unique_malloc_ptr.h"
+#include "aos/common/once.h"
+
+namespace aos {
+namespace configuration {
+namespace {
+
+// TODO(brians): This shouldn't be necesary for running tests. Provide a way to
+// set the IP address when running tests from the test.
+const char *const kLinuxNetInterface = "eth0";
+const in_addr *DoGetOwnIPAddress() {
+ static const char *kOverrideVariable = "FRC971_IP_OVERRIDE";
+ const char *override_ip = getenv(kOverrideVariable);
+ if (override_ip != NULL) {
+ LOG(INFO, "Override IP is %s\n", override_ip);
+ static in_addr r;
+ if (inet_aton(override_ip, &r) != 0) {
+ return &r;
+ } else {
+ LOG(WARNING, "error parsing %s value '%s'\n",
+ kOverrideVariable, override_ip);
+ }
+ } else {
+ LOG(INFO, "Couldn't get environmental variable.\n");
+ }
+
+ ifaddrs *addrs;
+ if (getifaddrs(&addrs) != 0) {
+ PLOG(FATAL, "getifaddrs(%p) failed", &addrs);
+ }
+ // Smart pointers don't work very well for iterating through a linked list,
+ // but it does do a very nice job of making sure that addrs gets freed.
+ unique_c_ptr<ifaddrs, freeifaddrs> addrs_deleter(addrs);
+
+ for (; addrs != nullptr; addrs = addrs->ifa_next) {
+ // ifa_addr tends to be nullptr on CAN interfaces.
+ if (addrs->ifa_addr != nullptr && addrs->ifa_addr->sa_family == AF_INET) {
+ if (strcmp(kLinuxNetInterface, addrs->ifa_name) == 0) {
+ static const in_addr r =
+ reinterpret_cast<sockaddr_in *>(__builtin_assume_aligned(
+ addrs->ifa_addr, alignof(sockaddr_in)))->sin_addr;
+ return &r;
+ }
+ }
+ }
+ LOG(FATAL, "couldn't find an AF_INET interface named \"%s\"\n",
+ kLinuxNetInterface);
+}
+
+const char *DoGetRootDirectory() {
+ ssize_t size = 0;
+ char *r = NULL;
+ while (true) {
+ if (r != NULL) delete r;
+ size += 256;
+ r = new char[size];
+
+ ssize_t ret = readlink("/proc/self/exe", r, size);
+ if (ret < 0) {
+ if (ret != -1) {
+ LOG(WARNING, "it returned %zd, not -1\n", ret);
+ }
+ PLOG(FATAL, "readlink(\"/proc/self/exe\", %p, %zu) failed", r, size);
+ }
+ if (ret < size) {
+ void *last_slash = memrchr(r, '/', ret);
+ if (last_slash == NULL) {
+ r[ret] = '\0';
+ LOG(FATAL, "couldn't find a '/' in \"%s\"\n", r);
+ }
+ *static_cast<char *>(last_slash) = '\0';
+ LOG(INFO, "got a root dir of \"%s\"\n", r);
+ return r;
+ }
+ }
+}
+
+const char *DoGetLoggingDirectory() {
+ static const char kSuffix[] = "/../../tmp/robot_logs";
+ const char *root = GetRootDirectory();
+ char *r = new char[strlen(root) + sizeof(kSuffix)];
+ strcpy(r, root);
+ strcat(r, kSuffix);
+ return r;
+}
+
+} // namespace
+
+const char *GetRootDirectory() {
+ static aos::Once<const char> once(DoGetRootDirectory);
+ return once.Get();
+}
+
+const char *GetLoggingDirectory() {
+ static aos::Once<const char> once(DoGetLoggingDirectory);
+ return once.Get();
+}
+
+const in_addr &GetOwnIPAddress() {
+ static aos::Once<const in_addr> once(DoGetOwnIPAddress);
+ return *once.Get();
+}
+
+} // namespace configuration
+} // namespace aos
diff --git a/aos/linux_code/configuration.h b/aos/linux_code/configuration.h
new file mode 100644
index 0000000..3568f28
--- /dev/null
+++ b/aos/linux_code/configuration.h
@@ -0,0 +1,31 @@
+#ifndef AOS_LINUX_CODE_CONFIGURATION_H_
+#define AOS_LINUX_CODE_CONFIGURATION_H_
+
+#include <stdint.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+namespace aos {
+
+// Holds global configuration data. All of the functions are safe to call
+// from wherever.
+namespace configuration {
+
+// Returns "our" IP address.
+const in_addr &GetOwnIPAddress();
+
+// Returns the "root directory" for this run. Under linux, this is the
+// directory where the executable is located (from /proc/self/exe)
+// The return value will always be to a static string, so no freeing is
+// necessary.
+const char *GetRootDirectory();
+// Returns the directory where logs get written. Relative to GetRootDirectory().
+// The return value will always be to a static string, so no freeing is
+// necessary.
+const char *GetLoggingDirectory();
+
+} // namespace configuration
+} // namespace aos
+
+#endif // AOS_LINUX_CODE_CONFIGURATION_H_
diff --git a/aos/linux_code/core.cc b/aos/linux_code/core.cc
new file mode 100644
index 0000000..189df1d
--- /dev/null
+++ b/aos/linux_code/core.cc
@@ -0,0 +1,32 @@
+#include <sys/wait.h>
+#include <sys/select.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+
+#include <string>
+
+#include "aos/linux_code/init.h"
+#include "aos/common/util/run_command.h"
+
+// Initializes shared memory. This is the only file that will create the shared
+// memory file if it doesn't already exist (and set everything up).
+//
+// Will also touch the file given as a first argument.
+
+int main(int argc, char **argv) {
+ aos::InitCreate();
+
+ if (argc > 1) {
+ const int result = ::aos::util::RunCommand(
+ (std::string("touch '") + argv[1] + "'").c_str());
+ if (result == -1 || !WIFEXITED(result) || WEXITSTATUS(result) != 0) {
+ fprintf(stderr, "`touch '%s'` failed; result = %x\n", argv[1], result);
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ select(0, NULL, NULL, NULL, NULL); // wait forever
+ aos::Cleanup();
+}
diff --git a/aos/linux_code/dump_rtprio.cc b/aos/linux_code/dump_rtprio.cc
new file mode 100644
index 0000000..cae69f1
--- /dev/null
+++ b/aos/linux_code/dump_rtprio.cc
@@ -0,0 +1,282 @@
+// This is a utility program that prints out realtime priorities for processes
+// on a system. It is useful both because the standard tools don't format that
+// information very well and the roboRIO's busybox ones don't seem to do it at
+// all.
+//
+// The output format is the following comma-separated columns:
+// exe,name,cpumask,policy,nice,priority,tid,pid,ppid,sid,cpu
+
+#include <sched.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <unistd.h>
+
+#include <string>
+
+#include "aos/common/logging/logging.h"
+#include "aos/common/logging/logging_impl.h"
+#include "aos/common/time.h"
+
+namespace {
+
+const char *policy_string(uint32_t policy) {
+ switch (policy) {
+ case SCHED_OTHER:
+ return "OTHER";
+ case SCHED_BATCH:
+ return "BATCH";
+ case SCHED_IDLE:
+ return "IDLE";
+ case SCHED_FIFO:
+ return "FIFO";
+ case SCHED_RR:
+ return "RR";
+#ifdef SCHED_DEADLINE
+ case SCHED_DEADLINE:
+ return "DEADLINE";
+#endif
+ default:
+ return "???";
+ }
+}
+
+::std::string strip_string_prefix(size_t length, ::std::string str) {
+ str = str.substr(length);
+ while (str[0] == ' ' || str[0] == '\t') {
+ str = str.substr(1);
+ }
+ return str.substr(0, str.size() - 1);
+}
+
+int find_pid_max() {
+ int r;
+ FILE *pid_max_file = fopen("/proc/sys/kernel/pid_max", "r");
+ if (pid_max_file == nullptr) {
+ PLOG(FATAL, "fopen(\"/proc/sys/kernel/pid_max\")");
+ }
+ CHECK_EQ(1, fscanf(pid_max_file, "%d", &r));
+ PCHECK(fclose(pid_max_file));
+ return r;
+}
+
+cpu_set_t find_all_cpus() {
+ long nproc = sysconf(_SC_NPROCESSORS_CONF);
+ if (nproc == -1) {
+ PLOG(FATAL, "sysconf(_SC_NPROCESSORS_CONF)");
+ }
+ cpu_set_t r;
+ CPU_ZERO(&r);
+ for (long i = 0; i < nproc; ++i) {
+ CPU_SET(i, &r);
+ }
+ return r;
+}
+
+cpu_set_t find_cpu_mask(int process, bool *not_there) {
+ cpu_set_t r;
+ const int result = sched_getaffinity(process, sizeof(r), &r);
+ if (result == -1 && errno == ESRCH) {
+ *not_there = true;
+ return cpu_set_t();
+ }
+ if (result != 0) {
+ PLOG(FATAL, "sched_getaffinity(%d, %zu, %p)", process, sizeof(r), &r);
+ }
+ return r;
+}
+
+sched_param find_sched_param(int process, bool *not_there) {
+ sched_param r;
+ const int result = sched_getparam(process, &r);
+ if (result == -1 && errno == ESRCH) {
+ *not_there = true;
+ return sched_param();
+ }
+ if (result != 0) {
+ PLOG(FATAL, "sched_getparam(%d)", process);
+ }
+ return r;
+}
+
+int find_scheduler(int process, bool *not_there) {
+ int scheduler = sched_getscheduler(process);
+ if (scheduler == -1 && errno == ESRCH) {
+ *not_there = true;
+ return 0;
+ }
+ if (scheduler == -1) {
+ PLOG(FATAL, "sched_getscheduler(%d)", process);
+ }
+ return scheduler;
+}
+
+::std::string find_exe(int process, bool *not_there) {
+ ::std::string exe_filename = "/proc/" + ::std::to_string(process) + "/exe";
+ char exe_buffer[1024];
+ ssize_t exe_size =
+ readlink(exe_filename.c_str(), exe_buffer, sizeof(exe_buffer));
+ if (exe_size == -1 && errno == ENOENT) {
+ return "ENOENT";
+ } else {
+ if (exe_size == -1 && errno == ESRCH) {
+ *not_there = true;
+ return "";
+ }
+ if (exe_size == -1) {
+ PLOG(FATAL, "readlink(%s, %p, %zu)", exe_filename.c_str(), exe_buffer,
+ sizeof(exe_buffer));
+ }
+ return ::std::string(exe_buffer, exe_size);
+ }
+}
+
+int find_nice_value(int process, bool *not_there) {
+ errno = 0;
+ int nice_value = getpriority(PRIO_PROCESS, process);
+ if (errno == ESRCH) {
+ *not_there = true;
+ return 0;
+ }
+ if (errno != 0) {
+ PLOG(FATAL, "getpriority(PRIO_PROCESS, %d)", process);
+ }
+ return nice_value;
+}
+
+void read_stat(int process, int *ppid, int *sid, bool *not_there) {
+ ::std::string stat_filename = "/proc/" + ::std::to_string(process) + "/stat";
+ FILE *stat = fopen(stat_filename.c_str(), "r");
+ if (stat == nullptr && errno == ENOENT) {
+ *not_there = true;
+ return;
+ }
+ if (stat == nullptr) {
+ PLOG(FATAL, "fopen(%s, \"r\")", stat_filename.c_str());
+ }
+
+ char buffer[2048];
+ if (fgets(buffer, sizeof(buffer), stat) == nullptr) {
+ if (ferror(stat)) {
+ if (errno == ESRCH) {
+ *not_there = true;
+ return;
+ }
+ PLOG(FATAL, "fgets(%p, %zu, %p)", buffer, sizeof(buffer), stat);
+ }
+ }
+
+ int pid = 0;
+
+ int field = 0;
+ size_t field_start = 0;
+ int parens = 0;
+ for (size_t i = 0; i < sizeof(buffer); ++i) {
+ if (buffer[i] == '\0') break;
+ if (buffer[i] == '(') ++parens;
+ if (parens > 0) {
+ if (buffer[i] == ')') --parens;
+ } else if (buffer[i] == ' ') {
+ ::std::string field_string(buffer, field_start, i - field_start);
+ switch (field) {
+ case 0:
+ pid = ::std::stoi(field_string);
+ break;
+ case 3:
+ *ppid = ::std::stoi(field_string);
+ break;
+ case 4:
+ *sid = ::std::stoi(field_string);
+ break;
+ default:
+ break;
+ }
+ ++field;
+ field_start = i + 1;
+ }
+ }
+ PCHECK(fclose(stat));
+
+ if (field < 4) {
+ LOG(FATAL, "couldn't get fields from /proc/%d/stat\n", process);
+ }
+ CHECK_EQ(pid, process);
+}
+
+void read_status(int process, int ppid, int *pgrp, ::std::string *name,
+ bool *not_there) {
+ ::std::string status_filename =
+ "/proc/" + ::std::to_string(process) + "/status";
+ FILE *status = fopen(status_filename.c_str(), "r");
+ if (status == nullptr && errno == ENOENT) {
+ *not_there = true;
+ return;
+ }
+ if (status == nullptr) {
+ PLOG(FATAL, "fopen(%s, \"r\")", status_filename.c_str());
+ }
+
+ int pid = 0, status_ppid = 0;
+ while (true) {
+ char buffer[1024];
+ if (fgets(buffer, sizeof(buffer), status) == nullptr) {
+ if (ferror(status)) {
+ PLOG(FATAL, "fgets(%p, %zu, %p)", buffer, sizeof(buffer), status);
+ } else {
+ break;
+ }
+ }
+ ::std::string line(buffer);
+ if (line.substr(0, 5) == "Name:") {
+ *name = strip_string_prefix(5, line);
+ } else if (line.substr(0, 4) == "Pid:") {
+ pid = ::std::stoi(strip_string_prefix(4, line));
+ } else if (line.substr(0, 5) == "PPid:") {
+ status_ppid = ::std::stoi(strip_string_prefix(5, line));
+ } else if (line.substr(0, 5) == "Tgid:") {
+ *pgrp = ::std::stoi(strip_string_prefix(5, line));
+ }
+ }
+ PCHECK(fclose(status));
+ CHECK_EQ(pid, process);
+ CHECK_EQ(status_ppid, ppid);
+}
+
+} // namespace
+
+int main() {
+ ::aos::logging::Init();
+ ::aos::logging::AddImplementation(
+ new ::aos::logging::StreamLogImplementation(stdout));
+
+ const int pid_max = find_pid_max();
+ const cpu_set_t all_cpus = find_all_cpus();
+
+ for (int i = 0; i < pid_max; ++i) {
+ bool not_there = false;
+
+ const cpu_set_t cpu_mask = find_cpu_mask(i, ¬_there);
+ const sched_param param = find_sched_param(i, ¬_there);
+ const int scheduler = find_scheduler(i, ¬_there);
+ const ::std::string exe = find_exe(i, ¬_there);
+ const int nice_value = find_nice_value(i, ¬_there);
+
+ int ppid = 0, sid = 0;
+ read_stat(i, &ppid, &sid, ¬_there);
+
+ int pgrp = 0;
+ ::std::string name;
+ read_status(i, ppid, &pgrp, &name, ¬_there);
+
+ if (not_there) continue;
+
+ const char *cpu_mask_string =
+ CPU_EQUAL(&cpu_mask, &all_cpus) ? "all" : "???";
+
+ printf("%s,%s,%s,%s,%d,%d,%d,%d,%d,%d\n", exe.c_str(), name.c_str(),
+ cpu_mask_string, policy_string(scheduler), nice_value,
+ param.sched_priority, i, pgrp, ppid, sid);
+ }
+}
diff --git a/aos/linux_code/init.cc b/aos/linux_code/init.cc
new file mode 100644
index 0000000..bf0b92d
--- /dev/null
+++ b/aos/linux_code/init.cc
@@ -0,0 +1,167 @@
+#include "aos/linux_code/init.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <errno.h>
+#include <sched.h>
+#include <sys/resource.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <sys/prctl.h>
+#include <malloc.h>
+
+#include "aos/common/die.h"
+#include "aos/linux_code/logging/linux_logging.h"
+#include "aos/linux_code/ipc_lib/shared_mem.h"
+
+#ifdef TCMALLOC
+namespace FLAG__namespace_do_not_use_directly_use_DECLARE_double_instead {
+extern double FLAGS_tcmalloc_release_rate;
+}
+using FLAG__namespace_do_not_use_directly_use_DECLARE_double_instead::
+ FLAGS_tcmalloc_release_rate;
+#endif
+
+namespace aos {
+namespace logging {
+namespace internal {
+
+// Implemented in aos/linux_code/logging/linux_interface.cc.
+void ReloadThreadName();
+
+} // namespace internal
+} // namespace logging
+namespace {
+
+void SetSoftRLimit(int resource, rlim64_t soft, bool set_for_root) {
+ bool am_root = getuid() == 0;
+ if (set_for_root || !am_root) {
+ struct rlimit64 rlim;
+ if (getrlimit64(resource, &rlim) == -1) {
+ PDie("%s-init: getrlimit64(%d) failed",
+ program_invocation_short_name, resource);
+ }
+ rlim.rlim_cur = soft;
+ rlim.rlim_max = ::std::max(rlim.rlim_max, soft);
+
+ if (setrlimit64(resource, &rlim) == -1) {
+ PDie("%s-init: setrlimit64(%d, {cur=%ju,max=%ju}) failed",
+ program_invocation_short_name, resource, (uintmax_t)rlim.rlim_cur,
+ (uintmax_t)rlim.rlim_max);
+ }
+ }
+}
+
+// Common stuff that needs to happen at the beginning of both the realtime and
+// non-realtime initialization sequences. May be called twice.
+void InitStart() {
+ ::aos::logging::Init();
+ WriteCoreDumps();
+}
+
+void LockAllMemory() {
+ // Allow locking as much as we want into RAM.
+ SetSoftRLimit(RLIMIT_MEMLOCK, RLIM_INFINITY, false);
+
+ InitStart();
+ if (mlockall(MCL_CURRENT | MCL_FUTURE) == -1) {
+ PDie("%s-init: mlockall failed", program_invocation_short_name);
+ }
+
+ // Don't give freed memory back to the OS.
+ CHECK_EQ(1, mallopt(M_TRIM_THRESHOLD, -1));
+ // Don't use mmap for large malloc chunks.
+ CHECK_EQ(1, mallopt(M_MMAP_MAX, 0));
+
+#ifdef TCMALLOC
+ // Tell tcmalloc not to return memory.
+ FLAGS_tcmalloc_release_rate = 0.0;
+#endif
+
+ // Forces the memory pages for all the stack space that we're ever going to
+ // use to be loaded into memory (so it can be locked there).
+ uint8_t data[4096 * 8];
+ // Not 0 because linux might optimize that to a 0-filled page.
+ memset(data, 1, sizeof(data));
+
+ static const size_t kHeapPreallocSize = 512 * 1024;
+ char *const heap_data = static_cast<char *>(malloc(kHeapPreallocSize));
+ memset(heap_data, 1, kHeapPreallocSize);
+ free(heap_data);
+}
+
+const char *const kNoRealtimeEnvironmentVariable = "AOS_NO_REALTIME";
+
+} // namespace
+
+void InitNRT() {
+ InitStart();
+ aos_core_create_shared_mem(false, false);
+ logging::linux_code::Register();
+}
+
+void InitCreate() {
+ InitStart();
+ aos_core_create_shared_mem(true, false);
+ logging::linux_code::Register();
+}
+
+void Init(int relative_priority) {
+ bool realtime = getenv(kNoRealtimeEnvironmentVariable) == nullptr;
+ if (realtime) {
+ LockAllMemory();
+
+ // Only let rt processes run for 3 seconds straight.
+ SetSoftRLimit(RLIMIT_RTTIME, 3000000, true);
+
+ // Allow rt processes up to priority 40.
+ SetSoftRLimit(RLIMIT_RTPRIO, 40, false);
+
+ // Set our process to the appropriate priority.
+ struct sched_param param;
+ param.sched_priority = 30 + relative_priority;
+ if (sched_setscheduler(0, SCHED_FIFO, ¶m) != 0) {
+ PDie("%s-init: setting SCHED_FIFO failed", program_invocation_short_name);
+ }
+ } else {
+ fprintf(stderr, "%s not doing realtime initialization because environment"
+ " variable %s is set\n", program_invocation_short_name,
+ kNoRealtimeEnvironmentVariable);
+ printf("no realtime for %s. see stderr\n", program_invocation_short_name);
+ }
+
+ InitStart();
+ aos_core_create_shared_mem(false, realtime);
+ logging::linux_code::Register();
+}
+
+void Cleanup() {
+ aos_core_free_shared_mem();
+}
+
+void WriteCoreDumps() {
+ // Do create core files of unlimited size.
+ SetSoftRLimit(RLIMIT_CORE, RLIM_INFINITY, true);
+}
+
+void SetCurrentThreadRealtimePriority(int priority) {
+ struct sched_param param;
+ param.sched_priority = priority;
+ if (sched_setscheduler(0, SCHED_FIFO, ¶m) == -1) {
+ PLOG(FATAL, "sched_setscheduler(0, SCHED_FIFO, %d) failed", priority);
+ }
+}
+
+void SetCurrentThreadName(const ::std::string &name) {
+ if (name.size() > 16) {
+ LOG(FATAL, "thread name '%s' too long\n", name.c_str());
+ }
+ LOG(INFO, "this thread is changing to '%s'\n", name.c_str());
+ PCHECK(prctl(PR_SET_NAME, name.c_str()));
+ logging::internal::ReloadThreadName();
+}
+
+} // namespace aos
diff --git a/aos/linux_code/init.h b/aos/linux_code/init.h
new file mode 100644
index 0000000..2e57166
--- /dev/null
+++ b/aos/linux_code/init.h
@@ -0,0 +1,40 @@
+#ifndef AOS_LINUX_CODE_INIT_H_
+#define AOS_LINUX_CODE_INIT_H_
+
+#include <string>
+
+namespace aos {
+
+// In order to use shared memory, one of the Init* functions must be called in
+// exactly 1 thread per process. It is OK to keep going without calling one of
+// them again after fork(2)ing.
+
+// Does the non-realtime parts of the initialization process.
+void InitNRT();
+// Initializes everything, including the realtime stuff.
+// relative_priority adjusts the priority of this process relative to all of the
+// other ones (positive for higher priority).
+void Init(int relative_priority = 0);
+// Same as InitNRT, except will remove an existing shared memory file and create
+// a new one.
+void InitCreate();
+// Cleans up (probably not going to get called very often because few things can
+// exit gracefully).
+void Cleanup();
+
+// Sets up this process to write core dump files.
+// This is called by Init*, but it's here for other files that want this
+// behavior without calling Init*.
+void WriteCoreDumps();
+
+// Sets the current thread's realtime priority.
+void SetCurrentThreadRealtimePriority(int priority);
+
+// Sets the name of the current thread.
+// This will displayed by `top -H`, dump_rtprio, and show up in logs.
+// name can have a maximum of 16 characters.
+void SetCurrentThreadName(const ::std::string &name);
+
+} // namespace aos
+
+#endif // AOS_LINUX_CODE_INIT_H_
diff --git a/aos/linux_code/ipc_lib/aos_sync.cc b/aos/linux_code/ipc_lib/aos_sync.cc
new file mode 100644
index 0000000..1275bbf
--- /dev/null
+++ b/aos/linux_code/ipc_lib/aos_sync.cc
@@ -0,0 +1,440 @@
+#if !AOS_DEBUG
+#define NDEBUG
+#endif
+
+#include "aos/linux_code/ipc_lib/aos_sync.h"
+
+#include <linux/futex.h>
+#include <unistd.h>
+#include <sys/syscall.h>
+#include <errno.h>
+#include <stdint.h>
+#include <limits.h>
+#include <string.h>
+#include <inttypes.h>
+#include <sys/types.h>
+#include <stddef.h>
+#include <assert.h>
+#include <pthread.h>
+#include <sched.h>
+
+#include <algorithm>
+
+#include "aos/common/logging/logging.h"
+#include "aos/common/once.h"
+#include "aos/common/macros.h"
+
+// This code was originally based on <http://www.akkadia.org/drepper/futex.pdf>,
+// but is has since evolved a lot. However, that still has useful information.
+//
+// Finding information about actually using futexes is really REALLY hard, so
+// here's a list of the stuff that I've used:
+// futex(7) has a really high-level overview.
+// <http://locklessinc.com/articles/futex_cheat_sheet/> describes some of the
+// operations in a bit more detail than most places.
+// <http://locklessinc.com/articles/mutex_cv_futex/> is the basis of our
+// implementations (before PI).
+// <http://lwn.net/Articles/360699/> has a nice overview of futexes in late 2009
+// (fairly recent compared to everything else...).
+// <https://www.kernel.org/doc/Documentation/pi-futex.txt>,
+// <https://www.kernel.org/doc/Documentation/futex-requeue-pi.txt>,
+// <https://www.kernel.org/doc/Documentation/robust-futexes.txt>,
+// and <https://www.kernel.org/doc/Documentation/robust-futex-ABI.txt> are all
+// useful references.
+// The kernel source (kernel/futex.c) has some useful comments about what the
+// various operations do (except figuring out which argument goes where in the
+// syscall is still confusing).
+// futex(2) is basically useless except for describing the order of the
+// arguments (it only has high-level descriptions of what some of the
+// operations do, and some of them are wrong in Wheezy).
+// glibc's nptl pthreads implementation is the intended user of most of these
+// things, so it is also a good place to look for examples. However, it is all
+// very hard to read because it supports ~20 different kinds of mutexes and
+// several variations of condition variables, and some of the pieces of code
+// are only written in assembly.
+// set_robust_list(2) is wrong in Wheezy (it doesn't actually take a TID
+// argument).
+//
+// Can't use PRIVATE futex operations because they use the pid (or something) as
+// part of the hash.
+//
+// ThreadSanitizer understands how these mutexes etc work. It appears to be able
+// to figure out the happens-before relationship from the __ATOMIC_SEQ_CST
+// atomic primitives.
+//
+// Remember that EAGAIN and EWOUDBLOCK are the same! (ie if you get EAGAIN from
+// FUTEX_WAIT, the docs call it EWOULDBLOCK...)
+
+// Values for an aos_mutex.futex (kernel-mandated):
+// 0 = unlocked
+// TID = locked, not contended
+// |FUTEX_WAITERS = there are waiters (aka contended)
+//
+// Values for an aos_futex being used directly:
+// 0 = unset
+// 1 = set
+//
+// The value of an aos_condition is just a generation counter.
+
+namespace {
+
+// These sys_futex_* functions are wrappers around syscall(SYS_futex). They each
+// take a specific set of arguments for a given futex operation. They return the
+// result or a negated errno value. -1..-4095 mean errors and not successful
+// results, which is guaranteed by the kernel.
+//
+// They each have optimized versions for ARM EABI (the syscall interface is
+// different for non-EABI ARM, so that is the right thing to test for) that
+// don't go through syscall(2) or errno.
+// These use register variables to get the values in the right registers to
+// actually make the syscall.
+
+// The actual macro that we key off of to use the inline versions or not.
+#define ARM_EABI_INLINE_SYSCALL defined(__ARM_EABI__)
+
+// Used for FUTEX_WAIT, FUTEX_LOCK_PI, and FUTEX_TRYLOCK_PI.
+inline int sys_futex_wait(int op, aos_futex *addr1, int val1,
+ const struct timespec *timeout) {
+#if ARM_EABI_INLINE_SYSCALL
+ register aos_futex *addr1_reg __asm__("r0") = addr1;
+ register int op_reg __asm__("r1") = op;
+ register int val1_reg __asm__("r2") = val1;
+ register const struct timespec *timeout_reg __asm__("r3") = timeout;
+ register int syscall_number __asm__("r7") = SYS_futex;
+ register int result __asm__("r0");
+ __asm__ volatile("swi #0"
+ : "=r"(result)
+ : "r"(addr1_reg), "r"(op_reg), "r"(val1_reg),
+ "r"(timeout_reg), "r"(syscall_number)
+ : "memory");
+ return result;
+#else
+ const int r = syscall(SYS_futex, addr1, op, val1, timeout);
+ if (r == -1) return -errno;
+ return r;
+#endif
+}
+
+inline int sys_futex_wake(aos_futex *addr1, int val1) {
+#if ARM_EABI_INLINE_SYSCALL
+ register aos_futex *addr1_reg __asm__("r0") = addr1;
+ register int op_reg __asm__("r1") = FUTEX_WAKE;
+ register int val1_reg __asm__("r2") = val1;
+ register int syscall_number __asm__("r7") = SYS_futex;
+ register int result __asm__("r0");
+ __asm__ volatile("swi #0"
+ : "=r"(result)
+ : "r"(addr1_reg), "r"(op_reg), "r"(val1_reg),
+ "r"(syscall_number)
+ : "memory");
+ return result;
+#else
+ const int r = syscall(SYS_futex, addr1, FUTEX_WAKE, val1);
+ if (r == -1) return -errno;
+ return r;
+#endif
+}
+
+inline int sys_futex_unlock_pi(aos_futex *addr1) {
+#if ARM_EABI_INLINE_SYSCALL
+ register aos_futex *addr1_reg __asm__("r0") = addr1;
+ register int op_reg __asm__("r1") = FUTEX_UNLOCK_PI;
+ register int syscall_number __asm__("r7") = SYS_futex;
+ register int result __asm__("r0");
+ __asm__ volatile("swi #0"
+ : "=r"(result)
+ : "r"(addr1_reg), "r"(op_reg), "r"(syscall_number)
+ : "memory");
+ return result;
+#else
+ const int r = syscall(SYS_futex, addr1, FUTEX_UNLOCK_PI);
+ if (r == -1) return -errno;
+ return r;
+#endif
+}
+
+// Returns true if it succeeds and false if it fails.
+// This is the same as __sync_bool_compare_and_swap, except it provides an easy
+// place to switch implementations and/or work around bugs.
+inline bool compare_and_swap(aos_futex *f, uint32_t before, uint32_t after) {
+ return __sync_bool_compare_and_swap(f, before, after);
+}
+
+pid_t do_get_tid() {
+ pid_t r = syscall(SYS_gettid);
+ assert(r > 0);
+ return r;
+}
+
+// This gets called by functions before LOG(FATAL)ing with error messages that
+// would be incorrect if the error was caused by a process forking without
+// initialize_in_new_thread getting called in the fork.
+void check_cached_tid(pid_t tid) {
+ pid_t actual = do_get_tid();
+ if (tid != actual) {
+ LOG(FATAL,
+ "task %jd forked into %jd without letting aos_sync know"
+ " so we're not really sure what's going on\n",
+ static_cast<intmax_t>(tid), static_cast<intmax_t>(actual));
+ }
+}
+
+// Starts off at 0 in each new thread (because that's what it gets initialized
+// to in most of them or it gets to reset to 0 after a fork by atfork_child()).
+thread_local pid_t my_tid = 0;
+
+// Gets called before the fork(2) wrapper function returns in the child.
+void atfork_child() {
+ // The next time get_tid() is called, it will set everything up again.
+ my_tid = 0;
+}
+
+void *InstallAtforkHook() {
+ if (pthread_atfork(NULL, NULL, atfork_child) != 0) {
+ PLOG(FATAL, "pthread_atfork(NULL, NULL, %p) failed", atfork_child);
+ }
+ return nullptr;
+}
+
+// This gets called to set everything up in a new thread by get_tid().
+void initialize_in_new_thread();
+
+// Gets the current thread's TID and does all of the 1-time initialization the
+// first time it's called in a given thread.
+inline uint32_t get_tid() {
+ if (__builtin_expect(my_tid == 0, 0)) {
+ initialize_in_new_thread();
+ }
+ static_assert(sizeof(my_tid) <= sizeof(uint32_t), "pid_t is too big");
+ return static_cast<uint32_t>(my_tid);
+}
+
+void initialize_in_new_thread() {
+ // No synchronization necessary in most of this because it's all thread-local!
+
+ my_tid = do_get_tid();
+
+ static ::aos::Once<void> atfork_hook_installed(InstallAtforkHook);
+ atfork_hook_installed.Get();
+}
+
+// Split out separately from mutex_get so condition_wait can call it too.
+inline int mutex_do_get(aos_mutex *m, bool signals_fail,
+ const struct timespec *timeout) {
+ const uint32_t tid = get_tid();
+
+ while (true) {
+ // If the atomic 0->TID transition fails.
+ if (!compare_and_swap(&m->futex, 0, tid)) {
+ // Wait in the kernel, which handles atomically ORing in FUTEX_WAITERS
+ // before actually sleeping.
+ const int ret = sys_futex_wait(FUTEX_LOCK_PI, &m->futex, 1, timeout);
+ if (ret != 0) {
+ if (timeout != NULL && ret == -ETIMEDOUT) {
+ return 3;
+ }
+ if (__builtin_expect(ret == -EINTR, 1)) {
+ if (signals_fail) {
+ return 2;
+ } else {
+ continue;
+ }
+ }
+ if (ret == -EDEADLK) {
+ LOG(FATAL, "multiple lock of %p by %" PRId32 "\n", m, tid);
+ }
+ PELOG(FATAL, -ret, "FUTEX_LOCK_PI(%p(=%" PRIu32 "), 1, %p) failed",
+ &m->futex, __atomic_load_n(&m->futex, __ATOMIC_SEQ_CST), timeout);
+ } else {
+ // The kernel already handled setting the value to our TID (ish).
+ break;
+ }
+ } else {
+ // Fastpath succeeded, so no need to call into the kernel.
+ break;
+ }
+ }
+
+#ifdef AOS_SANITIZER_thread
+ // Help tsan figure out that we're synchronizing on this.
+ __sync_fetch_and_add(&m->futex, 0);
+#endif
+
+ return 0;
+}
+
+// The common implementation for everything that wants to lock a mutex.
+// If signals_fail is false, the function will try again if the wait syscall is
+// interrupted by a signal.
+// timeout can be NULL for no timeout.
+inline int mutex_get(aos_mutex *m, bool signals_fail,
+ const struct timespec *timeout) {
+ get_tid();
+ const int r = mutex_do_get(m, signals_fail, timeout);
+ return r;
+}
+
+// The common implementation for broadcast and signal.
+// number_requeue is the number of waiters to requeue (probably INT_MAX or 0). 1
+// will always be woken.
+void condition_wake(aos_condition *c, aos_mutex * /*m*/, int number_requeue) {
+ // Make it so that anybody just going to sleep won't.
+ // This is where we might accidentally wake more than just 1 waiter with 1
+ // signal():
+ // 1 already sleeping will be woken but n might never actually make it to
+ // sleep in the kernel because of this.
+ __atomic_add_fetch(c, 1, __ATOMIC_SEQ_CST);
+
+ const int ret = sys_futex_wake(
+ c, ::std::min(::std::max(number_requeue, 1), INT_MAX - 4096));
+ if (__builtin_expect(
+ static_cast<unsigned int>(ret) > static_cast<unsigned int>(-4096),
+ 0)) {
+ PELOG(FATAL, -ret, "FUTEX_WAKE(%p, %d) failed", c, INT_MAX - 4096);
+ }
+}
+
+} // namespace
+
+int mutex_lock(aos_mutex *m) {
+ return mutex_get(m, true, NULL);
+}
+int mutex_lock_timeout(aos_mutex *m, const struct timespec *timeout) {
+ return mutex_get(m, true, timeout);
+}
+int mutex_grab(aos_mutex *m) {
+ return mutex_get(m, false, NULL);
+}
+
+void mutex_unlock(aos_mutex *m) {
+ const uint32_t tid = get_tid();
+
+ const uint32_t value = __atomic_load_n(&m->futex, __ATOMIC_SEQ_CST);
+ if (__builtin_expect((value & FUTEX_TID_MASK) != tid, 0)) {
+ check_cached_tid(tid);
+ if ((value & FUTEX_TID_MASK) == 0) {
+ LOG(FATAL, "multiple unlock of aos_mutex %p by %" PRId32 "\n", m, tid);
+ } else {
+ LOG(FATAL, "aos_mutex %p is locked by %" PRId32 ", not %" PRId32 "\n",
+ m, value & FUTEX_TID_MASK, tid);
+ }
+ }
+
+ // If the atomic TID->0 transition fails (ie FUTEX_WAITERS is set),
+ if (!compare_and_swap(&m->futex, tid, 0)) {
+ // The kernel handles everything else.
+ const int ret = sys_futex_unlock_pi(&m->futex);
+ if (ret != 0) {
+ PELOG(FATAL, -ret, "FUTEX_UNLOCK_PI(%p) failed", &m->futex);
+ }
+ } else {
+ // There aren't any waiters, so no need to call into the kernel.
+ }
+}
+
+int mutex_trylock(aos_mutex *m) {
+ // Try an atomic 0->TID transition.
+ uint32_t c = __sync_val_compare_and_swap(&m->futex, 0, get_tid());
+
+ if (c != 0) {
+ // Somebody else had it locked; we failed.
+ return 4;
+ }
+ return 0;
+}
+
+bool mutex_islocked(const aos_mutex *m) {
+ const uint32_t tid = get_tid();
+
+ const uint32_t value = __atomic_load_n(&m->futex, __ATOMIC_RELAXED);
+ return (value & FUTEX_TID_MASK) == tid;
+}
+
+int condition_wait(aos_condition *c, aos_mutex *m) {
+ const uint32_t wait_start = __atomic_load_n(c, __ATOMIC_SEQ_CST);
+
+ mutex_unlock(m);
+
+ while (true) {
+ // Wait in the kernel iff the value of it doesn't change (ie somebody else
+ // does a wake) from before we unlocked the mutex.
+ int ret;
+ ret = sys_futex_wait(FUTEX_WAIT, c, wait_start, nullptr);
+ if (ret != 0) {
+ // If it failed because somebody else did a wake and changed the value
+ // before we actually made it to sleep.
+ if (__builtin_expect(ret == -EAGAIN, 1)) {
+ // Everything is just normal locks
+ // etc, so there's no need to do anything special here.
+
+ // We have to relock it ourself because the kernel didn't do it.
+ const int r = mutex_do_get(m, false, nullptr);
+ assert(__builtin_expect(r == 0 || r == 1, 1));
+ return r;
+ }
+ // Try again if it was because of a signal.
+ if (__builtin_expect(ret == -EINTR, 1)) continue;
+ PELOG(FATAL, -ret, "FUTEX_WAIT(%p, %" PRIu32 ", nullptr) failed",
+ c, wait_start);
+ } else {
+ // We have to take the lock ourself because the kernel won't, but
+ // there's no need for it to be anything special because all waiters
+ // just relock it like usual.
+ const int r = mutex_do_get(m, false, nullptr);
+ assert(__builtin_expect(r == 0 || r == 1, 1));
+ return r;
+ }
+ }
+}
+
+void condition_signal(aos_condition *c, aos_mutex *m) {
+ condition_wake(c, m, 0);
+}
+
+void condition_broadcast(aos_condition *c, aos_mutex *m) {
+ condition_wake(c, m, INT_MAX);
+}
+
+int futex_wait_timeout(aos_futex *m, const struct timespec *timeout) {
+ if (__atomic_load_n(m, __ATOMIC_SEQ_CST) != 0) {
+ return 0;
+ }
+ const int ret = sys_futex_wait(FUTEX_WAIT, m, 0, timeout);
+ if (ret != 0) {
+ if (ret == -EINTR) {
+ return 1;
+ } else if (ret == -ETIMEDOUT) {
+ return 2;
+ } else if (ret != -EWOULDBLOCK) {
+ errno = -ret;
+ return -1;
+ }
+ }
+#ifdef AOS_SANITIZER_thread
+ // Help tsan figure out that we're synchronizing on this.
+ __sync_add_and_fetch(m, 0);
+#endif
+ return 0;
+}
+
+int futex_wait(aos_futex *m) { return futex_wait_timeout(m, NULL); }
+
+int futex_set_value(aos_futex *m, uint32_t value) {
+ __atomic_store_n(m, value, __ATOMIC_SEQ_CST);
+ const int r = sys_futex_wake(m, INT_MAX - 4096);
+ if (__builtin_expect(
+ static_cast<unsigned int>(r) > static_cast<unsigned int>(-4096), 0)) {
+ errno = -r;
+ return -1;
+ } else {
+ return r;
+ }
+}
+
+int futex_set(aos_futex *m) {
+ return futex_set_value(m, 1);
+}
+
+int futex_unset(aos_futex *m) {
+ return !__atomic_exchange_n(m, 0, __ATOMIC_SEQ_CST);
+}
diff --git a/aos/linux_code/ipc_lib/aos_sync.h b/aos/linux_code/ipc_lib/aos_sync.h
new file mode 100644
index 0000000..2628558
--- /dev/null
+++ b/aos/linux_code/ipc_lib/aos_sync.h
@@ -0,0 +1,131 @@
+#ifndef AOS_LINUX_CODE_IPC_LIB_SYNC_H_
+#define AOS_LINUX_CODE_IPC_LIB_SYNC_H_
+
+#include <stdlib.h>
+#include <signal.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif // __cplusplus
+
+// TODO(brians) add client requests to make helgrind useful with this code
+// <http://www.valgrind.org/docs/manual/hg-manual.html#hg-manual.client-requests>
+// and <http://www.valgrind.org/docs/manual/drd-manual.html#drd-manual.clientreqs>
+// list the interesting ones
+
+// Have to remember to align structs containing it (recursively) to sizeof(int).
+// Valid initial values for use with futex_ functions are 0 (unset) and 1 (set).
+// The value should not be changed after multiple processes have started
+// accessing an instance except through the functions declared in this file.
+typedef uint32_t aos_futex __attribute__((aligned(sizeof(int))));
+
+// For use with the condition_ functions.
+// No initialization is necessary.
+typedef aos_futex aos_condition;
+
+// For use with the mutex_ functions.
+// futex must be initialized to 0.
+// The recommended way to initialize one of these is by memset(3)ing the whole
+// thing to 0 or using C++ () initialization to avoid depending on the
+// implementation.
+struct aos_mutex {
+ aos_futex futex;
+};
+
+// The mutex_ functions are designed to be used as mutexes. A mutex can only be
+// unlocked from the same task which originally locked it.
+
+// All of these return 2 if
+// interrupted by a signal, 3 if timed out, or 4 if an optional lock fails. Some
+// of them (obviously) can never return some of those values.
+//
+// One of the highest priority processes blocked on a given mutex will be the
+// one to lock it when it is unlocked.
+int mutex_lock(struct aos_mutex *m) __attribute__((warn_unused_result));
+// Returns 2 if it timed out or 1 if interrupted by a signal.
+int mutex_lock_timeout(struct aos_mutex *m, const struct timespec *timeout)
+ __attribute__((warn_unused_result));
+// Ignores signals (retries until something other than getting a signal
+// happens).
+int mutex_grab(struct aos_mutex *m) __attribute__((warn_unused_result));
+// LOG(FATAL)s for multiple unlocking.
+void mutex_unlock(struct aos_mutex *m);
+// Does not block waiting for the mutex.
+int mutex_trylock(struct aos_mutex *m) __attribute__((warn_unused_result));
+#ifdef __cplusplus
+// Returns whether or not the mutex is locked by this thread.
+// There aren't very many valid uses for this function; the main ones are
+// checking mutexes as they are destroyed to catch problems with that early and
+// stack-based recursive mutex locking.
+bool mutex_islocked(const aos_mutex *m);
+#endif
+
+// The futex_ functions are similar to the mutex_ ones but different.
+// They are designed for signalling when something happens (possibly to
+// multiple listeners). A aos_futex manipulated with them can only be set or
+// unset. Also, they can be set/unset/waited on from any task independently of
+// who did something first and have no priority inversion protection.
+// They return -1 for other error (which will be in errno from futex(2)).
+// They have no spurious wakeups (because everybody always gets woken up).
+//
+// Another name for this kind of synchronization mechanism is a "notification".
+// Python calls it an "event".
+//
+// They are different from the condition_ functions in that they do NOT work
+// correctly as standard condition variables. While it is possible to keep
+// track of the "condition" using the value part of the futex_* functions, the
+// obvious implementation has basically the same race condition that condition
+// variables are designed to prevent between somebody else grabbing the mutex
+// and changing whether it's set or not and the futex_ function changing the
+// futex's value. A futex is effectively a resettable condition variable with
+// the condition being "has it been set"; if you have some other condition (for
+// example messages are available to read on a queue), use the condition_
+// functions or there will be race conditions.
+
+// Wait for the futex to be set. Will return immediately if it's already set.
+// Returns 0 if successful or it was already set, 1 if interrupted by a signal,
+// or -1 with an error in errno.
+int futex_wait(aos_futex *m) __attribute__((warn_unused_result));
+// The same as futex_wait except returns 2 if it times out.
+int futex_wait_timeout(aos_futex *m, const struct timespec *timeout)
+ __attribute__((warn_unused_result));
+
+// Set the futex and wake up anybody waiting on it.
+// Returns the number that were woken or -1 with an error in errno.
+//
+// This will always wake up all waiters at the same time and set the value to 1.
+int futex_set(aos_futex *m);
+// Same as above except lets something other than 1 be used as the final value.
+int futex_set_value(aos_futex *m, aos_futex value);
+// Unsets the futex (sets the value to 0).
+// Returns 0 if it was set before and 1 if it wasn't.
+// Can not fail.
+int futex_unset(aos_futex *m);
+
+// The condition_ functions implement condition variable support. The API is
+// similar to the pthreads api and works the same way. The same m argument must
+// be passed in for all calls to all of the condition_ functions with a given c.
+// They do have the potential for spurious wakeups.
+
+// Wait for the condition variable to be signalled. m will be unlocked
+// atomically with actually starting to wait. m is guaranteed to be locked when
+// this function returns.
+// NOTE: The relocking of m is not atomic with stopping the actual wait and
+// other process(es) may lock (+unlock) the mutex first.
+// Returns 0.
+int condition_wait(aos_condition *c, struct aos_mutex *m)
+ __attribute__((warn_unused_result));
+// If any other processes are condition_waiting on c, wake 1 of them. Does not
+// require m to be locked.
+// NOTE: There is a small chance that this will wake more than just 1 waiter.
+void condition_signal(aos_condition *c, struct aos_mutex *m);
+// Wakes all processes that are condition_waiting on c. Does not require m to be
+// locked.
+void condition_broadcast(aos_condition *c, struct aos_mutex *m);
+
+#ifdef __cplusplus
+}
+#endif // __cplusplus
+
+#endif // AOS_LINUX_CODE_IPC_LIB_SYNC_H_
diff --git a/aos/linux_code/ipc_lib/condition.cc b/aos/linux_code/ipc_lib/condition.cc
new file mode 100644
index 0000000..62ede1c
--- /dev/null
+++ b/aos/linux_code/ipc_lib/condition.cc
@@ -0,0 +1,30 @@
+#include "aos/common/condition.h"
+
+#include <inttypes.h>
+#include <assert.h>
+
+#include "aos/common/type_traits.h"
+#include "aos/common/mutex.h"
+
+namespace aos {
+
+static_assert(shm_ok<Condition>::value,
+ "Condition should work in shared memory");
+
+Condition::Condition(Mutex *m) : impl_(), m_(m) {}
+
+bool Condition::Wait() {
+ const int ret = condition_wait(&impl_, &m_->impl_);
+ assert(__builtin_expect(ret == 0 || ret == 1, 1));
+ return ret == 1;
+}
+
+void Condition::Signal() {
+ condition_signal(&impl_, &m_->impl_);
+}
+
+void Condition::Broadcast() {
+ condition_broadcast(&impl_, &m_->impl_);
+}
+
+} // namespace aos
diff --git a/aos/linux_code/ipc_lib/core_lib.c b/aos/linux_code/ipc_lib/core_lib.c
new file mode 100644
index 0000000..de418a4
--- /dev/null
+++ b/aos/linux_code/ipc_lib/core_lib.c
@@ -0,0 +1,46 @@
+#include "aos/linux_code/ipc_lib/core_lib.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#include "aos/linux_code/ipc_lib/shared_mem.h"
+
+static uint8_t aos_8max(uint8_t l, uint8_t r) {
+ return (l > r) ? l : r;
+}
+void *shm_malloc_aligned(size_t length, uint8_t alignment) {
+ // minimum alignments from
+ // <http://software.intel.com/en-us/articles/data-alignment-when-migrating-to-64-bit-intel-architecture/>
+ if (length <= 1) {
+ alignment = aos_8max(alignment, 1);
+ } else if (length <= 2) {
+ alignment = aos_8max(alignment, 2);
+ } else if (length <= 4) {
+ alignment = aos_8max(alignment, 4);
+ } else if (length <= 8) {
+ alignment = aos_8max(alignment, 8);
+ } else if (length <= 16) {
+ alignment = aos_8max(alignment, 16);
+ } else {
+ alignment = aos_8max(alignment, (length >= 64) ? 64 : 16);
+ }
+
+ void *msg = NULL;
+ aos_shm_core *shm_core = global_core->mem_struct;
+ int result = mutex_grab(&shm_core->msg_alloc_lock);
+ assert(result == 0);
+ shm_core->msg_alloc = (uint8_t *)shm_core->msg_alloc - length;
+ const uint8_t align_extra = (uintptr_t)shm_core->msg_alloc % alignment;
+ shm_core->msg_alloc = (uint8_t *)shm_core->msg_alloc - align_extra;
+ msg = shm_core->msg_alloc;
+ if (msg <= global_core->shared_mem) {
+ fprintf(stderr, "core_lib: RAN OUT OF SHARED MEMORY!!!----------------------------------------------------------\n");
+ printf("if you didn't see the stderr output just then, you should have\n");
+ abort();
+ }
+ //printf("alloc %p\n", msg);
+ mutex_unlock(&shm_core->msg_alloc_lock);
+ return msg;
+}
+
diff --git a/aos/linux_code/ipc_lib/core_lib.h b/aos/linux_code/ipc_lib/core_lib.h
new file mode 100644
index 0000000..ef81c3c
--- /dev/null
+++ b/aos/linux_code/ipc_lib/core_lib.h
@@ -0,0 +1,33 @@
+#ifndef _AOS_CORE_LIB_H_
+#define _AOS_CORE_LIB_H_
+
+#include <stdint.h>
+
+#include "aos/linux_code/ipc_lib/aos_sync.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif // __cplusplus
+
+// alloc_size was taken out of clang in r197866. It appears that it never
+// actually did anything.
+#if defined(__clang__)
+#define attribute_alloc_size(n)
+#else
+#define attribute_alloc_size(n) __attribute__((alloc_size(n)))
+#endif
+
+void *shm_malloc_aligned(size_t length, uint8_t alignment)
+ attribute_alloc_size(1);
+static void *shm_malloc(size_t length) attribute_alloc_size(1);
+static inline void *shm_malloc(size_t length) {
+ return shm_malloc_aligned(length, 0);
+}
+
+#undef attribute_alloc_size
+
+#ifdef __cplusplus
+}
+#endif // __cplusplus
+
+#endif
diff --git a/aos/linux_code/ipc_lib/event.cc b/aos/linux_code/ipc_lib/event.cc
new file mode 100644
index 0000000..e95b45e
--- /dev/null
+++ b/aos/linux_code/ipc_lib/event.cc
@@ -0,0 +1,35 @@
+#include "aos/common/event.h"
+
+#include "aos/common/type_traits.h"
+#include "aos/common/logging/logging.h"
+
+namespace aos {
+
+Event::Event() : impl_(0) {
+ static_assert(shm_ok<Event>::value,
+ "Event is not safe for use in shared memory.");
+}
+
+void Event::Wait() {
+ int ret;
+ do {
+ ret = futex_wait(&impl_);
+ } while (ret == 1);
+ if (ret == 0) return;
+ CHECK_EQ(-1, ret);
+ PLOG(FATAL, "futex_wait(%p) failed", &impl_);
+}
+
+// We're not going to expose the number woken because that's not easily portable
+// to condition variable-based implementations.
+void Event::Set() {
+ if (futex_set(&impl_) == -1) {
+ PLOG(FATAL, "futex_set(%p) failed", &impl_);
+ }
+}
+
+bool Event::Clear() {
+ return !futex_unset(&impl_);
+}
+
+} // namespace aos
diff --git a/aos/linux_code/ipc_lib/ipc_lib.gyp b/aos/linux_code/ipc_lib/ipc_lib.gyp
new file mode 100644
index 0000000..974a9f6
--- /dev/null
+++ b/aos/linux_code/ipc_lib/ipc_lib.gyp
@@ -0,0 +1,111 @@
+{
+ 'targets': [
+ {
+ 'target_name': 'aos_sync',
+ 'type': 'static_library',
+ 'sources': [
+ 'aos_sync.cc',
+ ],
+ 'dependencies': [
+ '<(AOS)/build/aos.gyp:logging_interface',
+ '<(AOS)/common/common.gyp:once',
+ ],
+ },
+ {
+ 'target_name': 'core_lib',
+ 'type': 'static_library',
+ 'sources': [
+ 'core_lib.c',
+ ],
+ 'dependencies': [
+ 'aos_sync',
+ 'shared_mem',
+ ],
+ 'export_dependent_settings': [
+ 'aos_sync',
+ ],
+ },
+ {
+ 'target_name': 'shared_mem',
+ 'type': 'static_library',
+ 'sources': [
+ 'shared_mem.c',
+ ],
+ 'dependencies': [
+ 'aos_sync',
+ '<(AOS)/build/aos.gyp:logging_interface',
+ ],
+ 'export_dependent_settings': [
+ 'aos_sync',
+ ],
+ },
+ {
+ 'target_name': 'queue',
+ 'type': 'static_library',
+ 'sources': [
+ 'queue.cc',
+ ],
+ 'dependencies': [
+ '<(AOS)/common/common.gyp:condition',
+ '<(AOS)/common/common.gyp:mutex',
+ 'core_lib',
+ '<(AOS)/build/aos.gyp:logging_interface',
+ ],
+ 'export_dependent_settings': [
+ '<(AOS)/build/aos.gyp:logging_interface',
+ ],
+ },
+ {
+ 'target_name': 'raw_queue_test',
+ 'type': 'executable',
+ 'sources': [
+ 'raw_queue_test.cc',
+ ],
+ 'dependencies': [
+ '<(EXTERNALS):gtest',
+ 'queue',
+ '<(AOS)/build/aos.gyp:logging',
+ 'core_lib',
+ '<(AOS)/common/common.gyp:queue_testutils',
+ '<(AOS)/common/common.gyp:time',
+ '<(AOS)/common/common.gyp:die',
+ '<(AOS)/common/util/util.gyp:thread',
+ '<(AOS)/common/util/util.gyp:death_test_log_implementation',
+ ],
+ },
+ {
+ 'target_name': 'ipc_stress_test',
+ 'type': 'executable',
+ 'sources': [
+ 'ipc_stress_test.cc',
+ ],
+ 'dependencies': [
+ '<(EXTERNALS):gtest',
+ '<(AOS)/common/common.gyp:time',
+ '<(AOS)/common/common.gyp:queue_testutils',
+ '<(AOS)/common/common.gyp:mutex',
+ 'core_lib',
+ '<(AOS)/common/common.gyp:die',
+ '<(AOS)/common/libc/libc.gyp:dirname',
+ '<(AOS)/common/libc/libc.gyp:aos_strsignal',
+ '<(AOS)/build/aos.gyp:logging',
+ ],
+ 'variables': {
+ 'is_special_test': 1,
+ },
+ },
+ {
+ 'target_name': 'scoped_message_ptr',
+ 'type': 'static_library',
+ 'sources': [
+ #'scoped_message_ptr.h',
+ ],
+ 'dependencies': [
+ 'queue',
+ ],
+ 'export_dependent_settings': [
+ 'queue',
+ ],
+ },
+ ],
+}
diff --git a/aos/linux_code/ipc_lib/ipc_stress_test.cc b/aos/linux_code/ipc_lib/ipc_stress_test.cc
new file mode 100644
index 0000000..880d226
--- /dev/null
+++ b/aos/linux_code/ipc_lib/ipc_stress_test.cc
@@ -0,0 +1,253 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <libgen.h>
+
+#include <string>
+
+#include "aos/common/time.h"
+#include "aos/common/queue_testutils.h"
+#include "aos/common/type_traits.h"
+#include "aos/common/mutex.h"
+#include "aos/linux_code/ipc_lib/core_lib.h"
+#include "aos/common/die.h"
+#include "aos/common/libc/dirname.h"
+#include "aos/common/libc/aos_strsignal.h"
+#include "aos/common/logging/logging.h"
+
+// This runs all of the IPC-related tests in a bunch of parallel processes for a
+// while and makes sure that they don't fail. It also captures the stdout and
+// stderr output from each test run and only prints it out (not interleaved with
+// the output from any other run) if the test fails.
+//
+// They have to be run in separate processes because (in addition to various
+// parts of our code not being thread-safe...) gtest does not like multiple
+// threads.
+//
+// It's written in C++ for performance. We need actual OS-level parallelism for
+// this to work, which means that Ruby's out because it doesn't have good
+// support for doing that. My Python implementation ended up pretty heavily disk
+// IO-bound, which is a bad way to test CPU contention.
+
+namespace aos {
+
+// Each test is represented by the name of the test binary and then any
+// arguments to pass to it.
+// Using --gtest_filter is a bad idea because it seems to result in a lot of
+// swapping which causes everything to be disk-bound (at least for me).
+static const size_t kTestMaxArgs = 10;
+static const char * kTests[][kTestMaxArgs] = {
+ {"queue_test"},
+ {"condition_test"},
+ {"mutex_test"},
+ {"raw_queue_test"},
+};
+static const size_t kTestsLength = sizeof(kTests) / sizeof(kTests[0]);
+// These arguments get inserted before any per-test arguments.
+static const char *kDefaultArgs[] = {
+ "--gtest_repeat=30",
+ "--gtest_shuffle",
+};
+
+// How many test processes to run at a time.
+static const int kTesters = 100;
+// How long to test for.
+static constexpr time::Time kTestTime = time::Time::InSeconds(30);
+
+// The structure that gets put into shared memory and then referenced by all of
+// the child processes.
+struct Shared {
+ Shared(const time::Time &stop_time)
+ : stop_time(stop_time), total_iterations(0) {}
+
+ // Synchronizes access to stdout/stderr to avoid interleaving failure
+ // messages.
+ Mutex output_mutex;
+
+ // When to stop.
+ time::Time stop_time;
+
+ // The total number of iterations. Updated by each child as it finishes.
+ int total_iterations;
+ // Sychronizes writes to total_iterations
+ Mutex total_iterations_mutex;
+
+ const char *path;
+};
+static_assert(shm_ok<Shared>::value,
+ "it's going to get shared between forked processes");
+
+// Gets called after each child forks to run a test.
+void __attribute__((noreturn)) DoRunTest(
+ Shared *shared, const char *(*test)[kTestMaxArgs], int pipes[2]) {
+ if (close(pipes[0]) == -1) {
+ PDie("close(%d) of read end of pipe failed", pipes[0]);
+ }
+ if (close(STDIN_FILENO) == -1) {
+ PDie("close(STDIN_FILENO(=%d)) failed", STDIN_FILENO);
+ }
+ if (dup2(pipes[1], STDOUT_FILENO) == -1) {
+ PDie("dup2(%d, STDOUT_FILENO(=%d)) failed", pipes[1], STDOUT_FILENO);
+ }
+ if (dup2(pipes[1], STDERR_FILENO) == -1) {
+ PDie("dup2(%d, STDERR_FILENO(=%d)) failed", pipes[1], STDERR_FILENO);
+ }
+
+ size_t size = kTestMaxArgs;
+ size_t default_size = sizeof(kDefaultArgs) / sizeof(kDefaultArgs[0]);
+ // There's no chance to free this because we either exec or Die.
+ const char **args = new const char *[size + default_size + 1];
+ // The actual executable to run.
+ ::std::string executable;
+ int i = 0;
+ for (size_t test_i = 0; test_i < size; ++test_i) {
+ const char *c = (*test)[test_i];
+ if (i == 0) {
+ executable = ::std::string(shared->path) + "/" + c;
+ args[0] = executable.c_str();
+ for (const ::std::string &ci : kDefaultArgs) {
+ args[++i] = ci.c_str();
+ }
+ } else {
+ args[i] = c;
+ }
+ ++i;
+ }
+ args[size] = NULL;
+ execv(executable.c_str(), const_cast<char *const *>(args));
+ PDie("execv(%s, %p) failed", executable.c_str(), args);
+}
+
+void DoRun(Shared *shared) {
+ int iterations = 0;
+ // An iterator pointing to a random one of the tests.
+ // We randomize based on PID because otherwise they all end up running the
+ // same test at the same time for the whole test.
+ const char *(*test)[kTestMaxArgs] = &kTests[getpid() % kTestsLength];
+ int pipes[2];
+ while (time::Time::Now() < shared->stop_time) {
+ if (pipe(pipes) == -1) {
+ PDie("pipe(%p) failed", &pipes);
+ }
+ switch (fork()) {
+ case 0: // in runner
+ DoRunTest(shared, test, pipes);
+ case -1:
+ PDie("fork() failed");
+ }
+
+ if (close(pipes[1]) == -1) {
+ PDie("close(%d) of write end of pipe failed", pipes[1]);
+ }
+
+ ::std::string output;
+ char buffer[2048];
+ while (true) {
+ ssize_t ret = read(pipes[0], &buffer, sizeof(buffer));
+ if (ret == 0) { // EOF
+ if (close(pipes[0]) == -1) {
+ PDie("close(%d) of pipe at EOF failed", pipes[0]);
+ }
+ break;
+ } else if (ret == -1) {
+ PDie("read(%d, %p, %zd) failed", pipes[0], &buffer, sizeof(buffer));
+ }
+ output += ::std::string(buffer, ret);
+ }
+
+ int status;
+ while (true) {
+ if (wait(&status) == -1) {
+ if (errno == EINTR) continue;
+ PDie("wait(%p) in child failed", &status);
+ } else {
+ break;
+ }
+ }
+ if (WIFEXITED(status)) {
+ if (WEXITSTATUS(status) != 0) {
+ MutexLocker sync(&shared->output_mutex);
+ fprintf(stderr, "Test %s exited with status %d. output:\n",
+ (*test)[0], WEXITSTATUS(status));
+ fputs(output.c_str(), stderr);
+ }
+ } else if (WIFSIGNALED(status)) {
+ MutexLocker sync(&shared->output_mutex);
+ fprintf(stderr, "Test %s terminated by signal %d: %s.\n", (*test)[0],
+ WTERMSIG(status), aos_strsignal(WTERMSIG(status)));
+ fputs(output.c_str(), stderr);
+ } else {
+ CHECK(WIFSTOPPED(status));
+ Die("Test %s was stopped.\n", (*test)[0]);
+ }
+
+ ++test;
+ if (test == kTests + 1) test = kTests;
+ ++iterations;
+ }
+ {
+ MutexLocker sync(&shared->total_iterations_mutex);
+ shared->total_iterations += iterations;
+ }
+}
+
+void Run(Shared *shared) {
+ switch (fork()) {
+ case 0: // in child
+ DoRun(shared);
+ _exit(EXIT_SUCCESS);
+ case -1:
+ PDie("fork() of child failed");
+ }
+}
+
+int Main(int argc, char **argv) {
+ if (argc < 1) {
+ fputs("need an argument\n", stderr);
+ return EXIT_FAILURE;
+ }
+
+ ::aos::common::testing::GlobalCoreInstance global_core;
+
+ Shared *shared = static_cast<Shared *>(shm_malloc(sizeof(Shared)));
+ new (shared) Shared(time::Time::Now() + kTestTime);
+
+ if (asprintf(const_cast<char **>(&shared->path),
+ "%s/../tests", ::aos::libc::Dirname(argv[0]).c_str()) == -1) {
+ PDie("asprintf failed");
+ }
+
+ for (int i = 0; i < kTesters; ++i) {
+ Run(shared);
+ }
+
+ bool error = false;
+ for (int i = 0; i < kTesters; ++i) {
+ int status;
+ if (wait(&status) == -1) {
+ if (errno == EINTR) {
+ --i;
+ } else {
+ PDie("wait(%p) failed", &status);
+ }
+ }
+ if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
+ error = true;
+ }
+ }
+
+ printf("Ran a total of %d tests.\n", shared->total_iterations);
+ if (error) {
+ printf("A child had a problem during the test.\n");
+ }
+ return error ? EXIT_FAILURE : EXIT_SUCCESS;
+}
+
+} // namespace aos
+
+int main(int argc, char **argv) {
+ return ::aos::Main(argc, argv);
+}
diff --git a/aos/linux_code/ipc_lib/mutex.cc b/aos/linux_code/ipc_lib/mutex.cc
new file mode 100644
index 0000000..c5b8652
--- /dev/null
+++ b/aos/linux_code/ipc_lib/mutex.cc
@@ -0,0 +1,58 @@
+#include "aos/common/mutex.h"
+
+#include <inttypes.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "aos/common/type_traits.h"
+#include "aos/common/logging/logging.h"
+
+namespace aos {
+
+Mutex::Mutex() : impl_() {
+ static_assert(shm_ok<Mutex>::value,
+ "Mutex is not safe for use in shared memory.");
+}
+
+Mutex::~Mutex() {
+ if (__builtin_expect(mutex_islocked(&impl_), false)) {
+ LOG(FATAL, "destroying locked mutex %p (aka %p)\n",
+ this, &impl_);
+ }
+}
+
+// Lock and Unlock use the return values of mutex_lock/mutex_unlock
+// to determine whether the lock/unlock succeeded.
+
+bool Mutex::Lock() {
+ const int ret = mutex_grab(&impl_);
+ if (ret == 0) {
+ return false;
+ } else {
+ LOG(FATAL, "mutex_grab(%p(=%" PRIu32 ")) failed with %d\n",
+ &impl_, impl_.futex, ret);
+ }
+}
+
+void Mutex::Unlock() {
+ mutex_unlock(&impl_);
+}
+
+Mutex::State Mutex::TryLock() {
+ const int ret = mutex_trylock(&impl_);
+ switch (ret) {
+ case 0:
+ return State::kLocked;
+ case 4:
+ return State::kLockFailed;
+ default:
+ LOG(FATAL, "mutex_trylock(%p(=%" PRIu32 ")) failed with %d\n",
+ &impl_, impl_.futex, ret);
+ }
+}
+
+bool Mutex::OwnedBySelf() const {
+ return mutex_islocked(&impl_);
+}
+
+} // namespace aos
diff --git a/aos/linux_code/ipc_lib/queue.cc b/aos/linux_code/ipc_lib/queue.cc
new file mode 100644
index 0000000..81ad51b
--- /dev/null
+++ b/aos/linux_code/ipc_lib/queue.cc
@@ -0,0 +1,567 @@
+#if !AOS_DEBUG
+#define NDEBUG
+#endif
+
+#include "aos/linux_code/ipc_lib/queue.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+
+#include <memory>
+#include <algorithm>
+
+#include "aos/common/type_traits.h"
+#include "aos/linux_code/ipc_lib/core_lib.h"
+
+namespace aos {
+namespace {
+
+static_assert(shm_ok<RawQueue>::value,
+ "RawQueue instances go into shared memory");
+
+const bool kReadDebug = false;
+const bool kWriteDebug = false;
+const bool kRefDebug = false;
+const bool kFetchDebug = false;
+const bool kReadIndexDebug = false;
+
+// The number of extra messages the pool associated with each queue will be able
+// to hold (for readers who are slow about freeing them or who leak one when
+// they get killed).
+const int kExtraMessages = 20;
+
+} // namespace
+
+constexpr Options<RawQueue>::Option RawQueue::kPeek;
+constexpr Options<RawQueue>::Option RawQueue::kFromEnd;
+constexpr Options<RawQueue>::Option RawQueue::kNonBlock;
+constexpr Options<RawQueue>::Option RawQueue::kBlock;
+constexpr Options<RawQueue>::Option RawQueue::kOverride;
+
+// This is what gets stuck in before each queue message in memory. It is always
+// allocated aligned to 8 bytes and its size has to maintain that alignment for
+// the message that follows immediately.
+struct RawQueue::MessageHeader {
+ MessageHeader *next;
+
+ // Gets the message header immediately preceding msg.
+ static MessageHeader *Get(const void *msg) {
+ return reinterpret_cast<MessageHeader *>(__builtin_assume_aligned(
+ static_cast<uint8_t *>(const_cast<void *>(msg)) - sizeof(MessageHeader),
+ alignof(MessageHeader)));
+ }
+
+ int32_t ref_count() const {
+ return __atomic_load_n(&ref_count_, __ATOMIC_RELAXED);
+ }
+ void set_ref_count(int32_t val) {
+ __atomic_store_n(&ref_count_, val, __ATOMIC_RELAXED);
+ }
+
+ void ref_count_sub() {
+ __atomic_sub_fetch(&ref_count_, 1, __ATOMIC_RELAXED);
+ }
+ void ref_count_add() {
+ __atomic_add_fetch(&ref_count_, 1, __ATOMIC_RELAXED);
+ }
+
+ private:
+ // This gets accessed with atomic instructions without any
+ // locks held by various member functions.
+ int32_t ref_count_;
+
+ // Padding to make the total size 8 bytes if we have 4-byte pointers or bump
+ // it to 16 if a pointer is 8 bytes by itself.
+#if __SIZEOF_POINTER__ == 8
+#ifdef __clang__
+ // Clang is smart enough to realize this is unused, but GCC doesn't like the
+ // attribute here...
+ __attribute__((unused))
+#endif
+ char padding[4];
+#elif __SIZEOF_POINTER__ == 4
+ // No padding needed to get 8 byte total size.
+#else
+#error Unknown pointer size.
+#endif
+};
+
+inline int RawQueue::index_add1(int index) {
+ // Doing it this way instead of with % is more efficient on ARM.
+ int r = index + 1;
+ assert(index <= data_length_);
+ if (r == data_length_) {
+ return 0;
+ } else {
+ return r;
+ }
+}
+
+void RawQueue::DecrementMessageReferenceCount(const void *msg) {
+ MessageHeader *header = MessageHeader::Get(msg);
+ header->ref_count_sub();
+ if (kRefDebug) {
+ printf("%p ref dec count: %p count=%d\n", this, msg, header->ref_count());
+ }
+
+ // The only way it should ever be 0 is if we were the last one to decrement,
+ // in which case nobody else should have it around to re-increment it or
+ // anything in the middle, so this is safe to do not atomically with the
+ // decrement.
+ if (header->ref_count() == 0) {
+ DoFreeMessage(msg);
+ } else {
+ assert(header->ref_count() > 0);
+ }
+}
+
+inline void RawQueue::IncrementMessageReferenceCount(const void *msg) const {
+ MessageHeader *const header = MessageHeader::Get(msg);
+ header->ref_count_add();
+ if (kRefDebug) {
+ printf("%p ref inc count: %p\n", this, msg);
+ }
+}
+
+inline void RawQueue::DoFreeMessage(const void *msg) {
+ MessageHeader *header = MessageHeader::Get(msg);
+ if (kRefDebug) {
+ printf("%p ref free to %p: %p\n", this, recycle_, msg);
+ }
+
+ if (__builtin_expect(recycle_ != nullptr, 0)) {
+ void *const new_msg = recycle_->GetMessage();
+ if (new_msg == nullptr) {
+ fprintf(stderr, "queue: couldn't get a message"
+ " for recycle queue %p\n", recycle_);
+ } else {
+ header->ref_count_add();
+ if (!recycle_->WriteMessage(const_cast<void *>(msg), kOverride)) {
+ fprintf(stderr, "queue: %p->WriteMessage(%p, kOverride) failed."
+ " aborting\n", recycle_, msg);
+ printf("see stderr\n");
+ abort();
+ }
+ msg = new_msg;
+ header = MessageHeader::Get(new_msg);
+ }
+ }
+
+ // This works around GCC bug 60272 (fixed in 4.8.3).
+ // new_next should just get replaced with header->next (and the body of the
+ // loop should become empty).
+ // The bug is that the store to new_next after the compare/exchange is
+ // unconditional but it should only be if it fails, which could mean
+ // overwriting what somebody else who preempted us right then changed it to.
+ // TODO(brians): Get rid of this workaround once we get a new enough GCC.
+ MessageHeader *new_next = __atomic_load_n(&free_messages_, __ATOMIC_RELAXED);
+ do {
+ header->next = new_next;
+ } while (__builtin_expect(
+ !__atomic_compare_exchange_n(&free_messages_, &new_next, header, true,
+ __ATOMIC_RELEASE, __ATOMIC_RELAXED),
+ 0));
+}
+
+void *RawQueue::GetMessage() {
+ MessageHeader *header = __atomic_load_n(&free_messages_, __ATOMIC_RELAXED);
+ do {
+ if (__builtin_expect(header == nullptr, 0)) {
+ LOG(FATAL, "overused pool of queue %p (%s)\n", this, name_);
+ }
+ } while (__builtin_expect(
+ !__atomic_compare_exchange_n(&free_messages_, &header, header->next, true,
+ __ATOMIC_ACQ_REL, __ATOMIC_RELAXED),
+ 0));
+ void *msg = reinterpret_cast<uint8_t *>(header + 1);
+ // It might be uninitialized, 0 from a previous use, or 1 from previously
+ // being recycled.
+ header->set_ref_count(1);
+ if (kRefDebug) {
+ printf("%p ref alloc: %p\n", this, msg);
+ }
+ return msg;
+}
+
+RawQueue::RawQueue(const char *name, size_t length, int hash, int queue_length)
+ : readable_(&data_lock_), writable_(&data_lock_) {
+ static_assert(shm_ok<RawQueue::MessageHeader>::value,
+ "the whole point is to stick it in shared memory");
+ static_assert((sizeof(RawQueue::MessageHeader) % 8) == 0,
+ "need to revalidate size/alignent assumptions");
+
+ if (queue_length < 1) {
+ LOG(FATAL, "queue length %d of %s needs to be at least 1\n", queue_length,
+ name_);
+ }
+
+ const size_t name_size = strlen(name) + 1;
+ char *temp = static_cast<char *>(shm_malloc(name_size));
+ memcpy(temp, name, name_size);
+ name_ = temp;
+ length_ = length;
+ hash_ = hash;
+ queue_length_ = queue_length;
+
+ next_ = NULL;
+ recycle_ = NULL;
+
+ if (kFetchDebug) {
+ printf("initializing name=%s, length=%zd, hash=%d, queue_length=%d\n",
+ name, length, hash, queue_length);
+ }
+
+ data_length_ = queue_length + 1;
+ data_ = static_cast<void **>(shm_malloc(sizeof(void *) * data_length_));
+ data_start_ = 0;
+ data_end_ = 0;
+ messages_ = 0;
+
+ msg_length_ = length + sizeof(MessageHeader);
+
+ // Create all of the messages for the free list and stick them on.
+ {
+ MessageHeader *previous = nullptr;
+ for (int i = 0; i < queue_length + kExtraMessages; ++i) {
+ MessageHeader *const message =
+ static_cast<MessageHeader *>(shm_malloc(msg_length_));
+ free_messages_ = message;
+ message->next = previous;
+ previous = message;
+ }
+ }
+
+ readable_waiting_ = false;
+
+ if (kFetchDebug) {
+ printf("made queue %s\n", name);
+ }
+}
+
+RawQueue *RawQueue::Fetch(const char *name, size_t length, int hash,
+ int queue_length) {
+ if (kFetchDebug) {
+ printf("fetching queue %s\n", name);
+ }
+ if (mutex_lock(&global_core->mem_struct->queues.lock) != 0) {
+ LOG(FATAL, "mutex_lock(%p) failed\n",
+ &global_core->mem_struct->queues.lock);
+ }
+ RawQueue *current = static_cast<RawQueue *>(
+ global_core->mem_struct->queues.pointer);
+ if (current != NULL) {
+ while (true) {
+ // If we found a matching queue.
+ if (strcmp(current->name_, name) == 0 && current->length_ == length &&
+ current->hash_ == hash && current->queue_length_ == queue_length) {
+ mutex_unlock(&global_core->mem_struct->queues.lock);
+ return current;
+ } else {
+ if (kFetchDebug) {
+ printf("rejected queue %s strcmp=%d target=%s\n", current->name_,
+ strcmp(current->name_, name), name);
+ }
+ }
+ // If this is the last one.
+ if (current->next_ == NULL) break;
+ current = current->next_;
+ }
+ }
+
+ RawQueue *r = new (shm_malloc(sizeof(RawQueue)))
+ RawQueue(name, length, hash, queue_length);
+ if (current == NULL) { // if we don't already have one
+ global_core->mem_struct->queues.pointer = r;
+ } else {
+ current->next_ = r;
+ }
+
+ mutex_unlock(&global_core->mem_struct->queues.lock);
+ return r;
+}
+
+RawQueue *RawQueue::Fetch(const char *name, size_t length, int hash,
+ int queue_length,
+ int recycle_hash, int recycle_length, RawQueue **recycle) {
+ RawQueue *r = Fetch(name, length, hash, queue_length);
+ r->recycle_ = Fetch(name, length, recycle_hash, recycle_length);
+ if (r == r->recycle_) {
+ fprintf(stderr, "queue: r->recycle_(=%p) == r(=%p)\n", r->recycle_, r);
+ printf("see stderr\n");
+ r->recycle_ = NULL;
+ abort();
+ }
+ *recycle = r->recycle_;
+ return r;
+}
+
+bool RawQueue::DoWriteMessage(void *msg, Options<RawQueue> options) {
+ if (kWriteDebug) {
+ printf("queue: %p->WriteMessage(%p, %x)\n", this, msg, options.printable());
+ }
+ {
+ IPCMutexLocker locker(&data_lock_);
+ CHECK(!locker.owner_died());
+ bool writable_waited = false;
+
+ int new_end;
+ while (true) {
+ new_end = index_add1(data_end_);
+ // If there is room in the queue right now.
+ if (new_end != data_start_) break;
+ if (options & kNonBlock) {
+ if (kWriteDebug) {
+ printf("queue: not blocking on %p. returning false\n", this);
+ }
+ DecrementMessageReferenceCount(msg);
+ return false;
+ } else if (options & kOverride) {
+ if (kWriteDebug) {
+ printf("queue: overriding on %p\n", this);
+ }
+ // Avoid leaking the message that we're going to overwrite.
+ DecrementMessageReferenceCount(data_[data_start_]);
+ data_start_ = index_add1(data_start_);
+ } else { // kBlock
+ assert(options & kBlock);
+ if (kWriteDebug) {
+ printf("queue: going to wait for writable_ of %p\n", this);
+ }
+ CHECK(!writable_.Wait());
+ writable_waited = true;
+ }
+ }
+ data_[data_end_] = msg;
+ ++messages_;
+ data_end_ = new_end;
+
+ if (readable_waiting_) {
+ if (kWriteDebug) {
+ printf("queue: broadcasting to readable_ of %p\n", this);
+ }
+ readable_waiting_ = false;
+ readable_.Broadcast();
+ } else if (kWriteDebug) {
+ printf("queue: skipping broadcast to readable_ of %p\n", this);
+ }
+
+ // If we got a signal on writable_ here and it's still writable, then we
+ // need to signal the next person in line (if any).
+ if (writable_waited && is_writable()) {
+ if (kWriteDebug) {
+ printf("queue: resignalling writable_ of %p\n", this);
+ }
+ writable_.Signal();
+ }
+ }
+ if (kWriteDebug) {
+ printf("queue: write returning true on queue %p\n", this);
+ }
+ return true;
+}
+
+inline void RawQueue::ReadCommonEnd() {
+ if (is_writable()) {
+ if (kReadDebug) {
+ printf("queue: %ssignalling writable_ of %p\n",
+ writable_start_ ? "not " : "", this);
+ }
+ if (!writable_start_) writable_.Signal();
+ }
+}
+
+bool RawQueue::ReadCommonStart(Options<RawQueue> options, int *index) {
+ while (data_start_ == data_end_ || ((index != NULL) && messages_ <= *index)) {
+ if (options & kNonBlock) {
+ if (kReadDebug) {
+ printf("queue: not going to block waiting on %p\n", this);
+ }
+ return false;
+ } else { // kBlock
+ assert(options & kBlock);
+ if (kReadDebug) {
+ printf("queue: going to wait for readable_ of %p\n", this);
+ }
+ readable_waiting_ = true;
+ // Wait for a message to become readable.
+ CHECK(!readable_.Wait());
+ if (kReadDebug) {
+ printf("queue: done waiting for readable_ of %p\n", this);
+ }
+ }
+ }
+ // We have to check down here because we might have unlocked the mutex while
+ // Wait()ing above so this value might have changed.
+ writable_start_ = is_writable();
+ if (kReadDebug) {
+ printf("queue: %p->read(%p) start=%d end=%d writable_start=%d\n",
+ this, index, data_start_, data_end_, writable_start_);
+ }
+ return true;
+}
+
+inline int RawQueue::LastMessageIndex() const {
+ int pos = data_end_ - 1;
+ if (pos < 0) { // If it wrapped around.
+ pos = data_length_ - 1;
+ }
+ return pos;
+}
+
+const void *RawQueue::DoReadMessage(Options<RawQueue> options) {
+ // TODO(brians): Test this function.
+ if (kReadDebug) {
+ printf("queue: %p->ReadMessage(%x)\n", this, options.printable());
+ }
+ void *msg = NULL;
+
+ IPCMutexLocker locker(&data_lock_);
+ CHECK(!locker.owner_died());
+
+ if (!ReadCommonStart(options, nullptr)) {
+ if (kReadDebug) {
+ printf("queue: %p common returned false\n", this);
+ }
+ return NULL;
+ }
+
+ if (options & kFromEnd) {
+ if (options & kPeek) {
+ if (kReadDebug) {
+ printf("queue: %p shortcutting c2: %d\n", this, LastMessageIndex());
+ }
+ msg = data_[LastMessageIndex()];
+ IncrementMessageReferenceCount(msg);
+ } else {
+ while (true) {
+ if (kReadDebug) {
+ printf("queue: %p start of c2\n", this);
+ }
+ // This loop pulls each message out of the buffer.
+ const int pos = data_start_;
+ data_start_ = index_add1(data_start_);
+ // If this is the last one.
+ if (data_start_ == data_end_) {
+ if (kReadDebug) {
+ printf("queue: %p reading from c2: %d\n", this, pos);
+ }
+ msg = data_[pos];
+ break;
+ }
+ // This message is not going to be in the queue any more.
+ DecrementMessageReferenceCount(data_[pos]);
+ }
+ }
+ } else {
+ if (kReadDebug) {
+ printf("queue: %p reading from d2: %d\n", this, data_start_);
+ }
+ msg = data_[data_start_];
+ if (options & kPeek) {
+ IncrementMessageReferenceCount(msg);
+ } else {
+ data_start_ = index_add1(data_start_);
+ }
+ }
+ ReadCommonEnd();
+ if (kReadDebug) {
+ printf("queue: %p read returning %p\n", this, msg);
+ }
+ return msg;
+}
+
+const void *RawQueue::DoReadMessageIndex(Options<RawQueue> options,
+ int *index) {
+ if (kReadDebug) {
+ printf("queue: %p->ReadMessageIndex(%x, %p(*=%d))\n",
+ this, options.printable(), index, *index);
+ }
+ void *msg = NULL;
+
+ IPCMutexLocker locker(&data_lock_);
+ CHECK(!locker.owner_died());
+
+ if (!ReadCommonStart(options, index)) {
+ if (kReadDebug) {
+ printf("queue: %p common returned false\n", this);
+ }
+ return NULL;
+ }
+
+ // TODO(parker): Handle integer wrap on the index.
+
+ if (options & kFromEnd) {
+ if (kReadDebug) {
+ printf("queue: %p reading from c1: %d\n", this, LastMessageIndex());
+ }
+ msg = data_[LastMessageIndex()];
+
+ // We'd skip this if we had kPeek, but kPeek | kFromEnd isn't valid for
+ // reading with an index.
+ *index = messages_;
+ } else {
+ // Where we're going to start reading.
+ int my_start;
+
+ const int unread_messages = messages_ - *index;
+ assert(unread_messages > 0);
+ int current_messages = data_end_ - data_start_;
+ if (current_messages < 0) current_messages += data_length_;
+ if (kReadIndexDebug) {
+ printf("queue: %p start=%d end=%d current=%d\n",
+ this, data_start_, data_end_, current_messages);
+ }
+ assert(current_messages > 0);
+ // If we're behind the available messages.
+ if (unread_messages > current_messages) {
+ // Catch index up to the last available message.
+ *index = messages_ - current_messages;
+ // And that's the one we're going to read.
+ my_start = data_start_;
+ if (kReadIndexDebug) {
+ printf("queue: %p jumping ahead to message %d (have %d) (at %d)\n",
+ this, *index, messages_, data_start_);
+ }
+ } else {
+ // Just start reading at the first available message that we haven't yet
+ // read.
+ my_start = data_end_ - unread_messages;
+ if (kReadIndexDebug) {
+ printf("queue: %p original read from %d\n", this, my_start);
+ }
+ if (data_start_ < data_end_) {
+ assert(my_start >= 0);
+ }
+ if (my_start < 0) my_start += data_length_;
+ }
+
+ if (kReadDebug) {
+ printf("queue: %p reading from d1: %d\n", this, my_start);
+ }
+ // We have to be either after the start or before the end, even if the queue
+ // is wrapped around (should be both if it's not).
+ assert((my_start >= data_start_) || (my_start < data_end_));
+ // More sanity checking.
+ assert((my_start >= 0) && (my_start < data_length_));
+ msg = data_[my_start];
+ if (!(options & kPeek)) ++(*index);
+ }
+ IncrementMessageReferenceCount(msg);
+
+ ReadCommonEnd();
+ return msg;
+}
+
+int RawQueue::FreeMessages() const {
+ int r = 0;
+ MessageHeader *header = free_messages_;
+ while (header != nullptr) {
+ ++r;
+ header = header->next;
+ }
+ return r;
+}
+
+} // namespace aos
diff --git a/aos/linux_code/ipc_lib/queue.h b/aos/linux_code/ipc_lib/queue.h
new file mode 100644
index 0000000..828e8f4
--- /dev/null
+++ b/aos/linux_code/ipc_lib/queue.h
@@ -0,0 +1,228 @@
+#ifndef AOS_LINUX_CODE_IPC_LIB_QUEUE_H_
+#define AOS_LINUX_CODE_IPC_LIB_QUEUE_H_
+
+#include "aos/linux_code/ipc_lib/shared_mem.h"
+#include "aos/common/mutex.h"
+#include "aos/common/condition.h"
+#include "aos/common/util/options.h"
+#include "aos/common/logging/logging.h"
+
+// TODO(brians) add valgrind client requests to the queue and shared_mem_malloc
+// code to make checking for leaks work better
+// <http://www.valgrind.org/docs/manual/mc-manual.html#mc-manual.mempools>
+// describes how
+
+// Any pointers returned from these functions can be safely passed to other
+// processes because they are all shared memory pointers.
+// IMPORTANT: Any message pointer must be passed back in some way
+// (FreeMessage and WriteMessage are common ones) or the
+// application will leak shared memory.
+// NOTE: Taking a message from ReadMessage and then passing it to WriteMessage
+// might work, but it is not guaranteed to.
+
+namespace aos {
+
+// Queues are the primary way to use shared memory. Basic use consists of
+// calling Queue::Fetch and then reading and/or writing messages.
+// Queues (as the name suggests) are a FIFO stack of messages. Each combination
+// of name and type signature will result in a different queue, which means
+// that if you only recompile some code that uses differently sized messages,
+// it will simply use a different queue than the old code.
+class RawQueue {
+ public:
+ // Retrieves (and creates if necessary) a queue. Each combination of name and
+ // signature refers to a completely independent queue.
+ // length is how large each message will be
+ // hash can differentiate multiple otherwise identical queues
+ // queue_length is how many messages the queue will be able to hold
+ // Will never return NULL.
+ static RawQueue *Fetch(const char *name, size_t length, int hash,
+ int queue_length);
+ // Same as above, except sets up the returned queue so that it will put
+ // messages on *recycle when they are freed (after they have been released by
+ // all other readers/writers and are not in the queue).
+ // recycle_queue_length determines how many freed messages will be kept.
+ // Other code can retrieve the 2 queues separately (the recycle queue will
+ // have the same length and hash as the main one). However, any frees made
+ // using a queue with only (name,length,hash,queue_length) before the
+ // recycle queue has been associated with it will not go on to the recycle
+ // queue.
+ // NOTE: calling this function with the same (name,length,hash,queue_length)
+ // but multiple recycle_queue_lengths will result in each freed message being
+ // put onto an undefined one of the recycle queues.
+ // Will never return NULL.
+ static RawQueue *Fetch(const char *name, size_t length, int hash,
+ int queue_length, int recycle_hash,
+ int recycle_queue_length, RawQueue **recycle);
+
+ // Doesn't update the currently read index (the read messages in the queue or
+ // the index). This means the returned message (and any others skipped with
+ // kFromEnd) will be left in the queue.
+ // For reading only.
+ // Not valid for ReadMessageIndex combined with kFromEnd.
+ static constexpr Options<RawQueue>::Option kPeek{0x0001};
+ // Reads the last message in the queue instead of just the next one.
+ // NOTE: This removes all of the messages until the last one from the queue
+ // (which means that nobody else will read them).
+ // For reading only.
+ // Not valid for ReadMessageIndex combined with kPeek.
+ static constexpr Options<RawQueue>::Option kFromEnd{0x0002};
+ // Causes reads to return NULL and writes to fail instead of waiting.
+ // For reading and writing.
+ static constexpr Options<RawQueue>::Option kNonBlock{0x0004};
+ // Causes things to block.
+ // For reading and writing.
+ static constexpr Options<RawQueue>::Option kBlock{0x0008};
+ // Causes writes to overwrite the oldest message in the queue instead of
+ // blocking.
+ // For writing only.
+ static constexpr Options<RawQueue>::Option kOverride{0x0010};
+
+ // Writes a message into the queue.
+ // This function takes ownership of msg.
+ // NOTE: msg must point to a valid message from this queue
+ // Returns true on success. A return value of false means msg has already been
+ // freed.
+ bool WriteMessage(void *msg, Options<RawQueue> options) {
+ static constexpr Options<RawQueue> kWriteFailureOptions =
+ kNonBlock | kBlock | kOverride;
+ if (!options.NoOthersSet(kWriteFailureOptions)) {
+ LOG(FATAL, "illegal write options in %x\n", options.printable());
+ }
+ if (!options.ExactlyOneSet(kWriteFailureOptions)) {
+ LOG(FATAL, "invalid write options %x\n", options.printable());
+ }
+ return DoWriteMessage(msg, options);
+ }
+
+ // Reads a message out of the queue.
+ // The return value will have at least the length of this queue's worth of
+ // valid data where it's pointing to.
+ // The return value is const because other people might be viewing the same
+ // messsage. Do not cast the const away!
+ // IMPORTANT: The return value (if not NULL) must eventually be passed to
+ // FreeMessage.
+ const void *ReadMessage(Options<RawQueue> options) {
+ CheckReadOptions(options);
+ return DoReadMessage(options);
+ }
+ // The same as ReadMessage, except it will never return the
+ // same message twice (when used with the same index argument). However,
+ // may not return some messages that pass through the queue.
+ // *index should start as 0. index does not have to be in shared memory, but
+ // it can be.
+ // Calling with both kPeek and kFromEnd in options isn't valid because that
+ // would mean ignoring index, which would make this function the same as
+ // ReadMessage (which should be used instead).
+ const void *ReadMessageIndex(Options<RawQueue> options, int *index) {
+ CheckReadOptions(options);
+ static constexpr Options<RawQueue> kFromEndAndPeek = kFromEnd | kPeek;
+ if (options.AllSet(kFromEndAndPeek)) {
+ LOG(FATAL, "ReadMessageIndex(kFromEnd | kPeek) is not allowed\n");
+ }
+ return DoReadMessageIndex(options, index);
+ }
+
+ // Retrieves ("allocates") a message that can then be written to the queue.
+ // NOTE: the return value will be completely uninitialized
+ // The return value will have at least the length of this queue's worth of
+ // valid memory where it's pointing to.
+ // Returns NULL for error.
+ // IMPORTANT: The return value (if not NULL) must eventually be passed to
+ // FreeMessage or WriteMessage.
+ void *GetMessage();
+
+ // It is ok to call this method with a NULL msg.
+ void FreeMessage(const void *msg) {
+ if (msg != NULL) DecrementMessageReferenceCount(msg);
+ }
+
+ // UNSAFE! Returns the number of free messages we have. Only safe to use when
+ // only 1 task is using this object (ie in tests).
+ int FreeMessages() const;
+
+ private:
+ struct MessageHeader;
+
+ // The public wrappers around these are inlined and do argument checking.
+ bool DoWriteMessage(void *msg, Options<RawQueue> options);
+ const void *DoReadMessage(Options<RawQueue> options);
+ const void *DoReadMessageIndex(Options<RawQueue> options, int *index);
+ void CheckReadOptions(Options<RawQueue> options) {
+ static constexpr Options<RawQueue> kValidOptions =
+ kPeek | kFromEnd | kNonBlock | kBlock;
+ if (!options.NoOthersSet(kValidOptions)) {
+ LOG(FATAL, "illegal read options in %x\n", options.printable());
+ }
+ static constexpr Options<RawQueue> kBlockChoices = kNonBlock | kBlock;
+ if (!options.ExactlyOneSet(kBlockChoices)) {
+ LOG(FATAL, "invalid read options %x\n", options.printable());
+ }
+ }
+
+ // Adds 1 to the given index and handles wrapping correctly.
+ int index_add1(int index);
+
+ bool is_readable() { return data_end_ != data_start_; }
+ bool is_writable() { return index_add1(data_end_) != data_start_; }
+
+ // These next 4 allow finding the right one.
+ const char *name_;
+ size_t length_;
+ int hash_;
+ int queue_length_;
+ // The next one in the linked list of queues.
+ RawQueue *next_;
+
+ RawQueue *recycle_;
+
+ Mutex data_lock_; // protects operations on data_ etc
+ // Always gets broadcasted to because different readers might have different
+ // ideas of what "readable" means (ie ones using separated indices).
+ Condition readable_;
+ Condition writable_;
+ int data_length_; // max length into data + 1
+ int data_start_; // is an index into data
+ int data_end_; // is an index into data
+ int messages_; // that have passed through
+ void **data_; // array of messages (with headers)
+
+ size_t msg_length_; // sizeof(each message) including the header
+ // A pointer to the first in the linked list of free messages.
+ MessageHeader *free_messages_;
+
+ // Keeps track of if the queue was writable before a read so we can Signal() a
+ // reader if we transition it.
+ bool writable_start_;
+
+ // True iff somebody is currently Wait()ing on readable_.
+ // Set to true by each reader before calling Wait() and set back to false
+ // before the Broadcast().
+ bool readable_waiting_;
+
+ // Actually frees the given message.
+ void DoFreeMessage(const void *msg);
+ // Calls DoFreeMessage if appropriate.
+ void DecrementMessageReferenceCount(const void *msg);
+ // Only does the actual incrementing of the reference count.
+ void IncrementMessageReferenceCount(const void *msg) const;
+
+ // Must be called with data_lock_ locked.
+ // *read_data will be initialized.
+ // Returns with a readable message in data_ or false.
+ bool ReadCommonStart(Options<RawQueue> options, int *index);
+ // Deals with setting/unsetting readable_ and writable_.
+ // Must be called after data_lock_ has been unlocked.
+ // read_data should be the same thing that was passed in to ReadCommonStart.
+ void ReadCommonEnd();
+ // Returns the index of the last message.
+ // Useful for reading with kPeek.
+ int LastMessageIndex() const;
+
+ // Gets called by Fetch when necessary (with placement new).
+ RawQueue(const char *name, size_t length, int hash, int queue_length);
+};
+
+} // namespace aos
+
+#endif // AOS_LINUX_CODE_IPC_LIB_QUEUE_H_
diff --git a/aos/linux_code/ipc_lib/raw_queue_test.cc b/aos/linux_code/ipc_lib/raw_queue_test.cc
new file mode 100644
index 0000000..adb84b9
--- /dev/null
+++ b/aos/linux_code/ipc_lib/raw_queue_test.cc
@@ -0,0 +1,1032 @@
+#include "aos/linux_code/ipc_lib/queue.h"
+
+#include <unistd.h>
+#include <sys/mman.h>
+#include <inttypes.h>
+
+#include <ostream>
+#include <memory>
+#include <map>
+
+#include "gtest/gtest.h"
+
+#include "aos/linux_code/ipc_lib/core_lib.h"
+#include "aos/common/type_traits.h"
+#include "aos/common/queue_testutils.h"
+#include "aos/common/time.h"
+#include "aos/common/logging/logging.h"
+#include "aos/common/die.h"
+#include "aos/common/util/thread.h"
+#include "aos/common/util/options.h"
+#include "aos/common/util/death_test_log_implementation.h"
+
+using ::testing::AssertionResult;
+using ::testing::AssertionSuccess;
+using ::testing::AssertionFailure;
+using ::aos::common::testing::GlobalCoreInstance;
+
+namespace aos {
+namespace testing {
+
+// The same constant from queue.cc. This will have to be updated if that one is.
+const int kExtraMessages = 20;
+
+class RawQueueTest : public ::testing::Test {
+ protected:
+ static const size_t kFailureSize = 400;
+ static char *fatal_failure;
+ private:
+ enum class ResultType : uint8_t {
+ NotCalled,
+ Called,
+ Returned,
+ };
+ const std::string ResultTypeString(volatile const ResultType &result) {
+ switch (result) {
+ case ResultType::Returned:
+ return "Returned";
+ case ResultType::Called:
+ return "Called";
+ case ResultType::NotCalled:
+ return "NotCalled";
+ default:
+ return std::string("unknown(") +
+ ::std::to_string(static_cast<uint8_t>(result)) + ")";
+ }
+ }
+ static_assert(aos::shm_ok<ResultType>::value,
+ "this will get put in shared memory");
+ template<typename T>
+ struct FunctionToCall {
+ FunctionToCall() : result(ResultType::NotCalled), started() {
+ }
+
+ volatile ResultType result;
+ bool expected;
+ void (*function)(T*, char*);
+ T *arg;
+ volatile char failure[kFailureSize];
+ aos_futex started;
+ };
+ template<typename T>
+ static void Hangs_(FunctionToCall<T> *const to_call) {
+ time::SleepFor(time::Time::InSeconds(0.01));
+ ASSERT_EQ(1, futex_set(&to_call->started));
+ to_call->result = ResultType::Called;
+ to_call->function(to_call->arg, const_cast<char *>(to_call->failure));
+ to_call->result = ResultType::Returned;
+ }
+
+ // How long until a function is considered to have hung.
+ static constexpr time::Time kHangTime = time::Time::InSeconds(0.09);
+ // How long to sleep after forking (for debugging).
+ static constexpr time::Time kForkSleep = time::Time::InSeconds(0);
+
+ // Represents a process that has been forked off. The destructor kills the
+ // process and wait(2)s for it.
+ class ForkedProcess {
+ public:
+ ForkedProcess(pid_t pid, aos_futex *done)
+ : pid_(pid), done_(done), exiting_(false) {};
+ ~ForkedProcess() {
+ if (!exiting_) {
+ if (kill(pid_, SIGTERM) == -1) {
+ if (errno == ESRCH) {
+ printf("process %jd was already dead\n",
+ static_cast<intmax_t>(pid_));
+ } else {
+ PLOG(FATAL, "kill(SIGKILL, %jd) failed",
+ static_cast<intmax_t>(pid_));
+ }
+ }
+ }
+ const pid_t ret = wait(NULL);
+ if (ret == -1) {
+ LOG(WARNING, "wait(NULL) failed."
+ " child %jd might still be alive\n",
+ static_cast<intmax_t>(pid_));
+ } else if (ret == 0) {
+ LOG(WARNING, "child %jd wasn't waitable. it might still be alive\n",
+ static_cast<intmax_t>(pid_));
+ } else if (ret != pid_) {
+ LOG(WARNING, "child %d is now confirmed dead"
+ ", but child %jd might still be alive\n",
+ ret, static_cast<intmax_t>(pid_));
+ }
+ }
+
+ enum class JoinResult {
+ Finished, Hung, Error
+ };
+ JoinResult Join(time::Time timeout = kHangTime) {
+ timespec done_timeout = (kForkSleep + timeout).ToTimespec();
+ switch (futex_wait_timeout(done_, &done_timeout)) {
+ case 2:
+ return JoinResult::Hung;
+ case 0:
+ exiting_ = true;
+ return JoinResult::Finished;
+ default:
+ return JoinResult::Error;
+ }
+ }
+
+ private:
+ const pid_t pid_;
+ aos_futex *const done_;
+ // True iff we know that the process is already exiting.
+ bool exiting_;
+ } __attribute__((unused));
+
+ // State for HangsFork and HangsCheck.
+ typedef uint8_t ChildID;
+ static void ReapExitHandler() {
+ for (auto it = children_.begin(); it != children_.end(); ++it) {
+ delete it->second;
+ }
+ }
+ static std::map<ChildID, ForkedProcess *> children_;
+ std::map<ChildID, FunctionToCall<void> *> to_calls_;
+
+ void SetUp() override {
+ ::testing::Test::SetUp();
+
+ SetDieTestMode(true);
+
+ fatal_failure = static_cast<char *>(shm_malloc(sizeof(fatal_failure)));
+ static bool registered = false;
+ if (!registered) {
+ atexit(ReapExitHandler);
+ registered = true;
+ }
+ }
+
+ protected:
+ // function gets called with arg in a forked process.
+ // Leaks shared memory.
+ template<typename T> __attribute__((warn_unused_result))
+ std::unique_ptr<ForkedProcess> ForkExecute(void (*function)(T*), T *arg) {
+ aos_futex *done = static_cast<aos_futex *>(shm_malloc_aligned(
+ sizeof(*done), alignof(aos_futex)));
+ *done = 0;
+ const pid_t pid = fork();
+ switch (pid) {
+ case 0: // child
+ if (kForkSleep != time::Time(0, 0)) {
+ LOG(INFO, "pid %jd sleeping for %ds%dns\n",
+ static_cast<intmax_t>(getpid()),
+ kForkSleep.sec(), kForkSleep.nsec());
+ time::SleepFor(kForkSleep);
+ }
+ ::aos::common::testing::PreventExit();
+ function(arg);
+ CHECK_NE(-1, futex_set(done));
+ exit(EXIT_SUCCESS);
+ case -1: // parent failure
+ PLOG(ERROR, "fork() failed");
+ return std::unique_ptr<ForkedProcess>();
+ default: // parent
+ return std::unique_ptr<ForkedProcess>(new ForkedProcess(pid, done));
+ }
+ }
+
+ // Checks whether or not the given function hangs.
+ // expected is whether to return success or failure if the function hangs
+ // NOTE: There are other reasons for it to return a failure than the function
+ // doing the wrong thing.
+ // Leaks shared memory.
+ template<typename T>
+ AssertionResult Hangs(void (*function)(T*, char*), T *arg, bool expected) {
+ AssertionResult fork_result(HangsFork<T>(function, arg, expected, 0));
+ if (!fork_result) {
+ return fork_result;
+ }
+ return HangsCheck(0);
+ }
+ // Starts the first part of Hangs.
+ // Use HangsCheck to get the result.
+ // Returns whether the fork succeeded or not, NOT whether or not the hang
+ // check succeeded.
+ template<typename T>
+ AssertionResult HangsFork(void (*function)(T*, char *), T *arg,
+ bool expected, ChildID id) {
+ static_assert(aos::shm_ok<FunctionToCall<T>>::value,
+ "this is going into shared memory");
+ FunctionToCall<T> *const to_call =
+ static_cast<FunctionToCall<T> *>(
+ shm_malloc_aligned(sizeof(*to_call), alignof(FunctionToCall<T>)));
+ new (to_call) FunctionToCall<T>();
+ to_call->function = function;
+ to_call->arg = arg;
+ to_call->expected = expected;
+ to_call->failure[0] = '\0';
+ static_cast<char *>(fatal_failure)[0] = '\0';
+ children_[id] = ForkExecute(Hangs_, to_call).release();
+ if (!children_[id]) return AssertionFailure() << "ForkExecute failed";
+ CHECK_EQ(0, futex_wait(&to_call->started));
+ to_calls_[id] = reinterpret_cast<FunctionToCall<void> *>(to_call);
+ return AssertionSuccess();
+ }
+ // Checks whether or not a function hung like it was supposed to.
+ // Use HangsFork first.
+ // NOTE: calls to HangsFork and HangsCheck with the same id argument will
+ // correspond, but they do not nest. Also, id 0 is used by Hangs.
+ // Return value is the same as Hangs.
+ AssertionResult HangsCheck(ChildID id) {
+ std::unique_ptr<ForkedProcess> child(children_[id]);
+ children_.erase(id);
+ const ForkedProcess::JoinResult result = child->Join();
+ if (to_calls_[id]->failure[0] != '\0') {
+ return AssertionFailure() << "function says: "
+ << const_cast<char *>(to_calls_[id]->failure);
+ }
+ if (result == ForkedProcess::JoinResult::Finished) {
+ return !to_calls_[id]->expected ? AssertionSuccess() : (AssertionFailure()
+ << "something happened and the the test only got to "
+ << ResultTypeString(to_calls_[id]->result));
+ } else {
+ if (to_calls_[id]->result == ResultType::Called) {
+ return to_calls_[id]->expected ? AssertionSuccess() :
+ AssertionFailure();
+ } else if (result == ForkedProcess::JoinResult::Error) {
+ return AssertionFailure() << "error joining child";
+ } else {
+ if (to_calls_[id]->result == ResultType::NotCalled) {
+ return AssertionFailure() << "stuff took too long getting started";
+ }
+ return AssertionFailure() << "something weird happened";
+ }
+ }
+ }
+#define EXPECT_HANGS(function, arg) \
+ EXPECT_HANGS_COND(function, arg, true, EXPECT_TRUE)
+#define EXPECT_RETURNS(function, arg) \
+ EXPECT_HANGS_COND(function, arg, false, EXPECT_TRUE)
+#define EXPECT_RETURNS_FAILS(function, arg) \
+ EXPECT_HANGS_COND(function, arg, false, EXPECT_FALSE)
+#define EXPECT_HANGS_COND(function, arg, hangs, cond) do { \
+ cond(Hangs(function, arg, hangs)); \
+ if (fatal_failure[0] != '\0') { \
+ FAIL() << fatal_failure; \
+ } \
+} while (false)
+
+ struct TestMessage {
+ // Some contents because we don't really want to test empty messages.
+ int16_t data;
+ };
+ struct MessageArgs {
+ RawQueue *const queue;
+ Options<RawQueue> flags;
+ int16_t data; // -1 means NULL expected
+ };
+ static void WriteTestMessage(MessageArgs *args, char *failure) {
+ TestMessage *msg = static_cast<TestMessage *>(args->queue->GetMessage());
+ if (msg == NULL) {
+ snprintf(fatal_failure, kFailureSize,
+ "couldn't get_msg from %p", args->queue);
+ return;
+ }
+ msg->data = args->data;
+ if (!args->queue->WriteMessage(msg, args->flags)) {
+ snprintf(failure, kFailureSize, "%p->WriteMessage(%p, %x) failed",
+ args->queue, msg, args->flags.printable());
+ }
+ }
+ static void ReadTestMessage(MessageArgs *args, char *failure) {
+ const TestMessage *msg = static_cast<const TestMessage *>(
+ args->queue->ReadMessage(args->flags));
+ if (msg == NULL) {
+ if (args->data != -1) {
+ snprintf(failure, kFailureSize,
+ "expected data of %" PRId16 " but got NULL message",
+ args->data);
+ }
+ } else {
+ if (args->data != msg->data) {
+ snprintf(failure, kFailureSize,
+ "expected data of %" PRId16 " but got %" PRId16 " instead",
+ args->data, msg->data);
+ }
+ args->queue->FreeMessage(msg);
+ }
+ }
+
+ void PushMessage(RawQueue *queue, uint16_t data) {
+ TestMessage *message = static_cast<TestMessage *>(queue->GetMessage());
+ message->data = data;
+ ASSERT_TRUE(queue->WriteMessage(message, RawQueue::kOverride));
+ }
+
+ private:
+ GlobalCoreInstance my_core;
+};
+
+char *RawQueueTest::fatal_failure;
+std::map<RawQueueTest::ChildID, RawQueueTest::ForkedProcess *>
+ RawQueueTest::children_;
+constexpr time::Time RawQueueTest::kHangTime;
+constexpr time::Time RawQueueTest::kForkSleep;
+
+typedef RawQueueTest RawQueueDeathTest;
+
+TEST_F(RawQueueTest, Reading) {
+ RawQueue *const queue = RawQueue::Fetch("Queue", sizeof(TestMessage), 1, 1);
+ MessageArgs args{queue, RawQueue::kBlock, -1};
+
+ args.flags = RawQueue::kNonBlock;
+ EXPECT_RETURNS(ReadTestMessage, &args);
+ args.flags = RawQueue::kNonBlock | RawQueue::kPeek;
+ EXPECT_RETURNS(ReadTestMessage, &args);
+ args.flags = RawQueue::kBlock;
+ EXPECT_HANGS(ReadTestMessage, &args);
+ args.flags = RawQueue::kPeek | RawQueue::kBlock;
+ EXPECT_HANGS(ReadTestMessage, &args);
+ args.data = 254;
+ args.flags = RawQueue::kBlock;
+ EXPECT_RETURNS(WriteTestMessage, &args);
+ args.flags = RawQueue::kPeek | RawQueue::kBlock;
+ EXPECT_RETURNS(ReadTestMessage, &args);
+ args.flags = RawQueue::kPeek | RawQueue::kBlock;
+ EXPECT_RETURNS(ReadTestMessage, &args);
+ args.flags = RawQueue::kPeek | RawQueue::kNonBlock;
+ EXPECT_RETURNS(ReadTestMessage, &args);
+ args.flags = RawQueue::kBlock;
+ EXPECT_RETURNS(ReadTestMessage, &args);
+ args.flags = RawQueue::kBlock;
+ args.data = -1;
+ EXPECT_HANGS(ReadTestMessage, &args);
+ args.flags = RawQueue::kNonBlock;
+ EXPECT_RETURNS(ReadTestMessage, &args);
+ args.flags = RawQueue::kBlock;
+ args.data = 971;
+ EXPECT_RETURNS_FAILS(ReadTestMessage, &args);
+}
+TEST_F(RawQueueTest, Writing) {
+ RawQueue *const queue = RawQueue::Fetch("Queue", sizeof(TestMessage), 1, 1);
+ MessageArgs args{queue, RawQueue::kBlock, 973};
+
+ args.flags = RawQueue::kBlock;
+ EXPECT_RETURNS(WriteTestMessage, &args);
+ args.flags = RawQueue::kBlock;
+ EXPECT_HANGS(WriteTestMessage, &args);
+ args.flags = RawQueue::kNonBlock;
+ EXPECT_RETURNS_FAILS(WriteTestMessage, &args);
+ args.flags = RawQueue::kNonBlock;
+ EXPECT_RETURNS_FAILS(WriteTestMessage, &args);
+ args.flags = RawQueue::kPeek | RawQueue::kBlock;
+ EXPECT_RETURNS(ReadTestMessage, &args);
+ args.data = 971;
+ args.flags = RawQueue::kOverride;
+ EXPECT_RETURNS(WriteTestMessage, &args);
+ args.flags = RawQueue::kOverride;
+ EXPECT_RETURNS(WriteTestMessage, &args);
+ args.flags = RawQueue::kBlock;
+ EXPECT_RETURNS(ReadTestMessage, &args);
+ args.flags = RawQueue::kNonBlock;
+ EXPECT_RETURNS(WriteTestMessage, &args);
+ args.flags = RawQueue::kBlock;
+ EXPECT_RETURNS(ReadTestMessage, &args);
+ args.flags = RawQueue::kOverride;
+ EXPECT_RETURNS(WriteTestMessage, &args);
+ args.flags = RawQueue::kBlock;
+ EXPECT_RETURNS(ReadTestMessage, &args);
+}
+
+TEST_F(RawQueueTest, MultiRead) {
+ RawQueue *const queue = RawQueue::Fetch("Queue", sizeof(TestMessage), 1, 1);
+ MessageArgs args{queue, RawQueue::kBlock, 1323};
+
+ args.flags = RawQueue::kBlock;
+ EXPECT_RETURNS(WriteTestMessage, &args);
+ args.flags = RawQueue::kBlock;
+ ASSERT_TRUE(HangsFork(ReadTestMessage, &args, true, 1));
+ ASSERT_TRUE(HangsFork(ReadTestMessage, &args, true, 2));
+ AssertionResult one = HangsCheck(1);
+ AssertionResult two = HangsCheck(2);
+ EXPECT_TRUE(one != two) << "'" <<
+ one.failure_message() << "' vs '" << two.failure_message() << "'";
+ // TODO(brians) finish this
+}
+
+// There used to be a bug where reading first without an index and then with an
+// index would crash. This test makes sure that's fixed.
+TEST_F(RawQueueTest, ReadIndexAndNot) {
+ RawQueue *const queue = RawQueue::Fetch("Queue", sizeof(TestMessage), 1, 2);
+
+ // Write a message, read it (with ReadMessage), and then write another
+ // message (before freeing the read one so the queue allocates a distinct
+ // message to use for it).
+ TestMessage *msg = static_cast<TestMessage *>(queue->GetMessage());
+ ASSERT_NE(nullptr, msg);
+ ASSERT_TRUE(queue->WriteMessage(msg, RawQueue::kBlock));
+ const void *read_msg = queue->ReadMessage(RawQueue::kBlock);
+ EXPECT_NE(nullptr, read_msg);
+ msg = static_cast<TestMessage *>(queue->GetMessage());
+ queue->FreeMessage(read_msg);
+ ASSERT_NE(nullptr, msg);
+ ASSERT_TRUE(queue->WriteMessage(msg, RawQueue::kBlock));
+
+ int index = 0;
+ const void *second_read_msg =
+ queue->ReadMessageIndex(RawQueue::kBlock, &index);
+ EXPECT_NE(nullptr, second_read_msg);
+ EXPECT_NE(read_msg, second_read_msg)
+ << "We already took that message out of the queue.";
+}
+
+TEST_F(RawQueueTest, Recycle) {
+ // TODO(brians) basic test of recycle queue
+ // include all of the ways a message can get into the recycle queue
+ RawQueue *recycle_queue = reinterpret_cast<RawQueue *>(23);
+ RawQueue *const queue = RawQueue::Fetch("Queue", sizeof(TestMessage),
+ 1, 2, 2, 2, &recycle_queue);
+ ASSERT_NE(reinterpret_cast<RawQueue *>(23), recycle_queue);
+ MessageArgs args{queue, RawQueue::kBlock, 973},
+ recycle{recycle_queue, RawQueue::kBlock, 973};
+
+ args.flags = RawQueue::kBlock;
+ EXPECT_RETURNS(WriteTestMessage, &args);
+ EXPECT_HANGS(ReadTestMessage, &recycle);
+ args.data = 254;
+ EXPECT_RETURNS(WriteTestMessage, &args);
+ EXPECT_HANGS(ReadTestMessage, &recycle);
+ args.data = 971;
+ args.flags = RawQueue::kOverride;
+ EXPECT_RETURNS(WriteTestMessage, &args);
+ recycle.flags = RawQueue::kBlock;
+ EXPECT_RETURNS(ReadTestMessage, &recycle);
+
+ EXPECT_HANGS(ReadTestMessage, &recycle);
+
+ TestMessage *msg = static_cast<TestMessage *>(queue->GetMessage());
+ ASSERT_TRUE(msg != NULL);
+ msg->data = 341;
+ queue->FreeMessage(msg);
+ recycle.data = 341;
+ EXPECT_RETURNS(ReadTestMessage, &recycle);
+
+ EXPECT_HANGS(ReadTestMessage, &recycle);
+
+ args.data = 254;
+ args.flags = RawQueue::kPeek | RawQueue::kBlock;
+ EXPECT_RETURNS(ReadTestMessage, &args);
+ recycle.flags = RawQueue::kBlock;
+ EXPECT_HANGS(ReadTestMessage, &recycle);
+ args.flags = RawQueue::kBlock;
+ EXPECT_RETURNS(ReadTestMessage, &args);
+ recycle.data = 254;
+ EXPECT_RETURNS(ReadTestMessage, &recycle);
+}
+
+// Makes sure that when a message doesn't get written with kNonBlock it does get
+// freed.
+TEST_F(RawQueueTest, NonBlockFailFree) {
+ RawQueue *const queue = RawQueue::Fetch("Queue", sizeof(TestMessage), 1, 1);
+
+ void *message1 = queue->GetMessage();
+ void *message2 = queue->GetMessage();
+ ASSERT_TRUE(queue->WriteMessage(message1, RawQueue::kNonBlock));
+ ASSERT_FALSE(queue->WriteMessage(message2, RawQueue::kNonBlock));
+ EXPECT_EQ(message2, queue->GetMessage());
+}
+
+// All of the tests from here down are designed to test every branch to
+// make sure it does what it's supposed to. They are generally pretty repetitive
+// and boring, and some of them may duplicate other tests above, but these ones
+// make it a lot easier to figure out what's wrong with bugs not related to race
+// conditions.
+
+TEST_F(RawQueueTest, ReadIndexNotFull) {
+ RawQueue *const queue = RawQueue::Fetch("Queue", sizeof(TestMessage), 1, 2);
+ const TestMessage *message, *peek_message;
+
+ EXPECT_EQ(0, kExtraMessages + 2 - queue->FreeMessages());
+ PushMessage(queue, 971);
+ EXPECT_EQ(1, kExtraMessages + 2 - queue->FreeMessages());
+
+ int index = 0;
+ peek_message = static_cast<const TestMessage *>(
+ queue->ReadMessageIndex(RawQueue::kNonBlock | RawQueue::kPeek, &index));
+ message = static_cast<const TestMessage *>(
+ queue->ReadMessageIndex(RawQueue::kNonBlock, &index));
+ ASSERT_NE(nullptr, message);
+ EXPECT_EQ(message, peek_message);
+ EXPECT_EQ(971, message->data);
+ EXPECT_EQ(1, index);
+ queue->FreeMessage(message);
+ queue->FreeMessage(peek_message);
+
+ PushMessage(queue, 1768);
+ EXPECT_EQ(2, kExtraMessages + 2 - queue->FreeMessages());
+ peek_message = static_cast<const TestMessage *>(
+ queue->ReadMessageIndex(RawQueue::kNonBlock | RawQueue::kPeek, &index));
+ message = static_cast<const TestMessage *>(
+ queue->ReadMessageIndex(RawQueue::kNonBlock, &index));
+ ASSERT_NE(nullptr, message);
+ EXPECT_EQ(message, peek_message);
+ EXPECT_EQ(1768, message->data);
+ EXPECT_EQ(2, index);
+ queue->FreeMessage(message);
+ queue->FreeMessage(peek_message);
+
+ PushMessage(queue, 254);
+ peek_message = static_cast<const TestMessage *>(
+ queue->ReadMessageIndex(RawQueue::kNonBlock | RawQueue::kPeek, &index));
+ message = static_cast<const TestMessage *>(
+ queue->ReadMessageIndex(RawQueue::kNonBlock, &index));
+ ASSERT_NE(nullptr, message);
+ EXPECT_EQ(message, peek_message);
+ EXPECT_EQ(254, message->data);
+ EXPECT_EQ(3, index);
+ queue->FreeMessage(message);
+ queue->FreeMessage(peek_message);
+
+ index = 0;
+ peek_message = static_cast<const TestMessage *>(queue->ReadMessage(
+ RawQueue::kNonBlock | RawQueue::kPeek | RawQueue::kFromEnd));
+ message = static_cast<const TestMessage *>(queue->ReadMessageIndex(
+ RawQueue::kNonBlock | RawQueue::kFromEnd, &index));
+ ASSERT_NE(nullptr, message);
+ EXPECT_EQ(message, peek_message);
+ EXPECT_EQ(254, message->data);
+ EXPECT_EQ(3, index);
+ queue->FreeMessage(message);
+ queue->FreeMessage(peek_message);
+
+ EXPECT_EQ(2, kExtraMessages + 2 - queue->FreeMessages());
+}
+
+TEST_F(RawQueueTest, ReadIndexNotBehind) {
+ RawQueue *const queue = RawQueue::Fetch("Queue", sizeof(TestMessage), 1, 2);
+ const TestMessage *message, *peek_message;
+
+ EXPECT_EQ(0, kExtraMessages + 2 - queue->FreeMessages());
+ PushMessage(queue, 971);
+ EXPECT_EQ(1, kExtraMessages + 2 - queue->FreeMessages());
+ PushMessage(queue, 1768);
+ EXPECT_EQ(2, kExtraMessages + 2 - queue->FreeMessages());
+
+ int index = 0;
+ peek_message = static_cast<const TestMessage *>(
+ queue->ReadMessageIndex(RawQueue::kNonBlock | RawQueue::kPeek, &index));
+ message = static_cast<const TestMessage *>(
+ queue->ReadMessageIndex(RawQueue::kNonBlock, &index));
+ ASSERT_NE(nullptr, message);
+ EXPECT_EQ(message, peek_message);
+ EXPECT_EQ(971, message->data);
+ EXPECT_EQ(1, index);
+ queue->FreeMessage(message);
+ queue->FreeMessage(peek_message);
+
+ index = 0;
+ peek_message = static_cast<const TestMessage *>(queue->ReadMessage(
+ RawQueue::kNonBlock | RawQueue::kPeek | RawQueue::kFromEnd));
+ message = static_cast<const TestMessage *>(queue->ReadMessageIndex(
+ RawQueue::kNonBlock | RawQueue::kFromEnd, &index));
+ ASSERT_NE(nullptr, message);
+ EXPECT_EQ(message, peek_message);
+ EXPECT_EQ(1768, message->data);
+ EXPECT_EQ(2, index);
+ queue->FreeMessage(message);
+}
+
+TEST_F(RawQueueTest, ReadIndexLittleBehindNotFull) {
+ RawQueue *const queue = RawQueue::Fetch("Queue", sizeof(TestMessage), 1, 2);
+ const TestMessage *message, *peek_message;
+
+ PushMessage(queue, 971);
+ PushMessage(queue, 1768);
+ ASSERT_NE(nullptr, queue->ReadMessage(RawQueue::kNonBlock));
+
+ int index = 0;
+
+ peek_message = static_cast<const TestMessage *>(
+ queue->ReadMessageIndex(RawQueue::kNonBlock | RawQueue::kPeek, &index));
+ message = static_cast<const TestMessage *>(
+ queue->ReadMessageIndex(RawQueue::kNonBlock, &index));
+ ASSERT_NE(nullptr, message);
+ EXPECT_EQ(message, peek_message);
+ EXPECT_EQ(1768, message->data);
+ EXPECT_EQ(2, index);
+ queue->FreeMessage(message);
+ queue->FreeMessage(peek_message);
+
+ index = 0;
+ peek_message = static_cast<const TestMessage *>(queue->ReadMessage(
+ RawQueue::kNonBlock | RawQueue::kPeek | RawQueue::kFromEnd));
+ message = static_cast<const TestMessage *>(queue->ReadMessageIndex(
+ RawQueue::kNonBlock | RawQueue::kFromEnd, &index));
+ ASSERT_NE(nullptr, message);
+ EXPECT_EQ(message, peek_message);
+ EXPECT_EQ(1768, message->data);
+ EXPECT_EQ(2, index);
+ queue->FreeMessage(message);
+}
+
+TEST_F(RawQueueTest, ReadIndexMoreBehind) {
+ RawQueue *const queue = RawQueue::Fetch("Queue", sizeof(TestMessage), 1, 2);
+ const TestMessage *message, *peek_message;
+
+ PushMessage(queue, 971);
+ PushMessage(queue, 1768);
+ ASSERT_NE(nullptr, queue->ReadMessage(RawQueue::kNonBlock));
+ PushMessage(queue, 254);
+
+ int index = 0;
+
+ peek_message = static_cast<const TestMessage *>(
+ queue->ReadMessageIndex(RawQueue::kNonBlock | RawQueue::kPeek, &index));
+ message = static_cast<const TestMessage *>(
+ queue->ReadMessageIndex(RawQueue::kNonBlock, &index));
+ ASSERT_NE(nullptr, message);
+ EXPECT_EQ(message, peek_message);
+ EXPECT_EQ(1768, message->data);
+ EXPECT_EQ(2, index);
+ queue->FreeMessage(message);
+ queue->FreeMessage(peek_message);
+
+ peek_message = static_cast<const TestMessage *>(
+ queue->ReadMessageIndex(RawQueue::kNonBlock | RawQueue::kPeek, &index));
+ message = static_cast<const TestMessage *>(
+ queue->ReadMessageIndex(RawQueue::kNonBlock, &index));
+ ASSERT_NE(nullptr, message);
+ EXPECT_EQ(message, peek_message);
+ EXPECT_EQ(254, message->data);
+ EXPECT_EQ(3, index);
+ queue->FreeMessage(message);
+ queue->FreeMessage(peek_message);
+
+ index = 0;
+ peek_message = static_cast<const TestMessage *>(queue->ReadMessage(
+ RawQueue::kNonBlock | RawQueue::kPeek | RawQueue::kFromEnd));
+ message = static_cast<const TestMessage *>(queue->ReadMessageIndex(
+ RawQueue::kNonBlock | RawQueue::kFromEnd, &index));
+ ASSERT_NE(nullptr, message);
+ EXPECT_EQ(message, peek_message);
+ EXPECT_EQ(254, message->data);
+ EXPECT_EQ(3, index);
+ queue->FreeMessage(message);
+}
+
+TEST_F(RawQueueTest, ReadIndexMoreBehindNotFull) {
+ RawQueue *const queue = RawQueue::Fetch("Queue", sizeof(TestMessage), 1, 2);
+ const TestMessage *message, *peek_message;
+
+ PushMessage(queue, 971);
+ PushMessage(queue, 1768);
+ ASSERT_NE(nullptr, queue->ReadMessage(RawQueue::kNonBlock));
+ PushMessage(queue, 254);
+ ASSERT_NE(nullptr, queue->ReadMessage(RawQueue::kNonBlock));
+
+ int index = 0;
+
+ peek_message = static_cast<const TestMessage *>(
+ queue->ReadMessageIndex(RawQueue::kNonBlock | RawQueue::kPeek, &index));
+ message = static_cast<const TestMessage *>(
+ queue->ReadMessageIndex(RawQueue::kNonBlock, &index));
+ ASSERT_NE(nullptr, message);
+ EXPECT_EQ(message, peek_message);
+ EXPECT_EQ(254, message->data);
+ EXPECT_EQ(3, index);
+ queue->FreeMessage(message);
+ queue->FreeMessage(peek_message);
+
+ index = 0;
+ peek_message = static_cast<const TestMessage *>(queue->ReadMessage(
+ RawQueue::kNonBlock | RawQueue::kPeek | RawQueue::kFromEnd));
+ message = static_cast<const TestMessage *>(queue->ReadMessageIndex(
+ RawQueue::kNonBlock | RawQueue::kFromEnd, &index));
+ ASSERT_NE(nullptr, message);
+ EXPECT_EQ(message, peek_message);
+ EXPECT_EQ(254, message->data);
+ EXPECT_EQ(3, index);
+ queue->FreeMessage(message);
+}
+
+TEST_F(RawQueueTest, ReadIndexLotBehind) {
+ RawQueue *const queue = RawQueue::Fetch("Queue", sizeof(TestMessage), 1, 2);
+ const TestMessage *message, *peek_message;
+
+ PushMessage(queue, 971);
+ PushMessage(queue, 1768);
+ {
+ const void *message1, *message2;
+ message1 = queue->ReadMessage(RawQueue::kNonBlock);
+ ASSERT_NE(nullptr, message1);
+ PushMessage(queue, 254);
+ message2 = queue->ReadMessage(RawQueue::kNonBlock);
+ ASSERT_NE(nullptr, message2);
+ PushMessage(queue, 973);
+ EXPECT_EQ(4, kExtraMessages + 2 - queue->FreeMessages());
+ queue->FreeMessage(message1);
+ EXPECT_EQ(3, kExtraMessages + 2 - queue->FreeMessages());
+ queue->FreeMessage(message2);
+ EXPECT_EQ(2, kExtraMessages + 2 - queue->FreeMessages());
+ }
+
+ int index = 0;
+
+ peek_message = static_cast<const TestMessage *>(
+ queue->ReadMessageIndex(RawQueue::kNonBlock | RawQueue::kPeek, &index));
+ message = static_cast<const TestMessage *>(
+ queue->ReadMessageIndex(RawQueue::kNonBlock, &index));
+ ASSERT_NE(nullptr, message);
+ EXPECT_EQ(message, peek_message);
+ EXPECT_EQ(254, message->data);
+ EXPECT_EQ(3, index);
+ queue->FreeMessage(message);
+ queue->FreeMessage(peek_message);
+
+ peek_message = static_cast<const TestMessage *>(
+ queue->ReadMessageIndex(RawQueue::kNonBlock | RawQueue::kPeek, &index));
+ message = static_cast<const TestMessage *>(
+ queue->ReadMessageIndex(RawQueue::kNonBlock, &index));
+ ASSERT_NE(nullptr, message);
+ EXPECT_EQ(message, peek_message);
+ EXPECT_EQ(973, message->data);
+ EXPECT_EQ(4, index);
+ queue->FreeMessage(message);
+ queue->FreeMessage(peek_message);
+
+ index = 0;
+ peek_message = static_cast<const TestMessage *>(queue->ReadMessage(
+ RawQueue::kNonBlock | RawQueue::kPeek | RawQueue::kFromEnd));
+ message = static_cast<const TestMessage *>(queue->ReadMessageIndex(
+ RawQueue::kNonBlock | RawQueue::kFromEnd, &index));
+ ASSERT_NE(nullptr, message);
+ EXPECT_EQ(message, peek_message);
+ EXPECT_EQ(973, message->data);
+ EXPECT_EQ(4, index);
+ queue->FreeMessage(message);
+}
+
+TEST_F(RawQueueTest, ReadIndexLotBehindNotFull) {
+ RawQueue *const queue = RawQueue::Fetch("Queue", sizeof(TestMessage), 1, 2);
+ const TestMessage *message, *peek_message;
+
+ PushMessage(queue, 971);
+ PushMessage(queue, 1768);
+ ASSERT_NE(nullptr, queue->ReadMessage(RawQueue::kNonBlock));
+ PushMessage(queue, 254);
+ ASSERT_NE(nullptr, queue->ReadMessage(RawQueue::kNonBlock));
+ PushMessage(queue, 973);
+ ASSERT_NE(nullptr, queue->ReadMessage(RawQueue::kNonBlock));
+
+ int index = 0;
+
+ peek_message = static_cast<const TestMessage *>(
+ queue->ReadMessageIndex(RawQueue::kNonBlock | RawQueue::kPeek, &index));
+ message = static_cast<const TestMessage *>(
+ queue->ReadMessageIndex(RawQueue::kNonBlock, &index));
+ ASSERT_NE(nullptr, message);
+ EXPECT_EQ(message, peek_message);
+ EXPECT_EQ(973, message->data);
+ EXPECT_EQ(4, index);
+ queue->FreeMessage(message);
+ queue->FreeMessage(peek_message);
+
+ index = 0;
+ peek_message = static_cast<const TestMessage *>(queue->ReadMessage(
+ RawQueue::kNonBlock | RawQueue::kPeek | RawQueue::kFromEnd));
+ message = static_cast<const TestMessage *>(queue->ReadMessageIndex(
+ RawQueue::kNonBlock | RawQueue::kFromEnd, &index));
+ ASSERT_NE(nullptr, message);
+ EXPECT_EQ(message, peek_message);
+ EXPECT_EQ(973, message->data);
+ EXPECT_EQ(4, index);
+ queue->FreeMessage(message);
+}
+
+TEST_F(RawQueueTest, ReadIndexEvenMoreBehind) {
+ RawQueue *const queue = RawQueue::Fetch("Queue", sizeof(TestMessage), 1, 2);
+ const TestMessage *message, *peek_message;
+
+ PushMessage(queue, 971);
+ PushMessage(queue, 1768);
+ ASSERT_NE(nullptr, queue->ReadMessage(RawQueue::kNonBlock));
+ PushMessage(queue, 254);
+ ASSERT_NE(nullptr, queue->ReadMessage(RawQueue::kNonBlock));
+ PushMessage(queue, 973);
+ ASSERT_NE(nullptr, queue->ReadMessage(RawQueue::kNonBlock));
+ PushMessage(queue, 1114);
+
+ int index = 0;
+
+ peek_message = static_cast<const TestMessage *>(
+ queue->ReadMessageIndex(RawQueue::kNonBlock | RawQueue::kPeek, &index));
+ message = static_cast<const TestMessage *>(
+ queue->ReadMessageIndex(RawQueue::kNonBlock, &index));
+ ASSERT_NE(nullptr, message);
+ EXPECT_EQ(message, peek_message);
+ EXPECT_EQ(973, message->data);
+ EXPECT_EQ(4, index);
+ queue->FreeMessage(message);
+ queue->FreeMessage(peek_message);
+
+ peek_message = static_cast<const TestMessage *>(
+ queue->ReadMessageIndex(RawQueue::kNonBlock | RawQueue::kPeek, &index));
+ message = static_cast<const TestMessage *>(
+ queue->ReadMessageIndex(RawQueue::kNonBlock, &index));
+ ASSERT_NE(nullptr, message);
+ EXPECT_EQ(message, peek_message);
+ EXPECT_EQ(1114, message->data);
+ EXPECT_EQ(5, index);
+ queue->FreeMessage(message);
+ queue->FreeMessage(peek_message);
+
+ index = 0;
+ peek_message = static_cast<const TestMessage *>(queue->ReadMessage(
+ RawQueue::kNonBlock | RawQueue::kPeek | RawQueue::kFromEnd));
+ message = static_cast<const TestMessage *>(queue->ReadMessageIndex(
+ RawQueue::kNonBlock | RawQueue::kFromEnd, &index));
+ ASSERT_NE(nullptr, message);
+ EXPECT_EQ(message, peek_message);
+ EXPECT_EQ(1114, message->data);
+ EXPECT_EQ(5, index);
+ queue->FreeMessage(message);
+}
+
+TEST_F(RawQueueTest, ReadIndexEvenMoreBehindNotFull) {
+ RawQueue *const queue = RawQueue::Fetch("Queue", sizeof(TestMessage), 1, 2);
+ const TestMessage *message, *peek_message;
+
+ PushMessage(queue, 971);
+ PushMessage(queue, 1768);
+ ASSERT_NE(nullptr, queue->ReadMessage(RawQueue::kNonBlock));
+ PushMessage(queue, 254);
+ ASSERT_NE(nullptr, queue->ReadMessage(RawQueue::kNonBlock));
+ PushMessage(queue, 973);
+ ASSERT_NE(nullptr, queue->ReadMessage(RawQueue::kNonBlock));
+ PushMessage(queue, 1114);
+ ASSERT_NE(nullptr, queue->ReadMessage(RawQueue::kNonBlock));
+
+ int index = 0;
+
+ peek_message = static_cast<const TestMessage *>(
+ queue->ReadMessageIndex(RawQueue::kNonBlock | RawQueue::kPeek, &index));
+ message = static_cast<const TestMessage *>(
+ queue->ReadMessageIndex(RawQueue::kNonBlock, &index));
+ ASSERT_NE(nullptr, message);
+ EXPECT_EQ(message, peek_message);
+ EXPECT_EQ(1114, message->data);
+ EXPECT_EQ(5, index);
+ queue->FreeMessage(message);
+ queue->FreeMessage(peek_message);
+
+ index = 0;
+ peek_message = static_cast<const TestMessage *>(queue->ReadMessage(
+ RawQueue::kNonBlock | RawQueue::kPeek | RawQueue::kFromEnd));
+ message = static_cast<const TestMessage *>(queue->ReadMessageIndex(
+ RawQueue::kNonBlock | RawQueue::kFromEnd, &index));
+ ASSERT_NE(nullptr, message);
+ EXPECT_EQ(message, peek_message);
+ EXPECT_EQ(1114, message->data);
+ EXPECT_EQ(5, index);
+ queue->FreeMessage(message);
+}
+
+TEST_F(RawQueueTest, MessageReferenceCounts) {
+ RawQueue *const queue = RawQueue::Fetch("Queue", sizeof(TestMessage), 1, 2);
+ const void *message1, *message2;
+
+ EXPECT_EQ(0, kExtraMessages + 2 - queue->FreeMessages());
+ message1 = queue->GetMessage();
+ EXPECT_NE(nullptr, message1);
+ EXPECT_EQ(1, kExtraMessages + 2 - queue->FreeMessages());
+ message2 = queue->GetMessage();
+ EXPECT_NE(nullptr, message2);
+ EXPECT_EQ(2, kExtraMessages + 2 - queue->FreeMessages());
+ queue->FreeMessage(message1);
+ EXPECT_EQ(1, kExtraMessages + 2 - queue->FreeMessages());
+ queue->FreeMessage(message2);
+ EXPECT_EQ(0, kExtraMessages + 2 - queue->FreeMessages());
+}
+
+// Tests that writing with kNonBlock fails and frees the message.
+TEST_F(RawQueueTest, WriteDontBlock) {
+ RawQueue *const queue = RawQueue::Fetch("Queue", sizeof(TestMessage), 1, 1);
+ void *message;
+
+ PushMessage(queue, 971);
+ int free_before = queue->FreeMessages();
+ message = queue->GetMessage();
+ ASSERT_NE(nullptr, message);
+ EXPECT_NE(free_before, queue->FreeMessages());
+ EXPECT_FALSE(queue->WriteMessage(message, RawQueue::kNonBlock));
+ EXPECT_EQ(free_before, queue->FreeMessages());
+}
+
+// Tests that writing with kOverride pushes the last message out of the queue.
+TEST_F(RawQueueTest, WriteOverride) {
+ RawQueue *const queue = RawQueue::Fetch("Queue", sizeof(TestMessage), 1, 2);
+ TestMessage *message1;
+
+ PushMessage(queue, 971);
+ PushMessage(queue, 1768);
+ int free_before = queue->FreeMessages();
+ message1 = static_cast<TestMessage *>(queue->GetMessage());
+ ASSERT_NE(nullptr, message1);
+ EXPECT_NE(free_before, queue->FreeMessages());
+ message1->data = 254;
+ EXPECT_TRUE(queue->WriteMessage(message1, RawQueue::kOverride));
+ EXPECT_EQ(free_before, queue->FreeMessages());
+
+ const TestMessage *message2;
+ message2 =
+ static_cast<const TestMessage *>(queue->ReadMessage(RawQueue::kNonBlock));
+ EXPECT_EQ(1768, message2->data);
+ queue->FreeMessage(message2);
+ EXPECT_EQ(free_before + 1, queue->FreeMessages());
+ message2 =
+ static_cast<const TestMessage *>(queue->ReadMessage(RawQueue::kNonBlock));
+ EXPECT_EQ(254, message2->data);
+ queue->FreeMessage(message2);
+ EXPECT_EQ(free_before + 2, queue->FreeMessages());
+}
+
+// Makes sure that ThreadSanitizer doesn't catch any issues freeing from
+// multiple threads at once.
+TEST_F(RawQueueTest, MultiThreadedFree) {
+ RawQueue *const queue = RawQueue::Fetch("Queue", sizeof(TestMessage), 1, 1);
+ PushMessage(queue, 971);
+ int free_before = queue->FreeMessages();
+
+ const void *const message1 =
+ queue->ReadMessage(RawQueue::kPeek | RawQueue::kNonBlock);
+ const void *const message2 =
+ queue->ReadMessage(RawQueue::kPeek | RawQueue::kNonBlock);
+ ASSERT_NE(nullptr, message1);
+ ASSERT_NE(nullptr, message2);
+ EXPECT_EQ(free_before, queue->FreeMessages());
+ util::FunctionThread t1([message1, queue](util::Thread *) {
+ queue->FreeMessage(message1);
+ });
+ util::FunctionThread t2([message2, queue](util::Thread *) {
+ queue->FreeMessage(message2);
+ });
+ t1.Start();
+ t2.Start();
+ t1.WaitUntilDone();
+ t2.WaitUntilDone();
+ EXPECT_EQ(free_before, queue->FreeMessages());
+}
+
+TEST_F(RawQueueDeathTest, OptionsValidation) {
+ RawQueue *const queue = RawQueue::Fetch("Queue", 1, 1, 1);
+
+ EXPECT_DEATH(
+ {
+ logging::AddImplementation(new util::DeathTestLogImplementation());
+ queue->WriteMessage(nullptr, RawQueue::kPeek);
+ },
+ ".*illegal write option.*");
+ EXPECT_DEATH(
+ {
+ logging::AddImplementation(new util::DeathTestLogImplementation());
+ queue->WriteMessage(nullptr, RawQueue::kFromEnd);
+ },
+ ".*illegal write option.*");
+ EXPECT_DEATH(
+ {
+ logging::AddImplementation(new util::DeathTestLogImplementation());
+ queue->WriteMessage(nullptr, RawQueue::kPeek | RawQueue::kFromEnd);
+ },
+ ".*illegal write option.*");
+ EXPECT_DEATH(
+ {
+ logging::AddImplementation(new util::DeathTestLogImplementation());
+ queue->WriteMessage(nullptr, RawQueue::kNonBlock | RawQueue::kBlock);
+ },
+ ".*invalid write option.*");
+
+ EXPECT_DEATH(
+ {
+ logging::AddImplementation(new util::DeathTestLogImplementation());
+ queue->ReadMessageIndex(
+ RawQueue::kBlock | RawQueue::kFromEnd | RawQueue::kPeek, nullptr);
+ },
+ ".*ReadMessageIndex.*is not allowed.*");
+ EXPECT_DEATH(
+ {
+ logging::AddImplementation(new util::DeathTestLogImplementation());
+ queue->ReadMessageIndex(RawQueue::kOverride, nullptr);
+ },
+ ".*illegal read option.*");
+ EXPECT_DEATH(
+ {
+ logging::AddImplementation(new util::DeathTestLogImplementation());
+ queue->ReadMessageIndex(RawQueue::kOverride | RawQueue::kBlock,
+ nullptr);
+ },
+ ".*illegal read option.*");
+ EXPECT_DEATH(
+ {
+ logging::AddImplementation(new util::DeathTestLogImplementation());
+ queue->ReadMessage(RawQueue::kNonBlock | RawQueue::kBlock);
+ },
+ ".*invalid read option.*");
+}
+
+} // namespace testing
+} // namespace aos
diff --git a/aos/linux_code/ipc_lib/shared_mem.c b/aos/linux_code/ipc_lib/shared_mem.c
new file mode 100644
index 0000000..c43bfa2
--- /dev/null
+++ b/aos/linux_code/ipc_lib/shared_mem.c
@@ -0,0 +1,140 @@
+#include "aos/linux_code/ipc_lib/shared_mem.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#include "aos/linux_code/ipc_lib/core_lib.h"
+#include "aos/common/logging/logging.h"
+
+// the path for the shared memory segment. see shm_open(3) for restrictions
+#define AOS_SHM_NAME "/aos_shared_mem"
+// Size of the shared mem segment.
+// This must fit in the tmpfs for /dev/shm/
+#define SIZEOFSHMSEG (4096 * 0x3000)
+
+void init_shared_mem_core(aos_shm_core *shm_core) {
+ memset(&shm_core->time_offset, 0 , sizeof(shm_core->time_offset));
+ memset(&shm_core->msg_alloc_lock, 0, sizeof(shm_core->msg_alloc_lock));
+ shm_core->queues.pointer = NULL;
+ memset(&shm_core->queues.lock, 0, sizeof(shm_core->queues.lock));
+ shm_core->queue_types.pointer = NULL;
+ memset(&shm_core->queue_types.lock, 0, sizeof(shm_core->queue_types.lock));
+}
+
+ptrdiff_t aos_core_get_mem_usage(void) {
+ return global_core->size -
+ ((ptrdiff_t)global_core->mem_struct->msg_alloc -
+ (ptrdiff_t)global_core->mem_struct);
+}
+
+struct aos_core *global_core = NULL;
+
+// TODO(brians): madvise(2) it to put this shm in core dumps.
+void aos_core_create_shared_mem(int create, int lock) {
+ assert(global_core == NULL);
+ static struct aos_core global_core_data;
+ global_core = &global_core_data;
+
+ {
+ char *shm_name = getenv("AOS_SHM_NAME");
+ if (shm_name == NULL) {
+ global_core->shm_name = AOS_SHM_NAME;
+ } else {
+ printf("AOS_SHM_NAME defined, using %s\n", shm_name);
+ global_core->shm_name = shm_name;
+ }
+ }
+
+ int shm;
+ if (create) {
+ while (1) {
+ shm = shm_open(global_core->shm_name, O_RDWR | O_CREAT | O_EXCL, 0666);
+ global_core->owner = 1;
+ if (shm == -1 && errno == EEXIST) {
+ printf("shared_mem: going to shm_unlink(%s)\n", global_core->shm_name);
+ if (shm_unlink(global_core->shm_name) == -1) {
+ PLOG(WARNING, "shm_unlink(%s) failed", global_core->shm_name);
+ break;
+ }
+ } else {
+ break;
+ }
+ }
+ } else {
+ shm = shm_open(global_core->shm_name, O_RDWR, 0);
+ global_core->owner = 0;
+ }
+ if (shm == -1) {
+ PLOG(FATAL, "shm_open(%s, O_RDWR [| O_CREAT | O_EXCL, 0|0666) failed",
+ global_core->shm_name);
+ }
+ if (global_core->owner) {
+ if (ftruncate(shm, SIZEOFSHMSEG) == -1) {
+ PLOG(FATAL, "fruncate(%d, 0x%zx) failed", shm, (size_t)SIZEOFSHMSEG);
+ }
+ }
+ int flags = MAP_SHARED | MAP_FIXED;
+ if (lock) flags |= MAP_LOCKED | MAP_POPULATE;
+ void *shm_address = mmap((void *)SHM_START, SIZEOFSHMSEG,
+ PROT_READ | PROT_WRITE, flags, shm, 0);
+ if (shm_address == MAP_FAILED) {
+ PLOG(FATAL, "shared_mem: mmap(%p, 0x%zx, stuff, %x, %d, 0) failed",
+ (void *)SHM_START, (size_t)SIZEOFSHMSEG, flags, shm);
+ }
+ if (create) {
+ printf("shared_mem: creating %s, shm at: %p\n", global_core->shm_name,
+ shm_address);
+ } else {
+ printf("shared_mem: not creating, shm at: %p\n", shm_address);
+ }
+ if (close(shm) == -1) {
+ PLOG(WARNING, "close(%d(=shm) failed", shm);
+ }
+ if (shm_address != (void *)SHM_START) {
+ LOG(FATAL, "shm isn't at hard-coded %p. at %p instead\n",
+ (void *)SHM_START, shm_address);
+ }
+ aos_core_use_address_as_shared_mem(shm_address, SIZEOFSHMSEG);
+ LOG(INFO, "shared_mem: end of create_shared_mem owner=%d\n",
+ global_core->owner);
+}
+
+void aos_core_use_address_as_shared_mem(void *address, size_t size) {
+ global_core->mem_struct = address;
+ global_core->size = size;
+ global_core->shared_mem =
+ (uint8_t *)address + sizeof(*global_core->mem_struct);
+ if (global_core->owner) {
+ global_core->mem_struct->msg_alloc = (uint8_t *)address + global_core->size;
+ init_shared_mem_core(global_core->mem_struct);
+ futex_set(&global_core->mem_struct->creation_condition);
+ } else {
+ if (futex_wait(&global_core->mem_struct->creation_condition) != 0) {
+ LOG(FATAL, "waiting on creation_condition failed\n");
+ }
+ }
+}
+
+void aos_core_free_shared_mem() {
+ void *shm_address = global_core->shared_mem;
+ if (munmap((void *)SHM_START, SIZEOFSHMSEG) == -1) {
+ PLOG(FATAL, "munmap(%p, 0x%zx) failed", shm_address,
+ (size_t)SIZEOFSHMSEG);
+ }
+ if (global_core->owner) {
+ if (shm_unlink(global_core->shm_name)) {
+ PLOG(FATAL, "shared_mem: shm_unlink(%s) failed", global_core->shm_name);
+ }
+ }
+}
+
+int aos_core_is_init(void) {
+ return global_core != NULL;
+}
diff --git a/aos/linux_code/ipc_lib/shared_mem.h b/aos/linux_code/ipc_lib/shared_mem.h
new file mode 100644
index 0000000..423fd0c
--- /dev/null
+++ b/aos/linux_code/ipc_lib/shared_mem.h
@@ -0,0 +1,87 @@
+#ifndef _SHARED_MEM_H_
+#define _SHARED_MEM_H_
+
+#include <stddef.h>
+#include <unistd.h>
+#include <time.h>
+
+#include "aos/linux_code/ipc_lib/aos_sync.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern struct aos_core *global_core __attribute__((weak));
+
+// Where the shared memory segment starts in each process's address space.
+// Has to be the same in all of them so that stuff in shared memory
+// can have regular pointers to other stuff in shared memory.
+#define SHM_START 0x20000000
+
+// A structure that represents some kind of global pointer that everything
+// shares.
+typedef struct aos_global_pointer_t {
+ struct aos_mutex lock;
+ void *pointer;
+} aos_global_pointer;
+
+typedef struct aos_shm_core_t {
+ // Gets 0-initialized at the start (as part of shared memory) and
+ // the owner sets as soon as it finishes setting stuff up.
+ aos_condition creation_condition;
+
+ // An offset from CLOCK_REALTIME to times for all the code.
+ // This is currently only set to non-zero by the log replay code.
+ // There is no synchronization on this to avoid the overhead because it is
+ // only updated with proper memory barriers when only a single task is
+ // running.
+ struct timespec time_offset;
+
+ struct aos_mutex msg_alloc_lock;
+ void *msg_alloc;
+
+ // A pointer to the head of the linked list of queues.
+ // pointer points to a ::aos::Queue.
+ aos_global_pointer queues;
+ // A pointer to the head of the linked list of queue message types.
+ // pointer points to a ::aos::type_cache::ShmType.
+ aos_global_pointer queue_types;
+} aos_shm_core;
+
+struct aos_core {
+ // Non-0 if we "own" shared_mem and should shm_unlink(3) it when we're done.
+ int owner;
+ void *shared_mem;
+ // How large the chunk of shared memory is.
+ ptrdiff_t size;
+ aos_shm_core *mem_struct;
+ // For the owner to store the name of the file to unlink when closing.
+ const char *shm_name;
+};
+
+void init_shared_mem_core(aos_shm_core *shm_core);
+
+ptrdiff_t aos_core_get_mem_usage(void);
+
+// Takes the specified memory address and uses it as the shared memory.
+// address is the memory address, and size is the size of the memory.
+// global_core needs to point to an instance of struct aos_core, and owner
+// should be set correctly there.
+// The owner should verify that the first sizeof(mutex) of data is set to 0
+// before passing the memory to this function.
+void aos_core_use_address_as_shared_mem(void *address, size_t size);
+
+// create is true to remove any existing shm to create a fresh one or false to
+// fail if it does not already exist.
+// lock is true to lock shared memory into RAM or false to not.
+void aos_core_create_shared_mem(int create, int lock);
+void aos_core_free_shared_mem(void);
+
+// Returns whether or not the shared memory system is active.
+int aos_core_is_init(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/aos/linux_code/ipc_lib/unique_message_ptr.h b/aos/linux_code/ipc_lib/unique_message_ptr.h
new file mode 100644
index 0000000..e30a4c0
--- /dev/null
+++ b/aos/linux_code/ipc_lib/unique_message_ptr.h
@@ -0,0 +1,39 @@
+#include <memory>
+
+#include "aos/atom_code/ipc_lib/queue.h"
+
+namespace aos {
+namespace internal {
+
+template<typename T>
+class queue_free {
+ public:
+ queue_free(RawQueue *queue) : queue_(queue) {}
+
+ void operator()(const T *message) {
+ queue_->FreeMessage(static_cast<const void *>(message));
+ }
+
+ private:
+ RawQueue *const queue_;
+};
+
+} // namespace internal
+
+template<typename T>
+class unique_message_ptr : public ::std::unique_ptr<T, ::aos::internal::queue_free<T>> {
+ public:
+ unique_message_ptr(RawQueue *queue, T *message = NULL)
+ : ::std::unique_ptr<T, ::aos::internal::queue_free<T>>(message, ::aos::internal::queue_free<T>(queue)) {}
+
+ // Perfectly forward this so that the move functionality of ::std::unique_ptr
+ // works.
+ template <typename... Args>
+ unique_message_ptr<T> &operator=(Args &&... args) {
+ ::std::unique_ptr<T, ::aos::internal::queue_free<T>>::operator=(
+ ::std::forward<Args>(args)...);
+ return *this;
+ }
+};
+
+} // namespace aos
diff --git a/aos/linux_code/linux_code.gyp b/aos/linux_code/linux_code.gyp
new file mode 100644
index 0000000..59f5d27
--- /dev/null
+++ b/aos/linux_code/linux_code.gyp
@@ -0,0 +1,76 @@
+{
+ 'targets': [
+ {
+ 'target_name': 'dump_rtprio',
+ 'type': 'executable',
+ 'variables': {
+ 'no_rsync': 1,
+ },
+ 'sources': [
+ 'dump_rtprio.cc',
+ ],
+ 'dependencies': [
+ '<(AOS)/build/aos.gyp:logging',
+ '<(AOS)/common/common.gyp:time',
+ ],
+ },
+ {
+ 'target_name': 'complex_thread_local',
+ 'type': 'static_library',
+ 'sources': [
+ 'complex_thread_local.cc',
+ ],
+ 'dependencies': [
+ '<(AOS)/common/common.gyp:once',
+ '<(AOS)/common/common.gyp:die',
+ ],
+ },
+ {
+ 'target_name': 'complex_thread_local_test',
+ 'type': 'executable',
+ 'sources': [
+ 'complex_thread_local_test.cc',
+ ],
+ 'dependencies': [
+ 'complex_thread_local',
+ '<(EXTERNALS):gtest',
+ '<(AOS)/common/util/util.gyp:thread',
+ '<(AOS)/build/aos.gyp:logging',
+ ],
+ },
+ {
+ 'target_name': 'init',
+ 'type': 'static_library',
+ 'sources': [
+ 'init.cc',
+ ],
+ 'dependencies': [
+ '<(AOS)/linux_code/ipc_lib/ipc_lib.gyp:shared_mem',
+ '<(AOS)/common/common.gyp:die',
+ '<(AOS)/build/aos.gyp:logging',
+ ],
+ },
+ {
+ 'target_name': 'configuration',
+ 'type': 'static_library',
+ 'sources': [
+ 'configuration.cc',
+ ],
+ 'dependencies': [
+ '<(AOS)/common/common.gyp:once',
+ '<(AOS)/build/aos.gyp:logging',
+ ],
+ },
+ {
+ 'target_name': 'core',
+ 'type': 'executable',
+ 'sources': [
+ 'core.cc',
+ ],
+ 'dependencies': [
+ 'init',
+ '<(AOS)/common/util/util.gyp:run_command',
+ ],
+ },
+ ],
+}
diff --git a/aos/linux_code/logging/binary_log_file.cc b/aos/linux_code/logging/binary_log_file.cc
new file mode 100644
index 0000000..b063fae
--- /dev/null
+++ b/aos/linux_code/logging/binary_log_file.cc
@@ -0,0 +1,263 @@
+#include "aos/linux_code/logging/binary_log_file.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <signal.h>
+#include <setjmp.h>
+
+namespace aos {
+namespace logging {
+namespace linux_code {
+namespace {
+
+unsigned long SystemPageSize() {
+ static unsigned long r = sysconf(_SC_PAGESIZE);
+ return r;
+}
+
+} // namespace
+
+LogFileAccessor::LogFileAccessor(int fd, bool writable)
+ : fd_(fd), writable_(writable), offset_(0), current_(0), position_(0) {
+ // Check to make sure that mmap will allow mmaping in chunks of kPageSize.
+ if (SystemPageSize() > kPageSize || (kPageSize % SystemPageSize()) != 0) {
+ LOG(FATAL, "system page size (%lu) not factor of kPageSize (%zd).\n",
+ SystemPageSize(), kPageSize);
+ }
+
+ MapNextPage();
+}
+
+void LogFileAccessor::Sync() const {
+ msync(current_, kPageSize, MS_ASYNC | MS_INVALIDATE);
+}
+
+void LogFileAccessor::SkipToLastSeekablePage() {
+ CHECK(definitely_use_mmap());
+
+ struct stat info;
+ if (fstat(fd_, &info) == -1) {
+ PLOG(FATAL, "fstat(%d, %p) failed", fd_, &info);
+ }
+
+ CHECK((info.st_size % kPageSize) == 0);
+ const auto last_readable_page_number = (info.st_size / kPageSize) - 1;
+ const auto last_seekable_page_number =
+ last_readable_page_number / kSeekPages * kSeekPages;
+ const off_t new_offset = last_seekable_page_number * kPageSize;
+ // We don't want to go backwards...
+ if (new_offset > offset_) {
+ Unmap(current_);
+ offset_ = new_offset;
+ MapNextPage();
+ }
+}
+
+// The only way to tell is using fstat, but we don't really want to be making a
+// syscall every single time somebody wants to know the answer, so it gets
+// cached in is_last_page_.
+bool LogFileAccessor::IsLastPage() {
+ if (is_last_page_ != Maybe::kUnknown) {
+ return is_last_page_ == Maybe::kYes;
+ }
+
+ struct stat info;
+ if (fstat(fd_, &info) == -1) {
+ PLOG(FATAL, "fstat(%d, %p) failed", fd_, &info);
+ }
+ bool r = offset_ == static_cast<off_t>(info.st_size - kPageSize);
+ is_last_page_ = r ? Maybe::kYes : Maybe::kNo;
+ return r;
+}
+
+void LogFileAccessor::MapNextPage() {
+ if (writable_) {
+ if (ftruncate(fd_, offset_ + kPageSize) == -1) {
+ PLOG(FATAL, "ftruncate(%d, %zd) failed", fd_, kPageSize);
+ }
+ }
+
+ if (use_read_ == Maybe::kYes) {
+ ssize_t todo = kPageSize;
+ while (todo > 0) {
+ ssize_t result = read(fd_, current_ + (kPageSize - todo), todo);
+ if (result == -1) {
+ PLOG(FATAL, "read(%d, %p, %zu) failed", fd_,
+ current_ + (kPageSize - todo), todo);
+ } else if (result == 0) {
+ memset(current_, 0, todo);
+ result = todo;
+ }
+ todo -= result;
+ }
+ CHECK_EQ(0, todo);
+ } else {
+ current_ = static_cast<char *>(
+ mmap(NULL, kPageSize, PROT_READ | (writable_ ? PROT_WRITE : 0),
+ MAP_SHARED, fd_, offset_));
+ if (current_ == MAP_FAILED) {
+ if (!writable_ && use_read_ == Maybe::kUnknown && errno == ENODEV) {
+ LOG(INFO, "Falling back to reading the file using read(2).\n");
+ use_read_ = Maybe::kYes;
+ current_ = new char[kPageSize];
+ MapNextPage();
+ return;
+ } else {
+ PLOG(FATAL,
+ "mmap(NULL, %zd, PROT_READ [ | PROT_WRITE], MAP_SHARED, %d, %jd)"
+ " failed",
+ kPageSize, fd_, static_cast<intmax_t>(offset_));
+ }
+ } else {
+ use_read_ = Maybe::kNo;
+ }
+ if (madvise(current_, kPageSize, MADV_SEQUENTIAL | MADV_WILLNEED) == -1) {
+ PLOG(WARNING, "madvise(%p, %zd, MADV_SEQUENTIAL | MADV_WILLNEED) failed",
+ current_, kPageSize);
+ }
+ }
+ offset_ += kPageSize;
+}
+
+void LogFileAccessor::Unmap(void *location) {
+ CHECK_NE(Maybe::kUnknown, use_read_);
+
+ if (use_read_ == Maybe::kNo) {
+ if (munmap(location, kPageSize) == -1) {
+ PLOG(FATAL, "munmap(%p, %zd) failed", location, kPageSize);
+ }
+ }
+ is_last_page_ = Maybe::kUnknown;
+ position_ = 0;
+}
+
+const LogFileMessageHeader *LogFileReader::ReadNextMessage(bool wait) {
+ LogFileMessageHeader *r;
+ do {
+ r = static_cast<LogFileMessageHeader *>(
+ static_cast<void *>(¤t()[position()]));
+ if (wait) {
+ CHECK(definitely_use_mmap());
+ if (futex_wait(&r->marker) != 0) continue;
+ }
+ if (r->marker == 2) {
+ Unmap(current());
+ MapNextPage();
+ CheckCurrentPageReadable();
+ r = static_cast<LogFileMessageHeader *>(static_cast<void *>(current()));
+ }
+ } while (wait && r->marker == 0);
+ if (r->marker == 0) {
+ return NULL;
+ }
+ IncrementPosition(sizeof(LogFileMessageHeader) + r->name_size +
+ r->message_size);
+ if (position() >= kPageSize) {
+ // It's a lot better to blow up here rather than getting SIGBUS errors the
+ // next time we try to read...
+ LOG(FATAL, "corrupt log file running over page size\n");
+ }
+ return r;
+}
+
+// This mess is because the only not completely hackish way to do this is to set
+// up a handler for SIGBUS/SIGSEGV that siglongjmps out to avoid either the
+// instruction being repeated infinitely (and more signals being delivered) or
+// (with SA_RESETHAND) the signal killing the process.
+namespace {
+
+void *volatile fault_address;
+sigjmp_buf jump_context;
+
+void CheckCurrentPageReadableHandler(int /*signal*/, siginfo_t *info, void *) {
+ fault_address = info->si_addr;
+
+ siglongjmp(jump_context, 1);
+}
+
+} // namespace
+void LogFileReader::CheckCurrentPageReadable() {
+ if (definitely_use_read()) return;
+
+ if (sigsetjmp(jump_context, 1) == 0) {
+ struct sigaction action;
+ action.sa_sigaction = CheckCurrentPageReadableHandler;
+ sigemptyset(&action.sa_mask);
+ action.sa_flags = SA_RESETHAND | SA_SIGINFO;
+ struct sigaction previous_bus, previous_segv;
+ if (sigaction(SIGBUS, &action, &previous_bus) == -1) {
+ PLOG(FATAL, "sigaction(SIGBUS(=%d), %p, %p) failed",
+ SIGBUS, &action, &previous_bus);
+ }
+ if (sigaction(SIGSEGV, &action, &previous_segv) == -1) {
+ PLOG(FATAL, "sigaction(SIGSEGV(=%d), %p, %p) failed",
+ SIGSEGV, &action, &previous_segv);
+ }
+
+ char __attribute__((unused)) c = current()[0];
+
+ if (sigaction(SIGBUS, &previous_bus, NULL) == -1) {
+ PLOG(FATAL, "sigaction(SIGBUS(=%d), %p, NULL) failed",
+ SIGBUS, &previous_bus);
+ }
+ if (sigaction(SIGSEGV, &previous_segv, NULL) == -1) {
+ PLOG(FATAL, "sigaction(SIGSEGV(=%d), %p, NULL) failed",
+ SIGSEGV, &previous_segv);
+ }
+ } else {
+ if (fault_address == current()) {
+ LOG(FATAL, "could not read 1 byte at offset 0x%jx into log file\n",
+ static_cast<uintmax_t>(offset()));
+ } else {
+ LOG(FATAL, "faulted at %p, not %p like we were (maybe) supposed to\n",
+ fault_address, current());
+ }
+ }
+}
+
+LogFileMessageHeader *LogFileWriter::GetWritePosition(size_t message_size) {
+ if (NeedNewPageFor(message_size)) ForceNewPage();
+ LogFileMessageHeader *const r = static_cast<LogFileMessageHeader *>(
+ static_cast<void *>(¤t()[position()]));
+ IncrementPosition(message_size);
+ return r;
+}
+
+// A number of seekable pages, not the actual file offset, is stored in *cookie.
+bool LogFileWriter::ShouldClearSeekableData(off_t *cookie,
+ size_t next_message_size) const {
+ off_t next_message_page = (offset() / kPageSize) - 1;
+ if (NeedNewPageFor(next_message_size)) {
+ ++next_message_page;
+ }
+ const off_t current_seekable_page = next_message_page / kSeekPages;
+ CHECK_LE(*cookie, current_seekable_page);
+ const bool r = *cookie != current_seekable_page;
+ *cookie = current_seekable_page;
+ return r;
+}
+
+bool LogFileWriter::NeedNewPageFor(size_t bytes) const {
+ return position() + bytes + (kAlignment - (bytes % kAlignment)) +
+ sizeof(aos_futex) >
+ kPageSize;
+}
+
+void LogFileWriter::ForceNewPage() {
+ char *const temp = current();
+ MapNextPage();
+ if (futex_set_value(
+ static_cast<aos_futex *>(static_cast<void *>(&temp[position()])),
+ 2) == -1) {
+ PLOG(WARNING, "readers will hang because futex_set_value(%p, 2) failed",
+ &temp[position()]);
+ }
+ Unmap(temp);
+}
+
+} // namespace linux_code
+} // namespace logging
+} // namespace aos
diff --git a/aos/linux_code/logging/binary_log_file.h b/aos/linux_code/logging/binary_log_file.h
new file mode 100644
index 0000000..bcf9da1
--- /dev/null
+++ b/aos/linux_code/logging/binary_log_file.h
@@ -0,0 +1,207 @@
+#ifndef AOS_LINUX_CODE_LOGGING_BINARY_LOG_FILE_H_
+#define AOS_LINUX_CODE_LOGGING_BINARY_LOG_FILE_H_
+
+#include <sys/types.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#include <algorithm>
+
+#include "aos/common/logging/logging_impl.h"
+
+namespace aos {
+namespace logging {
+namespace linux_code {
+
+// What to align messages to. A macro because it gets used in attributes.
+// This definition gets #undefed later. Use LogFileAccessor::kAlignment instead.
+#define MESSAGE_ALIGNMENT 8
+
+// File format: {
+// LogFileMessageHeader header;
+// char *name; // of the process that wrote the message
+// void *message;
+// } not crossing kPageSize boundaries into the file and aligned to
+// MESSAGE_ALIGNMENT.
+//
+// Field sizes designed to fit the various values from LogMessage even on
+// other machines (hopefully) because they're baked into the files. They are
+// layed out so that all of the fields are aligned even though the whole thing
+// is packed.
+//
+// A lot of the fields don't have comments because they're the same as the
+// identically named fields in LogMessage.
+struct __attribute__((aligned(MESSAGE_ALIGNMENT))) __attribute__((packed))
+ LogFileMessageHeader {
+ // Represents the type of an individual message.
+ enum class MessageType : uint16_t {
+ // char[] (no '\0' on the end).
+ kString,
+ kStructType,
+ kStruct,
+ kMatrix,
+ };
+
+ // Gets futex_set once this one has been written
+ // for readers keeping up with a live writer.
+ //
+ // Gets initialized to 0 by ftruncate.
+ //
+ // There will be something here after the last message on a "page" set to 2
+ // (by the futex_set) to indicate that the next message is on the next page.
+ aos_futex marker;
+ static_assert(sizeof(marker) == 4, "mutex changed size!");
+ static_assert(MESSAGE_ALIGNMENT >= alignof(aos_futex),
+ "MESSAGE_ALIGNMENT is too small");
+
+ uint32_t time_sec;
+ static_assert(sizeof(time_sec) >= sizeof(LogMessage::seconds),
+ "tv_sec won't fit");
+ uint32_t time_nsec;
+ static_assert(sizeof(time_nsec) >= sizeof(LogMessage::nseconds),
+ "tv_nsec won't fit");
+
+ int32_t source;
+ static_assert(sizeof(source) >= sizeof(LogMessage::source), "PIDs won't fit");
+
+ // Both including all of the bytes in that part of the message.
+ uint32_t name_size, message_size;
+
+ uint16_t sequence;
+ static_assert(sizeof(sequence) == sizeof(LogMessage::sequence),
+ "something changed");
+
+ MessageType type;
+
+ log_level level;
+ static_assert(sizeof(level) == 1, "log_level changed size!");
+};
+static_assert(std::is_pod<LogFileMessageHeader>::value,
+ "LogFileMessageHeader will to get dumped to a file");
+static_assert(offsetof(LogFileMessageHeader, marker) == 0,
+ "marker has to be at the start so readers can find it");
+
+// Handles the mmapping and munmapping for reading and writing log files.
+class LogFileAccessor {
+ public:
+ LogFileAccessor(int fd, bool writable);
+ ~LogFileAccessor() {
+ if (use_read_ == Maybe::kYes) {
+ delete[] current_;
+ }
+ }
+
+ // Asynchronously syncs all open mappings.
+ void Sync() const;
+
+ // Returns true iff we currently have the last page in the file mapped.
+ // This is fundamentally a racy question, so the return value may not be
+ // accurate by the time this method returns.
+ bool IsLastPage();
+
+ // Skips to the last page which is an even multiple of kSeekPages.
+ // This is fundamentally racy, so it may not actually be on the very last
+ // possible multiple of kSeekPages when it returns, but it should be close.
+ // This will never move backwards.
+ void SkipToLastSeekablePage();
+
+ size_t file_offset(const void *msg) {
+ return offset() + (static_cast<const char *>(msg) - current());
+ }
+
+ protected:
+ // The size of the chunks that get mmaped/munmapped together. Large enough so
+ // that not too much space is wasted and it's hopefully bigger than and a
+ // multiple of the system page size but small enough so that really large
+ // chunks of memory don't have to get mapped at the same time.
+ static const size_t kPageSize = 16384;
+ // What to align messages to, copied into an actual constant.
+ static const size_t kAlignment = MESSAGE_ALIGNMENT;
+#undef MESSAGE_ALIGNMENT
+ // Pages which are multiples of this from the beginning of a file start with
+ // no saved state (ie struct types). This allows seeking immediately to the
+ // largest currently written interval of this number when following.
+ static const size_t kSeekPages = 256;
+
+ char *current() const { return current_; }
+ size_t position() const { return position_; }
+ off_t offset() const { return offset_; }
+
+ void IncrementPosition(size_t size) {
+ position_ += size;
+ AlignPosition();
+ }
+
+ void MapNextPage();
+ void Unmap(void *location);
+
+ // Advances position to the next (aligned) location.
+ void AlignPosition() {
+ position_ += kAlignment - (position_ % kAlignment);
+ }
+
+ protected:
+ bool definitely_use_read() const { return use_read_ == Maybe::kYes; }
+ bool definitely_use_mmap() const { return use_read_ == Maybe::kNo; }
+
+ private:
+ // Used for representing things that we might know to be true/false or we
+ // might not know (yet).
+ enum class Maybe { kUnknown, kYes, kNo };
+
+ const int fd_;
+ const bool writable_;
+
+ // Into the file. Always a multiple of kPageSize.
+ off_t offset_;
+ char *current_;
+ size_t position_;
+
+ Maybe is_last_page_ = Maybe::kUnknown;
+
+ // Use read instead of mmap (necessary for fds that don't support mmap).
+ Maybe use_read_ = Maybe::kUnknown;
+};
+
+class LogFileReader : public LogFileAccessor {
+ public:
+ LogFileReader(int fd) : LogFileAccessor(fd, false) {}
+
+ // May return NULL iff wait is false.
+ const LogFileMessageHeader *ReadNextMessage(bool wait);
+
+ private:
+ // Tries reading from the current page to see if it fails because the file
+ // isn't big enough.
+ void CheckCurrentPageReadable();
+};
+
+class LogFileWriter : public LogFileAccessor {
+ public:
+ LogFileWriter(int fd) : LogFileAccessor(fd, true) {}
+
+ // message_size should be the total number of bytes needed for the message.
+ LogFileMessageHeader *GetWritePosition(size_t message_size);
+
+ // Returns true exactly once for each unique cookie on each page where cached
+ // data should be cleared.
+ // Call with a non-zero next_message_size to determine if cached data should
+ // be forgotten before writing a next_message_size-sized message.
+ // cookie should be initialized to 0.
+ bool ShouldClearSeekableData(off_t *cookie, size_t next_message_size) const;
+
+ // Forces a move to a new page for the next message.
+ // This is important when there is cacheable data that needs to be re-written
+ // before a message which will spill over onto the next page but the cacheable
+ // message being refreshed is smaller and won't get to a new page by itself.
+ void ForceNewPage();
+
+ private:
+ bool NeedNewPageFor(size_t bytes) const;
+};
+
+} // namespace linux_code
+} // namespace logging
+} // namespace aos
+
+#endif // AOS_LINUX_CODE_LOGGING_BINARY_LOG_FILE_H_
diff --git a/aos/linux_code/logging/binary_log_writer.cc b/aos/linux_code/logging/binary_log_writer.cc
new file mode 100644
index 0000000..dbe61e0
--- /dev/null
+++ b/aos/linux_code/logging/binary_log_writer.cc
@@ -0,0 +1,311 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <time.h>
+#include <string.h>
+#include <string>
+#include <unistd.h>
+#include <sys/types.h>
+#include <pwd.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <mntent.h>
+
+#include <map>
+#include <unordered_set>
+
+#include "aos/linux_code/logging/linux_logging.h"
+#include "aos/linux_code/logging/binary_log_file.h"
+#include "aos/linux_code/init.h"
+#include "aos/linux_code/configuration.h"
+#include "aos/common/queue_types.h"
+#include "aos/common/die.h"
+
+namespace aos {
+namespace logging {
+namespace linux_code {
+namespace {
+
+void CheckTypeWritten(uint32_t type_id, LogFileWriter *writer,
+ ::std::unordered_set<uint32_t> *written_type_ids) {
+ if (written_type_ids->count(type_id) > 0) return;
+ if (MessageType::IsPrimitive(type_id)) return;
+
+ const MessageType &type = type_cache::Get(type_id);
+ for (int i = 0; i < type.number_fields; ++i) {
+ CheckTypeWritten(type.fields[i]->type, writer, written_type_ids);
+ }
+
+ char buffer[1024];
+ ssize_t size = type.Serialize(buffer, sizeof(buffer));
+ if (size == -1) {
+ LOG(WARNING, "%zu-byte buffer is too small to serialize type %s\n",
+ sizeof(buffer), type.name.c_str());
+ return;
+ }
+ LogFileMessageHeader *const output =
+ writer->GetWritePosition(sizeof(LogFileMessageHeader) + size);
+
+ output->time_sec = output->time_nsec = 0;
+ output->source = getpid();
+ output->name_size = 0;
+ output->sequence = 0;
+ output->level = FATAL;
+
+ memcpy(output + 1, buffer, size);
+ output->message_size = size;
+
+ output->type = LogFileMessageHeader::MessageType::kStructType;
+ futex_set(&output->marker);
+
+ written_type_ids->insert(type_id);
+}
+
+void AllocateLogName(char **filename, const char *directory) {
+ int fileindex = 0;
+ DIR *const d = opendir(directory);
+ if (d == nullptr) {
+ PDie("could not open directory %s", directory);
+ }
+ int index = 0;
+ while (true) {
+ errno = 0;
+ struct dirent *const dir = readdir(d);
+ if (dir == nullptr) {
+ if (errno == 0) {
+ break;
+ } else {
+ PLOG(FATAL, "readdir(%p) failed", d);
+ }
+ } else {
+ if (sscanf(dir->d_name, "aos_log-%d", &index) == 1) {
+ if (index >= fileindex) {
+ fileindex = index + 1;
+ }
+ }
+ }
+ }
+ closedir(d);
+
+ char previous[512];
+ ::std::string path = ::std::string(directory) + "/aos_log-current";
+ ssize_t len = ::readlink(path.c_str(), previous, sizeof(previous));
+ if (len != -1) {
+ previous[len] = '\0';
+ } else {
+ previous[0] = '\0';
+ LOG(INFO, "Could not find aos_log-current\n");
+ printf("Could not find aos_log-current\n");
+ }
+ if (asprintf(filename, "%s/aos_log-%03d", directory, fileindex) == -1) {
+ PDie("couldn't create final name");
+ }
+ LOG(INFO, "Created log file (aos_log-%d) in directory (%s). Previous file "
+ "was (%s).\n",
+ fileindex, directory, previous);
+ printf("Created log file (aos_log-%d) in directory (%s). Previous file was "
+ "(%s).\n",
+ fileindex, directory, previous);
+}
+
+#ifdef AOS_ARCHITECTURE_arm_frc
+bool FoundThumbDrive(const char *path) {
+ FILE *mnt_fp = setmntent("/etc/mtab", "r");
+ if (mnt_fp == nullptr) {
+ Die("Could not open /etc/mtab");
+ }
+
+ bool found = false;
+ struct mntent mntbuf;
+ char buf[256];
+ while (!found) {
+ struct mntent *mount_list = getmntent_r(mnt_fp, &mntbuf, buf, sizeof(buf));
+ if (mount_list == nullptr) {
+ break;
+ }
+ if (strcmp(mount_list->mnt_dir, path) == 0) {
+ found = true;
+ }
+ }
+ endmntent(mnt_fp);
+ return found;
+}
+
+bool FindDevice(char *device, size_t device_size) {
+ char test_device[10];
+ for (char i = 'a'; i < 'z'; ++i) {
+ snprintf(test_device, sizeof(test_device), "/dev/sd%c", i);
+ LOG(INFO, "Trying to access %s\n", test_device);
+ if (access(test_device, F_OK) != -1) {
+ snprintf(device, device_size, "sd%c", i);
+ return true;
+ }
+ }
+ return false;
+}
+#endif
+
+int BinaryLogReaderMain() {
+ InitNRT();
+
+#ifdef AOS_ARCHITECTURE_arm_frc
+ char folder[128];
+
+ {
+ char dev_name[8];
+ while (!FindDevice(dev_name, sizeof(dev_name))) {
+ LOG(INFO, "Waiting for a device\n");
+ printf("Waiting for a device\n");
+ sleep(5);
+ }
+ snprintf(folder, sizeof(folder), "/media/%s1", dev_name);
+ while (!FoundThumbDrive(folder)) {
+ LOG(INFO, "Waiting for %s\n", folder);
+ printf("Waiting for %s\n", folder);
+ sleep(1);
+ }
+ snprintf(folder, sizeof(folder), "/media/%s1/", dev_name);
+ }
+
+ if (access(folder, F_OK) == -1) {
+#else
+ const char *folder = configuration::GetLoggingDirectory();
+ if (access(folder, R_OK | W_OK) == -1) {
+#endif
+ LOG(FATAL, "folder '%s' does not exist. please create it\n", folder);
+ }
+ LOG(INFO, "logging to folder '%s'\n", folder);
+
+ char *tmp;
+ AllocateLogName(&tmp, folder);
+ char *tmp2;
+ if (asprintf(&tmp2, "%s/aos_log-current", folder) == -1) {
+ PLOG(WARNING, "couldn't create current symlink name");
+ } else {
+ if (unlink(tmp2) == -1 && (errno != EROFS && errno != ENOENT)) {
+ LOG(WARNING, "unlink('%s') failed", tmp2);
+ }
+ if (symlink(tmp, tmp2) == -1) {
+ PLOG(WARNING, "symlink('%s', '%s') failed", tmp, tmp2);
+ }
+ free(tmp2);
+ }
+ int fd = open(tmp, O_SYNC | O_APPEND | O_RDWR | O_CREAT,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
+ free(tmp);
+ if (fd == -1) {
+ PLOG(FATAL, "opening file '%s' failed", tmp);
+ }
+ LogFileWriter writer(fd);
+
+ ::std::unordered_set<uint32_t> written_type_ids;
+ off_t clear_type_ids_cookie = 0;
+
+ while (true) {
+ const LogMessage *const msg = ReadNext();
+ if (msg == NULL) continue;
+
+ const size_t raw_output_length =
+ sizeof(LogFileMessageHeader) + msg->name_length + msg->message_length;
+ size_t output_length = raw_output_length;
+ if (msg->type == LogMessage::Type::kStruct) {
+ output_length += sizeof(msg->structure.type_id) + sizeof(uint32_t) +
+ msg->structure.string_length;
+ if (writer.ShouldClearSeekableData(&clear_type_ids_cookie,
+ output_length)) {
+ writer.ForceNewPage();
+ written_type_ids.clear();
+ }
+ CheckTypeWritten(msg->structure.type_id, &writer, &written_type_ids);
+ } else if (msg->type == LogMessage::Type::kMatrix) {
+ output_length +=
+ sizeof(msg->matrix.type) + sizeof(uint32_t) + sizeof(uint16_t) +
+ sizeof(uint16_t) + msg->matrix.string_length;
+ CHECK(MessageType::IsPrimitive(msg->matrix.type));
+ }
+ LogFileMessageHeader *const output = writer.GetWritePosition(output_length);
+ char *output_strings = reinterpret_cast<char *>(output) + sizeof(*output);
+ output->name_size = msg->name_length;
+ output->message_size = msg->message_length;
+ output->source = msg->source;
+ output->level = msg->level;
+ output->time_sec = msg->seconds;
+ output->time_nsec = msg->nseconds;
+ output->sequence = msg->sequence;
+ memcpy(output_strings, msg->name, msg->name_length);
+
+ switch (msg->type) {
+ case LogMessage::Type::kString:
+ memcpy(output_strings + msg->name_length, msg->message,
+ msg->message_length);
+ output->type = LogFileMessageHeader::MessageType::kString;
+ break;
+ case LogMessage::Type::kStruct: {
+ char *position = output_strings + msg->name_length;
+
+ memcpy(position, &msg->structure.type_id,
+ sizeof(msg->structure.type_id));
+ position += sizeof(msg->structure.type_id);
+ output->message_size += sizeof(msg->structure.type_id);
+
+ const uint32_t length = msg->structure.string_length;
+ memcpy(position, &length, sizeof(length));
+ position += sizeof(length);
+ memcpy(position, msg->structure.serialized,
+ length + msg->message_length);
+ position += length + msg->message_length;
+ output->message_size += sizeof(length) + length;
+
+ output->type = LogFileMessageHeader::MessageType::kStruct;
+ } break;
+ case LogMessage::Type::kMatrix: {
+ char *position = output_strings + msg->name_length;
+
+ memcpy(position, &msg->matrix.type, sizeof(msg->matrix.type));
+ position += sizeof(msg->matrix.type);
+ output->message_size += sizeof(msg->matrix.type);
+
+ uint32_t length = msg->matrix.string_length;
+ memcpy(position, &length, sizeof(length));
+ position += sizeof(length);
+ output->message_size += sizeof(length);
+
+ uint16_t rows = msg->matrix.rows, cols = msg->matrix.cols;
+ memcpy(position, &rows, sizeof(rows));
+ position += sizeof(rows);
+ memcpy(position, &cols, sizeof(cols));
+ position += sizeof(cols);
+ output->message_size += sizeof(rows) + sizeof(cols);
+ CHECK_EQ(msg->message_length,
+ MessageType::Sizeof(msg->matrix.type) * rows * cols);
+
+ memcpy(position, msg->matrix.data, msg->message_length + length);
+ output->message_size += length;
+
+ output->type = LogFileMessageHeader::MessageType::kMatrix;
+ } break;
+ }
+
+ if (output->message_size - msg->message_length !=
+ output_length - raw_output_length) {
+ LOG(FATAL, "%zu != %zu\n", output->message_size - msg->message_length,
+ output_length - raw_output_length);
+ }
+
+ futex_set(&output->marker);
+
+ logging::linux_code::Free(msg);
+ }
+
+ Cleanup();
+ return 0;
+}
+
+} // namespace
+} // namespace linux_code
+} // namespace logging
+} // namespace aos
+
+int main() {
+ return ::aos::logging::linux_code::BinaryLogReaderMain();
+}
diff --git a/aos/linux_code/logging/linux_interface.cc b/aos/linux_code/logging/linux_interface.cc
new file mode 100644
index 0000000..b0a5c1b
--- /dev/null
+++ b/aos/linux_code/logging/linux_interface.cc
@@ -0,0 +1,85 @@
+#include "aos/linux_code/logging/linux_logging.h"
+
+#include <sys/prctl.h>
+
+#include "aos/linux_code/complex_thread_local.h"
+#include "aos/common/die.h"
+
+namespace aos {
+namespace logging {
+namespace internal {
+namespace {
+
+// TODO(brians): Differentiate between threads with the same name in the same
+// process.
+
+::std::string GetMyName() {
+ // The maximum number of characters that can make up a thread name.
+ // The docs are unclear if it can be 16 characters with no '\0', so we'll be
+ // safe by adding our own where necessary.
+ static const size_t kThreadNameLength = 16;
+
+ ::std::string process_name(program_invocation_short_name);
+
+ char thread_name_array[kThreadNameLength + 1];
+ if (prctl(PR_GET_NAME, thread_name_array) != 0) {
+ PDie("prctl(PR_GET_NAME, %p) failed", thread_name_array);
+ }
+ thread_name_array[sizeof(thread_name_array) - 1] = '\0';
+ ::std::string thread_name(thread_name_array);
+
+ // If the first bunch of characters are the same.
+ // We cut off comparing at the shorter of the 2 strings because one or the
+ // other often ends up cut off.
+ if (strncmp(thread_name.c_str(), process_name.c_str(),
+ ::std::min(thread_name.length(), process_name.length())) == 0) {
+ // This thread doesn't have an actual name.
+ return process_name;
+ }
+
+ return process_name + '.' + thread_name;
+}
+
+::aos::ComplexThreadLocal<Context> my_context;
+
+// True if we're going to delete the current Context object ASAP. The
+// reason for doing this instead of just deleting them is that tsan (at least)
+// doesn't like it when pthread_atfork handlers do complicated stuff and it's
+// not a great idea anyways.
+thread_local bool delete_current_context(false);
+
+} // namespace
+
+// Used in aos/linux_code/init.cc when a thread's name is changed.
+void ReloadThreadName() {
+ if (my_context.created()) {
+ ::std::string my_name = GetMyName();
+ if (my_name.size() + 1 > sizeof(Context::name)) {
+ Die("logging: process/thread name '%s' is too long\n",
+ my_name.c_str());
+ }
+ strcpy(my_context->name, my_name.c_str());
+ my_context->name_size = my_name.size();
+ }
+}
+
+Context *Context::Get() {
+ if (__builtin_expect(delete_current_context, false)) {
+ my_context.Clear();
+ delete_current_context = false;
+ }
+ if (__builtin_expect(!my_context.created(), false)) {
+ my_context.Create();
+ ReloadThreadName();
+ my_context->source = getpid();
+ }
+ return my_context.get();
+}
+
+void Context::Delete() {
+ delete_current_context = true;
+}
+
+} // namespace internal
+} // namespace logging
+} // namespace aos
diff --git a/aos/linux_code/logging/linux_logging.cc b/aos/linux_code/logging/linux_logging.cc
new file mode 100644
index 0000000..b13a740
--- /dev/null
+++ b/aos/linux_code/logging/linux_logging.cc
@@ -0,0 +1,145 @@
+#include "aos/linux_code/logging/linux_logging.h"
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <unistd.h>
+#include <limits.h>
+#include <inttypes.h>
+
+#include <algorithm>
+
+#include "aos/common/die.h"
+#include "aos/common/logging/logging_impl.h"
+#include "aos/linux_code/ipc_lib/queue.h"
+#include "aos/common/time.h"
+
+namespace aos {
+namespace logging {
+namespace linux_code {
+namespace {
+
+RawQueue *queue = NULL;
+
+int dropped_messages = 0;
+::aos::time::Time dropped_start, backoff_start;
+// Wait this long after dropping a message before even trying to write any more.
+constexpr ::aos::time::Time kDropBackoff = ::aos::time::Time::InSeconds(0.1);
+
+LogMessage *GetMessageOrDie() {
+ LogMessage *message = static_cast<LogMessage *>(queue->GetMessage());
+ if (message == NULL) {
+ LOG(FATAL, "%p->GetMessage() failed\n", queue);
+ } else {
+ return message;
+ }
+}
+
+class LinuxQueueLogImplementation : public LogImplementation {
+ __attribute__((format(GOOD_PRINTF_FORMAT_TYPE, 3, 0)))
+ virtual void DoLog(log_level level, const char *format, va_list ap) override {
+ LogMessage *message = GetMessageOrDie();
+ internal::FillInMessage(level, format, ap, message);
+ Write(message);
+ }
+
+ virtual void LogStruct(log_level level, const ::std::string &message_string,
+ size_t size, const MessageType *type,
+ const ::std::function<size_t(char *)> &serialize)
+ override {
+ LogMessage *message = GetMessageOrDie();
+ internal::FillInMessageStructure(level, message_string, size, type,
+ serialize, message);
+ Write(message);
+ }
+
+ virtual void LogMatrix(log_level level, const ::std::string &message_string,
+ uint32_t type_id, int rows, int cols, const void *data)
+ override {
+ LogMessage *message = GetMessageOrDie();
+ internal::FillInMessageMatrix(level, message_string, type_id, rows, cols,
+ data, message);
+ Write(message);
+ }
+};
+
+} // namespace
+
+void Register() {
+ Init();
+
+ queue = RawQueue::Fetch("LoggingQueue", sizeof(LogMessage), 1323, 40000);
+ if (queue == NULL) {
+ Die("logging: couldn't fetch queue\n");
+ }
+
+ AddImplementation(new LinuxQueueLogImplementation());
+}
+
+const LogMessage *ReadNext(Options<RawQueue> flags, int *index) {
+ return static_cast<const LogMessage *>(queue->ReadMessageIndex(flags, index));
+}
+
+const LogMessage *ReadNext() {
+ return ReadNext(RawQueue::kBlock);
+}
+
+const LogMessage *ReadNext(Options<RawQueue> flags) {
+ const LogMessage *r = NULL;
+ do {
+ r = static_cast<const LogMessage *>(queue->ReadMessage(flags));
+ // not blocking means return a NULL if that's what it gets
+ } while ((flags & RawQueue::kBlock) && r == NULL);
+ return r;
+}
+
+LogMessage *Get() {
+ return static_cast<LogMessage *>(queue->GetMessage());
+}
+
+void Free(const LogMessage *msg) {
+ queue->FreeMessage(msg);
+}
+
+void Write(LogMessage *msg) {
+ if (__builtin_expect(dropped_messages > 0, false)) {
+ ::aos::time::Time message_time =
+ ::aos::time::Time(msg->seconds, msg->nseconds);
+ if (message_time - backoff_start < kDropBackoff) {
+ ++dropped_messages;
+ queue->FreeMessage(msg);
+ return;
+ }
+
+ LogMessage *dropped_message = GetMessageOrDie();
+ internal::FillInMessageVarargs(
+ ERROR, dropped_message,
+ "%d logs starting at %" PRId32 ".%" PRId32 " dropped\n",
+ dropped_messages, dropped_start.sec(), dropped_start.nsec());
+ if (queue->WriteMessage(dropped_message, RawQueue::kNonBlock)) {
+ dropped_messages = 0;
+ } else {
+ // Don't even bother trying to write this message because it's not likely
+ // to work and it would be confusing to have one log in the middle of a
+ // string of failures get through.
+ ++dropped_messages;
+ backoff_start = message_time;
+ queue->FreeMessage(msg);
+ return;
+ }
+ }
+ if (!queue->WriteMessage(msg, RawQueue::kNonBlock)) {
+ if (dropped_messages == 0) {
+ dropped_start = backoff_start =
+ ::aos::time::Time(msg->seconds, msg->nseconds);
+ }
+ ++dropped_messages;
+ }
+}
+
+} // namespace linux_code
+} // namespace logging
+} // namespace aos
diff --git a/aos/linux_code/logging/linux_logging.h b/aos/linux_code/logging/linux_logging.h
new file mode 100644
index 0000000..63eeaf1
--- /dev/null
+++ b/aos/linux_code/logging/linux_logging.h
@@ -0,0 +1,34 @@
+#ifndef AOS_LINUX_CODE_LOGGING_LOGGING_H_
+#define AOS_LINUX_CODE_LOGGING_LOGGING_H_
+
+#include "aos/common/logging/logging_impl.h"
+#include "aos/common/util/options.h"
+
+namespace aos {
+
+class RawQueue;
+
+namespace logging {
+namespace linux_code {
+
+// Calls AddImplementation to register the usual linux logging implementation
+// which sends the messages through a queue. This implementation relies on
+// another process(es) to read the log messages that it puts into the queue.
+// This function is usually called by aos::Init*.
+void Register();
+
+// Fairly simple wrappers around the raw queue calls.
+
+// This one never returns NULL if flags contains BLOCK.
+const LogMessage *ReadNext(Options<RawQueue> flags);
+const LogMessage *ReadNext(Options<RawQueue> flags, int *index);
+const LogMessage *ReadNext();
+LogMessage *Get();
+void Free(const LogMessage *msg);
+void Write(LogMessage *msg);
+
+} // namespace linux_code
+} // namespace logging
+} // namespace aos
+
+#endif
diff --git a/aos/linux_code/logging/log_displayer.cc b/aos/linux_code/logging/log_displayer.cc
new file mode 100644
index 0000000..4050bd4
--- /dev/null
+++ b/aos/linux_code/logging/log_displayer.cc
@@ -0,0 +1,410 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <inttypes.h>
+
+#include <algorithm>
+#include <memory>
+#include <string>
+
+#include "aos/linux_code/configuration.h"
+#include "aos/linux_code/logging/binary_log_file.h"
+#include "aos/common/queue_types.h"
+#include "aos/common/logging/logging_impl.h"
+#include "aos/common/logging/logging_printf_formats.h"
+#include "aos/common/util/string_to_num.h"
+
+using ::aos::logging::linux_code::LogFileMessageHeader;
+
+namespace {
+
+const char *kArgsHelp = "[OPTION]... [FILE]\n"
+ "Display log file FILE (created by BinaryLogReader) to stdout.\n"
+ "FILE is \"aos_log-current\" by default.\n"
+ "FILE can also be \"-\" to read from standard input.\n"
+ "\n"
+ " -n, --name-prefix NAME "
+ "only display entries from processes which share NAME as a prefix\n"
+ " -N, --name NAME only display entries from processes named NAME\n"
+ " -l, --level LEVEL "
+ "only display log entries at least as important as LEVEL\n"
+ " -p, --pid PID only display log entries from process PID\n"
+ " -f, --follow "
+ "wait when the end of the file is reached (implies --end)\n"
+ " -t, --terminate stop when the end of file is reached (default)\n"
+ " -b, --beginning start at the beginning of the file (default)\n"
+ " -e, --end start at the end of the file\n"
+ " -s, --skip NUMBER skip NUMBER matching logs\n"
+ " -m, --max NUMBER only display up to NUMBER logs\n"
+ " // -o, --format FORMAT use FORMAT to display log entries\n"
+ " -h, --help display this help and exit\n"
+ "\n"
+ "LEVEL must be DEBUG, INFO, WARNING, ERROR, or FATAL.\n"
+ " It defaults to INFO.\n"
+ "\n"
+ "TODO(brians) implement the commented out ones.\n";
+
+const char *kExampleUsages = "To view logs from the shooter:\n"
+ "\t`log_displayer -n shooter`\n"
+ "To view debug logs from the shooter:\n"
+ "\t`log_displayer -n shooter -l DEBUG`\n"
+ "To view what the shooter is logging in realtime:\n"
+ "\t`log_displayer -f -n shooter`\n"
+ "To view shooter logs from an old log file:\n"
+ "\t`log_displayer aos_log-<number> -n shooter`\n"
+ "To view the statuses of the shooter hall effects in realtime:\n"
+ "\t`log_displayer -f -n shooter -l DEBUG | grep .Position`\n";
+
+void PrintHelpAndExit() {
+ fprintf(stderr, "Usage: %s %s", program_invocation_name, kArgsHelp);
+ fprintf(stderr, "\nExample usages:\n\n%s", kExampleUsages);
+
+ // Get the possible executables from start_list.txt.
+ FILE *start_list = fopen("start_list.txt", "r");
+ if (!start_list) {
+ ::std::string path(::aos::configuration::GetRootDirectory());
+ path += "/start_list.txt";
+ start_list = fopen(path.c_str(), "r");
+ if (!start_list) {
+ printf("\nCannot open start_list.txt. This means that the\n"
+ "possible arguments for the -n option cannot be shown. log_displayer\n"
+ "looks for start_list.txt in the current working directory and in\n"
+ "%s.\n\n", ::aos::configuration::GetRootDirectory());
+ PLOG(FATAL, "Unable to open start_list.txt");
+ }
+ }
+
+ // Get file size.
+ if (fseek(start_list, 0, SEEK_END)) {
+ PLOG(FATAL, "fseek() failed while reading start_list.txt");
+ }
+ int size = ftell(start_list);
+ if (size < 0) {
+ PLOG(FATAL, "ftell() failed while reading start_list.txt");
+ }
+ rewind(start_list);
+
+ ::std::unique_ptr<char[]> contents(new char[size + 1]);
+ if (contents == NULL) {
+ LOG(FATAL, "malloc() failed while reading start_list.txt.\n");
+ }
+ size_t bytes_read = fread(contents.get(), 1, size, start_list);
+ if (bytes_read < static_cast<size_t>(size)) {
+ LOG(FATAL, "Read %zu bytes from start_list.txt, expected %d.\n",
+ bytes_read, size);
+ }
+
+ // printf doesn't like strings without the \0.
+ contents[size] = '\0';
+ fprintf(stderr, "\nPossible arguments for the -n option:\n%s", contents.get());
+
+ if (fclose(start_list)) {
+ LOG(FATAL, "fclose() failed.\n");
+ }
+
+ exit(EXIT_SUCCESS);
+}
+
+} // namespace
+
+int main(int argc, char **argv) {
+ const char *filter_name = nullptr, *filter_exact_name = nullptr;
+ size_t filter_length = 0;
+ log_level filter_level = INFO;
+ bool follow = false;
+ // Whether we need to skip everything until we get to the end of the file.
+ bool skip_to_end = false;
+ const char *filename = "aos_log-current";
+ int display_max = 0;
+ int32_t source_pid = -1;
+
+ ::aos::logging::Init();
+ ::aos::logging::AddImplementation(
+ new ::aos::logging::StreamLogImplementation(stdout));
+
+ while (true) {
+ static struct option long_options[] = {
+ {"name-prefix", required_argument, NULL, 'n'},
+ {"name", required_argument, NULL, 'N'},
+ {"level", required_argument, NULL, 'l'},
+ {"pid", required_argument, NULL, 'p'},
+
+ {"follow", no_argument, NULL, 'f'},
+ {"terminate", no_argument, NULL, 't'},
+ {"beginning", no_argument, NULL, 'b'},
+ {"end", no_argument, NULL, 'e'},
+ {"skip", required_argument, NULL, 's'},
+ {"max", required_argument, NULL, 'm'},
+
+ {"format", required_argument, NULL, 'o'},
+
+ {"help", no_argument, NULL, 'h'},
+ {0, 0, 0, 0}
+ };
+ int option_index = 0;
+
+ const int c = getopt_long(argc, argv, "N:n:l:p:fts:m:o:h",
+ long_options, &option_index);
+ if (c == -1) { // if we're at the end
+ break;
+ }
+ switch (c) {
+ case 0:
+ fputs("LogDisplayer: got a 0 option but didn't set up any\n", stderr);
+ abort();
+ case 'n':
+ filter_name = optarg;
+ filter_exact_name = nullptr;
+ filter_length = strlen(filter_name);
+ break;
+ case 'N':
+ filter_exact_name = optarg;
+ filter_name = nullptr;
+ filter_length = strlen(filter_name);
+ break;
+ case 'l':
+ filter_level = ::aos::logging::str_log(optarg);
+ if (filter_level == LOG_UNKNOWN) {
+ fprintf(stderr, "LogDisplayer: unknown log level '%s'\n", optarg);
+ exit(EXIT_FAILURE);
+ }
+ break;
+ case 'p':
+ if (!::aos::util::StringToNumber(::std::string(optarg), &source_pid)) {
+ fprintf(stderr, "ERROR: -p expects a number, not '%s'.\n", optarg);
+ exit(EXIT_FAILURE);
+ }
+ if (source_pid < 0) {
+ fprintf(stderr, "LogDisplayer: invalid pid '%s'\n", optarg);
+ exit(EXIT_FAILURE);
+ }
+ break;
+ case 'f':
+ follow = true;
+ skip_to_end = true;
+ break;
+ case 't':
+ follow = false;
+ break;
+ case 'b':
+ skip_to_end = false;
+ break;
+ case 'e':
+ skip_to_end = true;
+ break;
+ case 'm':
+ if (!::aos::util::StringToNumber(::std::string(optarg), &display_max)) {
+ fprintf(stderr, "ERROR: -m expects a number, not '%s'.\n", optarg);
+ exit(EXIT_FAILURE);
+ }
+ if (display_max <= 0) {
+ fprintf(stderr, "LogDisplayer: invalid max log number '%s'\n",
+ optarg);
+ exit(EXIT_FAILURE);
+ }
+ break;
+ case 'o':
+ abort();
+ break;
+ case 'h':
+ PrintHelpAndExit();
+ break;
+ case '?':
+ break;
+ default:
+ fprintf(stderr, "LogDisplayer: in a bad spot (%s: %d)\n",
+ __FILE__, __LINE__);
+ abort();
+ }
+ }
+
+ if (optind < argc) {
+ // We got a filename.
+ filename = argv[optind++];
+ }
+ if (optind < argc) {
+ fputs("non-option ARGV-elements: ", stderr);
+ while (optind < argc) {
+ fprintf(stderr, "%s\n", argv[optind++]);
+ }
+ }
+
+ int fd;
+ if (strcmp(filename, "-") == 0) {
+ if (skip_to_end) {
+ fputs("Can't skip to end of stdin!\n", stderr);
+ return EXIT_FAILURE;
+ }
+ fd = STDIN_FILENO;
+ } else {
+ fd = open(filename, O_RDONLY);
+ }
+
+ fprintf(stderr, "displaying down to level %s from file '%s'\n",
+ ::aos::logging::log_str(filter_level), filename);
+
+ if (fd == -1) {
+ PLOG(FATAL, "couldn't open file '%s' for reading", filename);
+ }
+ ::aos::logging::linux_code::LogFileReader reader(fd);
+
+ if (skip_to_end) {
+ fputs("skipping old logs...\n", stderr);
+ reader.SkipToLastSeekablePage();
+ }
+
+ const LogFileMessageHeader *msg;
+ int displayed = 0;
+ do {
+ msg = reader.ReadNextMessage(follow);
+ if (msg == NULL) {
+ fputs("reached end of file\n", stderr);
+ return 0;
+ }
+
+ if (msg->type == LogFileMessageHeader::MessageType::kStructType) {
+ size_t bytes = msg->message_size;
+ ::aos::MessageType *type = ::aos::MessageType::Deserialize(
+ reinterpret_cast<const char *>(msg + 1), &bytes);
+ if (type == nullptr) {
+ LOG(INFO, "Trying old version of type decoding.\n");
+ bytes = msg->message_size;
+ type = ::aos::MessageType::Deserialize(
+ reinterpret_cast<const char *>(msg + 1), &bytes, false);
+ }
+
+ if (type == nullptr) {
+ LOG(WARNING, "Error deserializing MessageType of size %" PRIx32
+ " starting at %zx.\n",
+ msg->message_size, reader.file_offset(msg + 1));
+ } else {
+ ::aos::type_cache::Add(*type);
+ }
+ continue;
+ }
+
+ if (source_pid >= 0 && msg->source != source_pid) {
+ // Message is from the wrong process.
+ continue;
+ }
+
+ if (skip_to_end) {
+ if (reader.IsLastPage()) {
+ fputs("done skipping old logs\n", stderr);
+ skip_to_end = false;
+ } else {
+ continue;
+ }
+ }
+
+ if (::aos::logging::log_gt_important(filter_level, msg->level)) continue;
+
+ const char *position =
+ reinterpret_cast<const char *>(msg + 1);
+
+ if (filter_name != nullptr) {
+ const size_t compare_length =
+ ::std::min<size_t>(filter_length, msg->name_size);
+ if (memcmp(filter_name, position, compare_length) != 0) {
+ continue;
+ }
+ if (compare_length < msg->name_size) {
+ if (position[compare_length] != '.') continue;
+ }
+ }
+
+ if (filter_exact_name != nullptr) {
+ if (filter_length != msg->name_size) continue;
+ if (memcmp(filter_exact_name, position, filter_length) != 0) {
+ continue;
+ }
+ }
+
+ if (display_max && displayed++ >= display_max) {
+ fputs("Not displaying the rest of the messages.\n", stderr);
+ return 0;
+ }
+
+ position += msg->name_size;
+
+#define BASE_ARGS \
+ AOS_LOGGING_BASE_ARGS( \
+ msg->name_size, reinterpret_cast<const char *>(msg + 1), msg->source, \
+ msg->sequence, msg->level, msg->time_sec, msg->time_nsec)
+ switch (msg->type) {
+ case LogFileMessageHeader::MessageType::kString:
+ fprintf(stdout, AOS_LOGGING_BASE_FORMAT "%.*s", BASE_ARGS,
+ static_cast<int>(msg->message_size), position);
+ break;
+ case LogFileMessageHeader::MessageType::kStruct: {
+ uint32_t type_id;
+ memcpy(&type_id, position, sizeof(type_id));
+ position += sizeof(type_id);
+
+ uint32_t string_length;
+ memcpy(&string_length, position, sizeof(string_length));
+ position += sizeof(string_length);
+
+ char buffer[2048];
+ size_t output_length = sizeof(buffer);
+ size_t input_length =
+ msg->message_size -
+ (sizeof(type_id) + sizeof(uint32_t) + string_length);
+ if (!PrintMessage(buffer, &output_length, position + string_length,
+ &input_length, ::aos::type_cache::Get(type_id))) {
+ LOG(FATAL, "printing message (%.*s) of type %s into %zu-byte buffer "
+ "failed\n",
+ static_cast<int>(string_length), position,
+ ::aos::type_cache::Get(type_id).name.c_str(), sizeof(buffer));
+ }
+ if (input_length > 0) {
+ LOG(WARNING, "%zu extra bytes on message of type %s\n",
+ input_length, ::aos::type_cache::Get(type_id).name.c_str());
+ }
+ fprintf(stdout, AOS_LOGGING_BASE_FORMAT "%.*s: %.*s\n", BASE_ARGS,
+ static_cast<int>(string_length), position,
+ static_cast<int>(sizeof(buffer) - output_length), buffer);
+ } break;
+ case LogFileMessageHeader::MessageType::kMatrix: {
+ uint32_t type;
+ memcpy(&type, position, sizeof(type));
+ position += sizeof(type);
+
+ uint32_t string_length;
+ memcpy(&string_length, position, sizeof(string_length));
+ position += sizeof(string_length);
+
+ uint16_t rows;
+ memcpy(&rows, position, sizeof(rows));
+ position += sizeof(rows);
+ uint16_t cols;
+ memcpy(&cols, position, sizeof(cols));
+ position += sizeof(cols);
+
+ const size_t matrix_bytes =
+ msg->message_size -
+ (sizeof(type) + sizeof(uint32_t) + sizeof(uint16_t) +
+ sizeof(uint16_t) + string_length);
+ CHECK_EQ(matrix_bytes, ::aos::MessageType::Sizeof(type) * rows * cols);
+ char buffer[2048];
+ size_t output_length = sizeof(buffer);
+ if (!::aos::PrintMatrix(buffer, &output_length,
+ position + string_length, type, rows, cols)) {
+ LOG(FATAL, "printing %dx%d matrix of type %" PRIu32 " failed\n", rows,
+ cols, type);
+ }
+ fprintf(stdout, AOS_LOGGING_BASE_FORMAT "%.*s: %.*s\n", BASE_ARGS,
+ static_cast<int>(string_length), position,
+ static_cast<int>(sizeof(buffer) - output_length), buffer);
+ } break;
+ case LogFileMessageHeader::MessageType::kStructType:
+ LOG(FATAL, "shouldn't get here\n");
+ break;
+ }
+#undef BASE_ARGS
+ } while (msg != NULL);
+}
diff --git a/aos/linux_code/logging/log_replay.cc b/aos/linux_code/logging/log_replay.cc
new file mode 100644
index 0000000..32991de
--- /dev/null
+++ b/aos/linux_code/logging/log_replay.cc
@@ -0,0 +1,44 @@
+#include "aos/linux_code/logging/log_replay.h"
+
+namespace aos {
+namespace logging {
+namespace linux_code {
+
+bool LogReplayer::ProcessMessage() {
+ const LogFileMessageHeader *message = reader_->ReadNextMessage(false);
+ if (message == nullptr) return true;
+ if (message->type != LogFileMessageHeader::MessageType::kStruct) return false;
+
+ const char *position = reinterpret_cast<const char *>(message + 1);
+
+ ::std::string process(position, message->name_size);
+ position += message->name_size;
+
+ uint32_t type_id;
+ memcpy(&type_id, position, sizeof(type_id));
+ position += sizeof(type_id);
+
+ uint32_t message_length;
+ memcpy(&message_length, position, sizeof(message_length));
+ position += sizeof(message_length);
+ ::std::string message_text(position, message_length);
+ position += message_length;
+
+ size_t split_index = message_text.find_first_of(':') + 2;
+ split_index = message_text.find_first_of(':', split_index) + 2;
+ message_text = message_text.substr(split_index);
+
+ auto handler = handlers_.find(Key(process, message_text));
+ if (handler == handlers_.end()) return false;
+
+ handler->second->HandleStruct(
+ ::aos::time::Time(message->time_sec, message->time_nsec), type_id,
+ position,
+ message->message_size -
+ (sizeof(type_id) + sizeof(message_length) + message_length));
+ return false;
+}
+
+} // namespace linux_code
+} // namespace logging
+} // namespace aos
diff --git a/aos/linux_code/logging/log_replay.h b/aos/linux_code/logging/log_replay.h
new file mode 100644
index 0000000..942f26f
--- /dev/null
+++ b/aos/linux_code/logging/log_replay.h
@@ -0,0 +1,164 @@
+#ifndef AOS_LINUX_CODE_LOGGING_LOG_REPLAY_H_
+#define AOS_LINUX_CODE_LOGGING_LOG_REPLAY_H_
+
+#include <unordered_map>
+#include <string>
+#include <functional>
+#include <memory>
+
+#include "aos/linux_code/logging/binary_log_file.h"
+#include "aos/common/queue.h"
+#include "aos/common/logging/logging.h"
+#include "aos/common/macros.h"
+#include "aos/linux_code/ipc_lib/queue.h"
+#include "aos/common/queue_types.h"
+
+namespace aos {
+namespace logging {
+namespace linux_code {
+
+// Manages pulling logged queue messages out of log files.
+//
+// Basic usage:
+// - Use the Add* methods to register handlers for various message sources.
+// - Call OpenFile to open a log file.
+// - Call ProcessMessage repeatedly until it returns true.
+//
+// This code could do something to adapt similar-but-not-identical
+// messages to the current versions, but currently it will LOG(FATAL) if any of
+// the messages don't match up exactly.
+class LogReplayer {
+ public:
+ LogReplayer() {}
+
+ // Gets ready to read messages from fd.
+ // Does not take ownership of fd.
+ void OpenFile(int fd) {
+ reader_.reset(new LogFileReader(fd));
+ }
+ // Closes the currently open file.
+ void CloseCurrentFile() { reader_.reset(); }
+ // Returns true if we have a file which is currently open.
+ bool HasCurrentFile() const { return reader_.get() != nullptr; }
+
+ // Processes a single message from the currently open file.
+ // Returns true if there are no more messages in the file.
+ // This will not call any of the handlers if the next message either has no
+ // registered handlers or is not a queue message.
+ bool ProcessMessage();
+
+ // Adds a handler for messages with a certain string from a certain process.
+ // T must be a Message with the same format as the messages generated by
+ // the .q files.
+ // LOG(FATAL)s for duplicate handlers.
+ template <class T>
+ void AddHandler(const ::std::string &process_name,
+ const ::std::string &log_message,
+ ::std::function<void(const T &message)> handler) {
+ CHECK(handlers_.emplace(
+ ::std::piecewise_construct,
+ ::std::forward_as_tuple(process_name, log_message),
+ ::std::forward_as_tuple(::std::unique_ptr<StructHandlerInterface>(
+ new TypedStructHandler<T>(handler)))).second);
+ }
+
+ // Adds a handler which takes messages and places them directly on a queue.
+ // T must be a Message with the same format as the messages generated by
+ // the .q files.
+ template <class T>
+ void AddDirectQueueSender(const ::std::string &process_name,
+ const ::std::string &log_message,
+ const ::aos::Queue<T> &queue) {
+ AddHandler(process_name, log_message,
+ ::std::function<void(const T &)>(
+ QueueDumpStructHandler<T>(queue.name())));
+ }
+
+ private:
+ // A generic handler of struct log messages.
+ class StructHandlerInterface {
+ public:
+ virtual ~StructHandlerInterface() {}
+
+ virtual void HandleStruct(::aos::time::Time log_time, uint32_t type_id,
+ const void *data, size_t data_size) = 0;
+ };
+
+ // Converts struct log messages to a message type and passes it to an
+ // ::std::function.
+ template <class T>
+ class TypedStructHandler : public StructHandlerInterface {
+ public:
+ TypedStructHandler(::std::function<void(const T &message)> handler)
+ : handler_(handler) {}
+
+ void HandleStruct(::aos::time::Time log_time, uint32_t type_id,
+ const void *data, size_t data_size) override {
+ CHECK_EQ(type_id, T::GetType()->id);
+ T message;
+ CHECK_EQ(data_size, T::Size());
+ CHECK_EQ(data_size, message.Deserialize(static_cast<const char *>(data)));
+ message.sent_time = log_time;
+ handler_(message);
+ }
+
+ private:
+ const ::std::function<void(T message)> handler_;
+ };
+
+ // A callable class which dumps messages straight to a queue.
+ template <class T>
+ class QueueDumpStructHandler {
+ public:
+ QueueDumpStructHandler(const ::std::string &queue_name)
+ : queue_(RawQueue::Fetch(queue_name.c_str(), sizeof(T), T::kHash,
+ T::kQueueLength)) {}
+
+ void operator()(const T &message) {
+ LOG_STRUCT(DEBUG, "re-sending", message);
+ void *raw_message = queue_->GetMessage();
+ CHECK_NOTNULL(raw_message);
+ memcpy(raw_message, &message, sizeof(message));
+ CHECK(queue_->WriteMessage(raw_message, RawQueue::kOverride));
+ }
+
+ private:
+ ::aos::RawQueue *const queue_;
+ };
+
+ // A key for specifying log messages to give to a certain handler.
+ struct Key {
+ Key(const ::std::string &process_name, const ::std::string &log_message)
+ : process_name(process_name), log_message(log_message) {}
+
+ ::std::string process_name;
+ ::std::string log_message;
+ };
+
+ struct KeyHash {
+ size_t operator()(const Key &key) const {
+ return string_hash(key.process_name) ^
+ (string_hash(key.log_message) << 1);
+ }
+
+ private:
+ const ::std::hash<::std::string> string_hash = ::std::hash<::std::string>();
+ };
+ struct KeyEqual {
+ bool operator()(const Key &a, const Key &b) const {
+ return a.process_name == b.process_name && a.log_message == b.log_message;
+ }
+ };
+
+ ::std::unordered_map<const Key, ::std::unique_ptr<StructHandlerInterface>,
+ KeyHash, KeyEqual> handlers_;
+ ::std::unique_ptr<LogFileReader> reader_;
+
+ DISALLOW_COPY_AND_ASSIGN(LogReplayer);
+};
+
+} // namespace linux_code
+} // namespace logging
+} // namespace aos
+
+#endif // AOS_LINUX_CODE_LOGGING_LOG_REPLAY_H_
diff --git a/aos/linux_code/logging/log_streamer.cc b/aos/linux_code/logging/log_streamer.cc
new file mode 100644
index 0000000..2fe7c04
--- /dev/null
+++ b/aos/linux_code/logging/log_streamer.cc
@@ -0,0 +1,51 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <time.h>
+#include <string.h>
+#include <string>
+#include <unistd.h>
+#include <sys/types.h>
+#include <pwd.h>
+#include <fcntl.h>
+#include <inttypes.h>
+
+#include "aos/linux_code/logging/linux_logging.h"
+#include "aos/linux_code/init.h"
+#include "aos/linux_code/ipc_lib/queue.h"
+#include "aos/common/logging/logging_impl.h"
+#include "aos/common/time.h"
+
+namespace aos {
+namespace logging {
+namespace linux_code {
+namespace {
+
+int LogStreamerMain() {
+ InitNRT();
+
+ const time::Time now = time::Time::Now();
+ printf("starting at %" PRId32 "s%" PRId32 "ns-----------------------------\n",
+ now.sec(), now.nsec());
+
+ while (true) {
+ const LogMessage *const msg = ReadNext();
+ if (msg == NULL) continue;
+
+ internal::PrintMessage(stdout, *msg);
+
+ logging::linux_code::Free(msg);
+ }
+
+ Cleanup();
+ return 0;
+}
+
+} // namespace
+} // namespace linux_code
+} // namespace logging
+} // namespace aos
+
+int main() {
+ return ::aos::logging::linux_code::LogStreamerMain();
+}
diff --git a/aos/linux_code/logging/logging.gyp b/aos/linux_code/logging/logging.gyp
new file mode 100644
index 0000000..2ae9e0b
--- /dev/null
+++ b/aos/linux_code/logging/logging.gyp
@@ -0,0 +1,73 @@
+{
+ 'targets': [
+ # linux_* is dealt with by aos/build/aos.gyp:logging.
+ {
+ 'target_name': 'log_replay',
+ 'type': 'static_library',
+ 'sources': [
+ 'log_replay.cc',
+ ],
+ 'dependencies': [
+ 'binary_log_file',
+ '<(AOS)/common/common.gyp:queues',
+ '<(AOS)/build/aos.gyp:logging',
+ '<(AOS)/linux_code/ipc_lib/ipc_lib.gyp:queue',
+ ],
+ },
+ {
+ 'target_name': 'binary_log_writer',
+ 'type': 'executable',
+ 'sources': [
+ 'binary_log_writer.cc',
+ ],
+ 'dependencies': [
+ '<(AOS)/build/aos.gyp:logging',
+ '<(AOS)/linux_code/linux_code.gyp:init',
+ '<(AOS)/linux_code/linux_code.gyp:configuration',
+ '<(AOS)/common/common.gyp:die',
+ 'binary_log_file',
+ '<(AOS)/common/common.gyp:queue_types',
+ ],
+ },
+ {
+ 'target_name': 'log_streamer',
+ 'type': 'executable',
+ 'sources': [
+ 'log_streamer.cc',
+ ],
+ 'dependencies': [
+ '<(AOS)/build/aos.gyp:logging',
+ '<(AOS)/linux_code/linux_code.gyp:init',
+ '<(AOS)/common/common.gyp:time',
+ '<(AOS)/linux_code/ipc_lib/ipc_lib.gyp:queue',
+ ],
+ },
+ {
+ 'target_name': 'log_displayer',
+ 'type': 'executable',
+ 'sources': [
+ 'log_displayer.cc',
+ ],
+ 'dependencies': [
+ '<(AOS)/build/aos.gyp:logging',
+ '<(AOS)/linux_code/linux_code.gyp:init',
+ 'binary_log_file',
+ '<(AOS)/common/common.gyp:queue_types',
+ '<(AOS)/linux_code/linux_code.gyp:configuration',
+ ],
+ },
+ {
+ 'target_name': 'binary_log_file',
+ 'type': 'static_library',
+ 'sources': [
+ 'binary_log_file.cc',
+ ],
+ 'dependencies': [
+ '<(AOS)/build/aos.gyp:logging',
+ ],
+ 'export_dependent_settings': [
+ '<(AOS)/build/aos.gyp:logging',
+ ],
+ },
+ ],
+}
diff --git a/aos/linux_code/output/HTTPServer.cpp b/aos/linux_code/output/HTTPServer.cpp
new file mode 100644
index 0000000..d18572b
--- /dev/null
+++ b/aos/linux_code/output/HTTPServer.cpp
@@ -0,0 +1,152 @@
+#include "aos/linux_code/output/HTTPServer.h"
+
+#include <inttypes.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+
+#include <memory>
+
+#include "event2/event.h"
+
+#include "aos/common/scoped_fd.h"
+#include "aos/common/unique_malloc_ptr.h"
+
+namespace aos {
+namespace http {
+
+HTTPServer::HTTPServer(const char *directory, uint16_t port) :
+ directory_(directory), base_(event_base_new()), http_(evhttp_new(base_)) {
+ if (base_ == NULL) {
+ LOG(FATAL, "couldn't create an event_base\n");
+ }
+ if (http_ == NULL) {
+ LOG(FATAL, "couldn't create an evhttp\n");
+ }
+ if (evhttp_bind_socket(http_, "0.0.0.0", port) != 0) {
+ LOG(FATAL, "evhttp_bind_socket(%p, \"0.0.0.0\", %" PRIu16 ") failed\n",
+ http_, port);
+ }
+ evhttp_set_gencb(http_, StaticServeFile, this);
+}
+
+void HTTPServer::AddPage(const std::string &path,
+ void (*handler)(evhttp_request *, void *), void *data) {
+ switch (evhttp_set_cb(http_, path.c_str(), handler, data)) {
+ case 0:
+ LOG(DEBUG, "set callback handler for '%s'\n", path.c_str());
+ break;
+ case -1:
+ LOG(INFO, "changed callback handler for '%s'\n", path.c_str());
+ break;
+ default:
+ LOG(WARNING, "evhttp_set_cb(%p, %s, %p, %p) failed\n", http_, path.c_str(),
+ handler, data);
+ break;
+ }
+}
+
+void HTTPServer::AddStandardHeaders(evhttp_request *request) {
+ if (evhttp_add_header(evhttp_request_get_output_headers(request),
+ "Server", "aos::HTTPServer/0.0") == -1) {
+ LOG(WARNING, "adding Server header failed\n");
+ }
+}
+
+namespace {
+// All of these functions return false, NULL, or -1 if they fail (and send back
+// an error).
+
+// Returns the path of the file that is being requested.
+const char *GetPath(evhttp_request *request) {
+ // Docs are unclear whether this needs freeing, but it looks like it just
+ // returns an internal field of the request.
+ // Running valgrind with no freeing of uri or path doesn't report anything
+ // related to this code.
+ const evhttp_uri *uri = evhttp_request_get_evhttp_uri(request);
+ const char *path = evhttp_uri_get_path(uri);
+ if (path == NULL) {
+ evhttp_send_error(request, HTTP_BADREQUEST, "need a path");
+ return NULL;
+ }
+ if (strstr(path, "..") != NULL) {
+ evhttp_send_error(request, HTTP_NOTFOUND, "no .. allowed!!");
+ return NULL;
+ }
+ return path;
+}
+// Returns an fd open for reading for the file at "directory/path".
+int OpenFile(evhttp_request *request, const char *path,
+ const char *directory) {
+ char *temp;
+ if (asprintf(&temp, "%s/%s", directory, path) == -1) {
+ PLOG(WARNING, "asprintf(%p, \"%%s/%%s\", %p, %p) failed",
+ &temp, directory, path);
+ evhttp_send_error(request, HTTP_INTERNAL, NULL);
+ return -1;
+ }
+ const unique_c_ptr<char> filename(temp);
+ ScopedFD file(open(filename.get(), O_RDONLY));
+ if (!file) {
+ if (errno == ENOENT) {
+ evhttp_send_error(request, HTTP_NOTFOUND, NULL);
+ return -1;
+ }
+ PLOG(ERROR, "open('%s', 0) failed", filename.get(),
+ evhttp_send_error(request, HTTP_INTERNAL, NULL);
+ return -1;
+ }
+ return file.release();
+}
+// Returns the size of the file specified by the given fd.
+off_t GetSize(int file) {
+ struct stat info;
+ if (fstat(file, &info) == -1) {
+ PLOG(ERROR, "stat(%d, %p) failed", file, &info);
+ return -1;
+ }
+ return info.st_size;
+}
+bool SendFileResponse(evhttp_request *request, int file_num) {
+ ScopedFD file(file_num);
+ const off_t size = GetSize(file.get());
+ if (size == -1) {
+ evhttp_send_error(request, HTTP_INTERNAL, NULL);
+ return false;
+ }
+ evbuffer *const buf = evhttp_request_get_output_buffer(request);
+ if (evbuffer_add_file(buf, file.get(), 0, size) == -1) {
+ LOG(WARNING, "evbuffer_add_file(%p, %d, 0, %jd) failed\n", buf,
+ file.get(), static_cast<intmax_t>(size));
+ evhttp_send_error(request, HTTP_INTERNAL, NULL);
+ return false;
+ } else {
+ // it succeeded, so evhttp takes ownership
+ file.release();
+ }
+ evhttp_send_reply(request, HTTP_OK, NULL, NULL);
+ return true;
+}
+
+} // namespace
+void HTTPServer::ServeFile(evhttp_request *request) {
+ AddStandardHeaders(request);
+
+ const char *path = GetPath(request);
+ if (path == NULL) return;
+
+ ScopedFD file(OpenFile(request, path, directory_));
+ if (!file) return;
+
+ if (!SendFileResponse(request, file.release())) return;
+}
+
+void HTTPServer::Run() {
+ event_base_dispatch(base_);
+ LOG(FATAL, "event_base_dispatch returned\n");
+}
+
+} // namespace http
+} // namespace aos
diff --git a/aos/linux_code/output/HTTPServer.h b/aos/linux_code/output/HTTPServer.h
new file mode 100644
index 0000000..99eb295
--- /dev/null
+++ b/aos/linux_code/output/HTTPServer.h
@@ -0,0 +1,58 @@
+#include "event2/buffer.h"
+#include "event2/http.h"
+
+#include <string>
+
+namespace aos {
+namespace http {
+
+// An HTTP server that serves files from a directory using libevent.
+// Also allows configuring certain URLs to be dynamically generated.
+class HTTPServer {
+ public:
+ HTTPServer(const char *directory, uint16_t port);
+ // Starts serving pages.
+ // Might not clean up everything before returning.
+ void Run();
+ protected:
+ template<class T> class MemberHandler {
+ public:
+ typedef void (T::*Handler)(evhttp_request *);
+ struct Holder {
+ T *self;
+ Handler handler;
+ };
+ static void Call(evhttp_request *request, void *handler_in) {
+ const Holder *const holder = static_cast<Holder *>(handler_in);
+ AddStandardHeaders(request);
+ ((holder->self)->*(holder->handler))(request);
+ }
+ };
+ void AddPage(const std::string &path, void (*handler)(evhttp_request *, void *),
+ void *data);
+ template<class T> void AddPage(const std::string &path,
+ typename MemberHandler<T>::Handler handler,
+ T *self) {
+ // have to put "typename" in, so the typedef makes it clearer
+ typedef typename MemberHandler<T>::Holder HolderType;
+ AddPage(path, MemberHandler<T>::Call, new HolderType{self, handler});
+ }
+ // This gets set up as the generic handler.
+ // It can also be called separately to serve the file that the request is
+ // requesting from the filesystem.
+ void ServeFile(evhttp_request *request);
+ private:
+ // The directory where files to be served come from.
+ const char *directory_;
+ // The main libevent structure.
+ event_base *const base_;
+ // The libevent HTTP server handle.
+ evhttp *const http_;
+ static void AddStandardHeaders(evhttp_request *request);
+ static void StaticServeFile(evhttp_request *request, void *self) {
+ static_cast<HTTPServer *>(self)->ServeFile(request);
+ }
+};
+
+} // namespace http
+} // namespace aos
diff --git a/aos/linux_code/output/ctemplate_cache.cc b/aos/linux_code/output/ctemplate_cache.cc
new file mode 100644
index 0000000..cf961c6
--- /dev/null
+++ b/aos/linux_code/output/ctemplate_cache.cc
@@ -0,0 +1,24 @@
+#include "aos/linux_code/output/ctemplate_cache.h"
+
+#include "aos/linux_code/configuration.h"
+#include "aos/common/once.h"
+
+namespace aos {
+namespace http {
+
+namespace {
+ctemplate::TemplateCache *CreateTemplateCache() {
+ ctemplate::TemplateCache *r = new ctemplate::TemplateCache();
+
+ r->SetTemplateRootDirectory(configuration::GetRootDirectory());
+
+ return r;
+}
+} // namespace
+ctemplate::TemplateCache *get_template_cache() {
+ static Once<ctemplate::TemplateCache> once(CreateTemplateCache);
+ return once.Get();
+}
+
+} // namespace http
+} // namespace aos
diff --git a/aos/linux_code/output/ctemplate_cache.h b/aos/linux_code/output/ctemplate_cache.h
new file mode 100644
index 0000000..7e5dc3d
--- /dev/null
+++ b/aos/linux_code/output/ctemplate_cache.h
@@ -0,0 +1,12 @@
+#include "ctemplate/template_cache.h"
+
+namespace aos {
+namespace http {
+
+// Retrieves the cache used by all of the aos functions etc.
+// This cache will have its root directory set to the directory where the
+// executable is running from.
+ctemplate::TemplateCache *get_template_cache();
+
+} // namespace http
+} // namespace aos
diff --git a/aos/linux_code/output/evhttp_ctemplate_emitter.cc b/aos/linux_code/output/evhttp_ctemplate_emitter.cc
new file mode 100644
index 0000000..2301ffb
--- /dev/null
+++ b/aos/linux_code/output/evhttp_ctemplate_emitter.cc
@@ -0,0 +1,18 @@
+#include "aos/linux_code/output/evhttp_ctemplate_emitter.h"
+
+#include "aos/common/logging/logging.h"
+
+namespace aos {
+namespace http {
+
+void EvhttpCtemplateEmitter::Emit(const char *s, size_t slen) {
+ if (error_) return;
+ if (evbuffer_add(buf_, s, slen) != 0) {
+ LOG(ERROR, "evbuffer_add(%p, %p, %zd) failed\n",
+ buf_, s, slen);
+ error_ = true;
+ }
+}
+
+} // namespace http
+} // namespace aos
diff --git a/aos/linux_code/output/evhttp_ctemplate_emitter.h b/aos/linux_code/output/evhttp_ctemplate_emitter.h
new file mode 100644
index 0000000..5b8e96a
--- /dev/null
+++ b/aos/linux_code/output/evhttp_ctemplate_emitter.h
@@ -0,0 +1,34 @@
+#ifndef AOS_LINUX_CODE_OUTPUT_EVHTTP_CTEMPLATE_EMITTER_H_
+#define AOS_LINUX_CODE_OUTPUT_EVHTTP_CTEMPLATE_EMITTER_H_
+
+#include <string.h>
+
+#include "event2/buffer.h"
+#include "ctemplate/template_emitter.h"
+
+namespace aos {
+namespace http {
+
+// Writes everything directly into an evbuffer*.
+// Handles errors by refusing to write anything else into the buffer and storing
+// the state (which can be retrieved with error()).
+class EvhttpCtemplateEmitter : public ctemplate::ExpandEmitter {
+ public:
+ EvhttpCtemplateEmitter(evbuffer *buf) : buf_(buf), error_(false) {}
+ virtual void Emit(char c) { Emit(&c, 1); };
+ virtual void Emit(const std::string& s) { Emit(s.data(), s.size()); };
+ virtual void Emit(const char* s) { Emit(s, strlen(s)); }
+ virtual void Emit(const char* s, size_t slen);
+ // Retrieves whether or not there has been an error. If true, the error will
+ // already have been logged.
+ bool error() { return error_; }
+
+ private:
+ evbuffer *const buf_;
+ bool error_;
+};
+
+} // namespace http
+} // namespace aos
+
+#endif // AOS_LINUX_CODE_OUTPUT_EVHTTP_CTEMPLATE_EMITTER_H_
diff --git a/aos/linux_code/output/output.gyp b/aos/linux_code/output/output.gyp
new file mode 100644
index 0000000..f697630
--- /dev/null
+++ b/aos/linux_code/output/output.gyp
@@ -0,0 +1,24 @@
+{
+ 'targets': [
+ {
+ 'target_name': 'http_server',
+ 'type': 'static_library',
+ 'sources': [
+ 'HTTPServer.cpp',
+ 'evhttp_ctemplate_emitter.cc',
+ 'ctemplate_cache.cc',
+ ],
+ 'dependencies': [
+ '<(EXTERNALS):libevent',
+ '<(EXTERNALS):ctemplate',
+ '<(AOS)/common/common.gyp:once',
+ '<(AOS)/common/common.gyp:scoped_fd',
+ '<(AOS)/build/aos.gyp:logging',
+ ],
+ 'export_dependent_settings': [
+ '<(EXTERNALS):libevent',
+ '<(EXTERNALS):ctemplate',
+ ],
+ },
+ ],
+}
diff --git a/aos/linux_code/queue-tmpl.h b/aos/linux_code/queue-tmpl.h
new file mode 100644
index 0000000..25eb8d9
--- /dev/null
+++ b/aos/linux_code/queue-tmpl.h
@@ -0,0 +1,116 @@
+namespace aos {
+
+template <class T>
+bool ScopedMessagePtr<T>::Send() {
+ assert(msg_ != NULL);
+ msg_->SetTimeToNow();
+ assert(queue_ != NULL);
+ bool return_value = queue_->WriteMessage(msg_, RawQueue::kOverride);
+ msg_ = NULL;
+ return return_value;
+}
+
+template <class T>
+bool ScopedMessagePtr<T>::SendBlocking() {
+ assert(msg_ != NULL);
+ msg_->SetTimeToNow();
+ assert(queue_ != NULL);
+ bool return_value = queue_->WriteMessage(msg_, RawQueue::kBlock);
+ msg_ = NULL;
+ return return_value;
+}
+
+template <class T>
+void ScopedMessagePtr<T>::reset(T *msg) {
+ if (queue_ != NULL && msg_ != NULL) {
+ queue_->FreeMessage(msg_);
+ }
+ msg_ = msg;
+}
+
+template <class T>
+void Queue<T>::Init() {
+ if (queue_ == NULL) {
+ queue_ = RawQueue::Fetch(queue_name_, sizeof(T),
+ static_cast<int>(T::kHash),
+ T::kQueueLength);
+ queue_msg_.set_queue(queue_);
+ }
+}
+
+template <class T>
+void Queue<T>::Clear() {
+ if (queue_ != NULL) {
+ queue_msg_.reset();
+ queue_ = NULL;
+ queue_msg_.set_queue(NULL);
+ }
+ index_ = 0;
+}
+
+template <class T>
+bool Queue<T>::FetchNext() {
+ Init();
+ const T *msg = static_cast<const T *>(
+ queue_->ReadMessageIndex(RawQueue::kNonBlock, &index_));
+ // Only update the internal pointer if we got a new message.
+ if (msg != NULL) {
+ queue_msg_.reset(msg);
+ }
+ return msg != NULL;
+}
+
+template <class T>
+void Queue<T>::FetchNextBlocking() {
+ Init();
+ const T *msg = static_cast<const T *>(
+ queue_->ReadMessageIndex(RawQueue::kBlock, &index_));
+ queue_msg_.reset(msg);
+ assert (msg != NULL);
+}
+
+template <class T>
+bool Queue<T>::FetchLatest() {
+ Init();
+ static constexpr Options<RawQueue> kOptions =
+ RawQueue::kFromEnd | RawQueue::kNonBlock;
+ const T *msg =
+ static_cast<const T *>(queue_->ReadMessageIndex(kOptions, &index_));
+ // Only update the internal pointer if we got a new message.
+ if (msg != NULL && msg != queue_msg_.get()) {
+ queue_msg_.reset(msg);
+ return true;
+ }
+ // The message has to get freed if we didn't use it (and RawQueue::FreeMessage
+ // is ok to call on NULL).
+ queue_->FreeMessage(msg);
+ return false;
+}
+
+template <class T>
+void Queue<T>::FetchAnother() {
+ if (!FetchLatest()) FetchNextBlocking();
+}
+
+template <class T>
+ScopedMessagePtr<T> Queue<T>::MakeMessage() {
+ Init();
+ return ScopedMessagePtr<T>(queue_, MakeRawMessage());
+}
+
+template <class T>
+T *Queue<T>::MakeRawMessage() {
+ T *ret = static_cast<T *>(queue_->GetMessage());
+ assert(ret != NULL);
+ return ret;
+}
+
+template <class T>
+aos::MessageBuilder<T> Queue<T>::MakeWithBuilder() {
+ Init();
+ T *const ret = MakeRawMessage();
+ ret->Zero();
+ return aos::MessageBuilder<T>(queue_, ret);
+}
+
+} // namespace aos
diff --git a/aos/linux_code/starter/starter.cc b/aos/linux_code/starter/starter.cc
new file mode 100644
index 0000000..616bbdb
--- /dev/null
+++ b/aos/linux_code/starter/starter.cc
@@ -0,0 +1,819 @@
+// This has to come before anybody drags in <stdlib.h> or else we end up with
+// the wrong version of WIFEXITED etc (for one thing, they don't const-qualify
+// their casts) (sometimes at least).
+#include <sys/wait.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <sys/inotify.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <signal.h>
+#include <stdint.h>
+#include <errno.h>
+#include <string.h>
+#include <inttypes.h>
+
+#include <map>
+#include <functional>
+#include <deque>
+#include <fstream>
+#include <queue>
+#include <list>
+#include <string>
+#include <vector>
+#include <memory>
+#include <set>
+
+#include <event2/event.h>
+
+#include "aos/common/logging/logging.h"
+#include "aos/common/logging/logging_impl.h"
+#include "aos/linux_code/init.h"
+#include "aos/common/unique_malloc_ptr.h"
+#include "aos/common/time.h"
+#include "aos/common/once.h"
+#include "aos/common/libc/aos_strsignal.h"
+#include "aos/common/util/run_command.h"
+
+// This is the main piece of code that starts all of the rest of the code and
+// restarts it when the binaries are modified.
+//
+// Throughout, the code is not terribly concerned with thread safety because
+// there is only 1 thread. It does some setup and then lets inotify run things
+// when appropriate.
+//
+// NOTE: This program should never exit nicely. It catches all nice attempts to
+// exit, forwards them to all of the children that it has started, waits for
+// them to exit nicely, and then SIGKILLs anybody left (which will always
+// include itself).
+
+using ::std::unique_ptr;
+
+namespace aos {
+namespace starter {
+
+// TODO(brians): split out the c++ libevent wrapper stuff into its own file(s)
+class EventBaseDeleter {
+ public:
+ void operator()(event_base *base) {
+ if (base == NULL) return;
+ event_base_free(base);
+ }
+};
+typedef unique_ptr<event_base, EventBaseDeleter> EventBaseUniquePtr;
+EventBaseUniquePtr libevent_base;
+
+class EventDeleter {
+ public:
+ void operator()(event *evt) {
+ if (evt == NULL) return;
+ if (event_del(evt) != 0) {
+ LOG(WARNING, "event_del(%p) failed\n", evt);
+ }
+ }
+};
+typedef unique_ptr<event, EventDeleter> EventUniquePtr;
+
+// Watches a file path for modifications. Once created, keeps watching until
+// destroyed or RemoveWatch() is called.
+// TODO(brians): split this out into its own file + tests
+class FileWatch {
+ public:
+ // Will call callback(value) when filename is modified.
+ // If value is NULL, then a pointer to this object will be passed instead.
+ //
+ // Watching for file creations is slightly different. To do that, pass true
+ // as create, the directory where the file will be created for filename, and
+ // the name of the file (without directory name) for check_filename.
+ FileWatch(std::string filename,
+ std::function<void(void *)> callback,
+ void *value,
+ bool create = false,
+ std::string check_filename = "")
+ : filename_(filename),
+ callback_(callback),
+ value_(value),
+ create_(create),
+ check_filename_(check_filename),
+ watch_(-1) {
+ init_once.Get();
+
+ CreateWatch();
+ }
+ // Cleans up everything.
+ ~FileWatch() {
+ if (watch_ != -1) {
+ RemoveWatch();
+ }
+ }
+
+ // After calling this method, this object won't really be doing much of
+ // anything besides possibly running its callback or something.
+ void RemoveWatch() {
+ CHECK_NE(watch_, -1);
+ CHECK_EQ(watch_to_remove_, -1);
+
+ if (inotify_rm_watch(notify_fd, watch_) == -1) {
+ PLOG(WARNING, "inotify_rm_watch(%d, %d) failed", notify_fd, watch_);
+ }
+ watch_to_remove_ = watch_;
+ watch_ = -1;
+ }
+
+ private:
+ // Performs the static initialization. Called by init_once from the
+ // constructor.
+ static void *Init() {
+ notify_fd = inotify_init1(IN_CLOEXEC);
+ EventUniquePtr notify_event(event_new(libevent_base.get(), notify_fd,
+ EV_READ | EV_PERSIST,
+ FileWatch::INotifyReadable, NULL));
+ event_add(notify_event.release(), NULL);
+ return NULL;
+ }
+
+ void RemoveWatchFromMap() {
+ int watch = watch_to_remove_;
+ if (watch == -1) {
+ CHECK_NE(watch_, -1);
+ watch = watch_;
+ }
+ if (watchers[watch] != this) {
+ LOG(WARNING, "watcher for %s (%p) didn't find itself in the map\n",
+ filename_.c_str(), this);
+ } else {
+ watchers.erase(watch);
+ }
+ LOG(DEBUG, "removed watch ID %d\n", watch);
+ if (watch_to_remove_ == -1) {
+ watch_ = -1;
+ } else {
+ watch_to_remove_ = -1;
+ }
+ }
+
+ void CreateWatch() {
+ CHECK_EQ(watch_, -1);
+ watch_ = inotify_add_watch(notify_fd, filename_.c_str(),
+ create_ ? IN_CREATE : (IN_ATTRIB |
+ IN_MODIFY |
+ IN_DELETE_SELF |
+ IN_MOVE_SELF));
+ if (watch_ == -1) {
+ PLOG(FATAL, "inotify_add_watch(%d, %s,"
+ " %s ? IN_CREATE : (IN_ATTRIB | IN_MODIFY)) failed",
+ notify_fd, filename_.c_str(), create_ ? "true" : "false");
+ }
+ watchers[watch_] = this;
+ LOG(DEBUG, "watch for %s is %d\n", filename_.c_str(), watch_);
+ }
+
+ // This gets set up as the callback for EV_READ on the inotify file
+ // descriptor. It calls FileNotified on the appropriate instance.
+ static void INotifyReadable(int /*fd*/, short /*events*/, void *) {
+ unsigned int to_read;
+ // Use FIONREAD to figure out how many bytes there are to read.
+ if (ioctl(notify_fd, FIONREAD, &to_read) < 0) {
+ PLOG(FATAL, "FIONREAD(%d, %p) failed", notify_fd, &to_read);
+ }
+ inotify_event *notifyevt = static_cast<inotify_event *>(malloc(to_read));
+ const char *end = reinterpret_cast<char *>(notifyevt) + to_read;
+ aos::unique_c_ptr<inotify_event> freer(notifyevt);
+
+ ssize_t ret = read(notify_fd, notifyevt, to_read);
+ if (ret < 0) {
+ PLOG(FATAL, "read(%d, %p, %u) failed", notify_fd, notifyevt, to_read);
+ }
+ if (static_cast<size_t>(ret) != to_read) {
+ LOG(ERROR, "read(%d, %p, %u) returned %zd instead of %u\n",
+ notify_fd, notifyevt, to_read, ret, to_read);
+ return;
+ }
+
+ // Keep looping through until we get to the end because inotify does return
+ // multiple events at once.
+ while (reinterpret_cast<char *>(notifyevt) < end) {
+ if (watchers.count(notifyevt->wd) != 1) {
+ LOG(WARNING, "couldn't find whose watch ID %d is\n", notifyevt->wd);
+ } else {
+ LOG(DEBUG, "mask=%" PRIu32 "\n", notifyevt->mask);
+ // If the watch was removed.
+ if (notifyevt->mask & IN_IGNORED) {
+ watchers[notifyevt->wd]->WatchDeleted();
+ } else {
+ watchers[notifyevt->wd]
+ ->FileNotified((notifyevt->len > 0) ? notifyevt->name : NULL);
+ }
+ }
+
+ notifyevt = reinterpret_cast<inotify_event *>(
+ __builtin_assume_aligned(reinterpret_cast<char *>(notifyevt) +
+ sizeof(*notifyevt) + notifyevt->len,
+ alignof(inotify_event)));
+ }
+ }
+
+ // INotifyReadable calls this method whenever the watch for our file gets
+ // removed somehow.
+ void WatchDeleted() {
+ LOG(DEBUG, "watch for %s deleted\n", filename_.c_str());
+ RemoveWatchFromMap();
+ CreateWatch();
+ }
+
+ // INotifyReadable calls this method whenever the watch for our file triggers.
+ void FileNotified(const char *filename) {
+ CHECK_NE(watch_, -1);
+ LOG(DEBUG, "got a notification for %s\n", filename_.c_str());
+
+ if (!check_filename_.empty()) {
+ if (filename == NULL) {
+ return;
+ }
+ if (std::string(filename) != check_filename_) {
+ return;
+ }
+ }
+
+ callback_((value_ == NULL) ? this : value_);
+ }
+
+ // To make sure that Init gets called exactly once.
+ static ::aos::Once<void> init_once;
+
+ const std::string filename_;
+ const std::function<void(void *)> callback_;
+ void *const value_;
+ const bool create_;
+ std::string check_filename_;
+
+ // The watch descriptor or -1 if we don't have one any more.
+ int watch_;
+ // The watch that we still have to take out of the map once we get the
+ // IN_IGNORED or -1.
+ int watch_to_remove_ = -1;
+
+ // Map from watch IDs to instances of this class.
+ // <https://patchwork.kernel.org/patch/73192/> ("inotify: do not reuse watch
+ // descriptors") says they won't get reused, but that shouldn't be counted on
+ // because we might have a modified/different version/whatever kernel.
+ static std::map<int, FileWatch *> watchers;
+ // The inotify(7) file descriptor.
+ static int notify_fd;
+
+ DISALLOW_COPY_AND_ASSIGN(FileWatch);
+};
+::aos::Once<void> FileWatch::init_once(FileWatch::Init);
+std::map<int, FileWatch *> FileWatch::watchers;
+int FileWatch::notify_fd;
+
+// Runs the given command and returns its first line of output (not including
+// the \n). LOG(FATAL)s if the command has an exit status other than 0 or does
+// not print out an entire line.
+std::string RunCommand(std::string command) {
+ // popen(3) might fail and not set it.
+ errno = 0;
+ FILE *pipe = popen(command.c_str(), "r");
+ if (pipe == NULL) {
+ PLOG(FATAL, "popen(\"%s\", \"r\") failed", command.c_str());
+ }
+
+ // result_size is how many bytes result is currently allocated to.
+ size_t result_size = 128, read = 0;
+ unique_c_ptr<char> result(static_cast<char *>(malloc(result_size)));
+ while (true) {
+ // If we filled up the buffer, then realloc(3) it bigger.
+ if (read == result_size) {
+ result_size *= 2;
+ void *new_result = realloc(result.get(), result_size);
+ if (new_result == NULL) {
+ PLOG(FATAL, "realloc(%p, %zd) failed", result.get(), result_size);
+ } else {
+ result.release();
+ result = unique_c_ptr<char>(static_cast<char *>(new_result));
+ }
+ }
+
+ size_t ret = fread(result.get() + read, 1, result_size - read, pipe);
+ // If the read didn't fill up the whole buffer, check to see if it was
+ // because of an error.
+ if (ret < result_size - read) {
+ if (ferror(pipe)) {
+ PLOG(FATAL, "couldn't finish reading output of \"%s\"\n",
+ command.c_str());
+ }
+ }
+ read += ret;
+ if (read > 0 && result.get()[read - 1] == '\n') {
+ break;
+ }
+
+ if (feof(pipe)) {
+ LOG(FATAL, "`%s` failed. didn't print a whole line\n", command.c_str());
+ }
+ }
+
+ // Get rid of the first \n and anything after it.
+ *strchrnul(result.get(), '\n') = '\0';
+
+ int child_status = pclose(pipe);
+ if (child_status == -1) {
+ PLOG(FATAL, "pclose(%p) failed", pipe);
+ }
+
+ if (child_status != 0) {
+ LOG(FATAL, "`%s` failed. return %d\n", command.c_str(), child_status);
+ }
+
+ return std::string(result.get());
+}
+
+// Will call callback(arg) after time.
+void Timeout(time::Time time, void (*callback)(int, short, void *), void *arg) {
+ EventUniquePtr timeout(evtimer_new(libevent_base.get(), callback, arg));
+ struct timeval time_timeval = time.ToTimeval();
+ if (evtimer_add(timeout.release(), &time_timeval) != 0) {
+ LOG(FATAL, "evtimer_add(%p, %p) failed\n",
+ timeout.release(), &time_timeval);
+ }
+}
+
+class Child;
+// This is where all of the Child instances except core live.
+std::vector<unique_ptr<Child>> children;
+// A global place to hold on to which child is core.
+unique_ptr<Child> core;
+
+// Represents a child process. It will take care of restarting itself etc.
+class Child {
+ public:
+ // command is the (space-separated) command to run and its arguments.
+ Child(const std::string &command) : pid_(-1),
+ stat_at_start_valid_(false) {
+ if (!restart_timeout) {
+ restart_timeout = EventUniquePtr(
+ evtimer_new(libevent_base.get(), StaticDoRestart, nullptr));
+ }
+ const char *start, *end;
+ start = command.c_str();
+ while (true) {
+ end = strchrnul(start, ' ');
+ args_.push_back(std::string(start, end - start));
+ start = end + 1;
+ if (*end == '\0') {
+ break;
+ }
+ }
+
+ original_binary_ = RunCommand("which " + args_[0]);
+ binary_ = original_binary_ + ".stm";
+
+ watcher_ = unique_ptr<FileWatch>(
+ new FileWatch(original_binary_, StaticFileModified, this));
+
+ Start();
+ }
+
+ pid_t pid() { return pid_; }
+
+ // This gets called whenever the actual process dies and should (probably) be
+ // restarted.
+ void ProcessDied() {
+ pid_ = -1;
+ restarts_.push(time::Time::Now());
+ if (restarts_.size() > kMaxRestartsNumber) {
+ time::Time oldest = restarts_.front();
+ restarts_.pop();
+ if ((time::Time::Now() - oldest) <= kMaxRestartsTime) {
+ LOG(WARNING, "process %s getting restarted too often\n", name());
+ Timeout(kResumeWait, StaticStart, this);
+ return;
+ }
+ }
+ Start();
+ }
+
+ // Returns a name for logging purposes.
+ const char *name() {
+ return args_[0].c_str();
+ }
+
+ private:
+ struct CheckDiedStatus {
+ Child *self;
+ pid_t old_pid;
+ };
+
+ // How long to wait for a child to die nicely.
+ static constexpr time::Time kProcessDieTime = time::Time::InSeconds(2);
+
+ // How long to wait after the file is modified to restart it.
+ // This is important because some programs like modifying the binaries by
+ // writing them in little bits, which results in attempting to start partial
+ // binaries without this.
+ static constexpr time::Time kRestartWaitTime = time::Time::InSeconds(1.5);
+
+ // Only kMaxRestartsNumber restarts will be allowed in kMaxRestartsTime.
+ static constexpr time::Time kMaxRestartsTime = time::Time::InSeconds(4);
+ static const size_t kMaxRestartsNumber = 3;
+ // How long to wait if it gets restarted too many times.
+ static constexpr time::Time kResumeWait = time::Time::InSeconds(5);
+
+ static void StaticFileModified(void *self) {
+ static_cast<Child *>(self)->FileModified();
+ }
+
+ void FileModified() {
+ LOG(DEBUG, "file for %s modified\n", name());
+ struct timeval restart_time_timeval = kRestartWaitTime.ToTimeval();
+ // This will reset the timeout again if it hasn't run yet.
+ if (evtimer_add(restart_timeout.get(), &restart_time_timeval) != 0) {
+ LOG(FATAL, "evtimer_add(%p, %p) failed\n",
+ restart_timeout.get(), &restart_time_timeval);
+ }
+ waiting_to_restart.insert(this);
+ }
+
+ static void StaticDoRestart(int, short, void *) {
+ LOG(DEBUG, "restarting everything that needs it\n");
+ if (waiting_to_restart.find(core.get()) != waiting_to_restart.end()) {
+ core->DoRestart();
+ waiting_to_restart.erase(core.get());
+ }
+ for (auto c : waiting_to_restart) {
+ c->DoRestart();
+ }
+ waiting_to_restart.clear();
+ }
+
+ // Called after somebody else has finished modifying the file.
+ void DoRestart() {
+ fprintf(stderr, "DoRestart(%s)\n", binary_.c_str());
+ if (stat_at_start_valid_) {
+ struct stat current_stat;
+ if (stat(original_binary_.c_str(), ¤t_stat) == -1) {
+ PLOG(FATAL, "stat(%s, %p) failed",
+ original_binary_.c_str(), ¤t_stat);
+ }
+ if (current_stat.st_mtime == stat_at_start_.st_mtime) {
+ LOG(DEBUG, "ignoring trigger for %s because mtime didn't change\n",
+ name());
+ return;
+ }
+ }
+
+ if (this == core.get()) {
+ fprintf(stderr, "Restarting core -> exiting now.\n");
+ exit(0);
+ }
+ if (pid_ != -1) {
+ LOG(DEBUG, "sending SIGTERM to child %d to restart it\n", pid_);
+ if (kill(pid_, SIGTERM) == -1) {
+ PLOG(WARNING, "kill(%d, SIGTERM) failed", pid_);
+ }
+ CheckDiedStatus *status = new CheckDiedStatus();
+ status->self = this;
+ status->old_pid = pid_;
+ Timeout(kProcessDieTime, StaticCheckDied, status);
+ } else {
+ LOG(WARNING, "%s restart attempted but not running\n", name());
+ }
+ }
+
+ static void StaticCheckDied(int, short, void *status_in) {
+ CheckDiedStatus *status = static_cast<CheckDiedStatus *>(status_in);
+ status->self->CheckDied(status->old_pid);
+ delete status;
+ }
+
+ // Checks to see if the child using the PID old_pid is still running.
+ void CheckDied(pid_t old_pid) {
+ if (pid_ == old_pid) {
+ LOG(WARNING, "child %d refused to die\n", old_pid);
+ if (kill(old_pid, SIGKILL) == -1) {
+ PLOG(WARNING, "kill(%d, SIGKILL) failed", old_pid);
+ }
+ }
+ }
+
+ static void StaticStart(int, short, void *self) {
+ static_cast<Child *>(self)->Start();
+ }
+
+ // Actually starts the child.
+ void Start() {
+ if (pid_ != -1) {
+ LOG(WARNING, "calling Start() but already have child %d running\n",
+ pid_);
+ if (kill(pid_, SIGKILL) == -1) {
+ PLOG(WARNING, "kill(%d, SIGKILL) failed", pid_);
+ return;
+ }
+ pid_ = -1;
+ }
+
+ // Remove the name that we run from (ie from a previous execution) and then
+ // hard link the real filename to it.
+ if (unlink(binary_.c_str()) != 0 && errno != ENOENT) {
+ PLOG(FATAL, "removing %s failed", binary_.c_str());
+ }
+ if (link(original_binary_.c_str(), binary_.c_str()) != 0) {
+ PLOG(FATAL, "link('%s', '%s') failed",
+ original_binary_.c_str(), binary_.c_str());
+ }
+
+ if (stat(original_binary_.c_str(), &stat_at_start_) == -1) {
+ PLOG(FATAL, "stat(%s, %p) failed",
+ original_binary_.c_str(), &stat_at_start_);
+ }
+ stat_at_start_valid_ = true;
+
+ if ((pid_ = fork()) == 0) {
+ ssize_t args_size = args_.size();
+ const char **argv = new const char *[args_size + 1];
+ for (int i = 0; i < args_size; ++i) {
+ argv[i] = args_[i].c_str();
+ }
+ argv[args_size] = NULL;
+ // The const_cast is safe because no code that might care if it gets
+ // modified can run afterwards.
+ execv(binary_.c_str(), const_cast<char **>(argv));
+ PLOG(FATAL, "execv(%s, %p) failed", binary_.c_str(), argv);
+ _exit(EXIT_FAILURE);
+ }
+ if (pid_ == -1) {
+ PLOG(FATAL, "forking to run \"%s\" failed", binary_.c_str());
+ }
+ LOG(DEBUG, "started \"%s\" successfully\n", binary_.c_str());
+ }
+
+ // A history of the times that this process has been restarted.
+ std::queue<time::Time, std::list<time::Time>> restarts_;
+
+ // The currently running child's PID or NULL.
+ pid_t pid_;
+
+ // All of the arguments (including the name of the binary).
+ std::deque<std::string> args_;
+
+ // The name of the real binary that we were told to run.
+ std::string original_binary_;
+ // The name of the file that we're actually running.
+ std::string binary_;
+
+ // Watches original_binary_.
+ unique_ptr<FileWatch> watcher_;
+
+ // Captured from the original file when we most recently started a new child
+ // process. Used to see if it actually changes or not.
+ struct stat stat_at_start_;
+ bool stat_at_start_valid_;
+
+ // An event that restarts after kRestartWaitTime.
+ static EventUniquePtr restart_timeout;
+
+ // The set of children waiting to be restarted once all modifications stop.
+ static ::std::set<Child *> waiting_to_restart;
+
+ DISALLOW_COPY_AND_ASSIGN(Child);
+};
+
+constexpr time::Time Child::kProcessDieTime;
+constexpr time::Time Child::kRestartWaitTime;
+constexpr time::Time Child::kMaxRestartsTime;
+constexpr time::Time Child::kResumeWait;
+
+EventUniquePtr Child::restart_timeout;
+::std::set<Child *> Child::waiting_to_restart;
+
+// Kills off the entire process group (including ourself).
+void KillChildren(bool try_nice) {
+ if (try_nice) {
+ static const int kNiceStopSignal = SIGTERM;
+ static const time::Time kNiceWaitTime = time::Time::InSeconds(1);
+
+ // Make sure that we don't just nicely stop ourself...
+ sigset_t mask;
+ sigemptyset(&mask);
+ sigaddset(&mask, kNiceStopSignal);
+ sigprocmask(SIG_BLOCK, &mask, NULL);
+
+ kill(-getpid(), kNiceStopSignal);
+
+ fflush(NULL);
+ time::SleepFor(kNiceWaitTime);
+ }
+
+ // Send SIGKILL to our whole process group, which will forcibly terminate any
+ // of them that are still running (us for sure, maybe more too).
+ kill(-getpid(), SIGKILL);
+}
+
+void ExitHandler() {
+ KillChildren(true);
+}
+
+void KillChildrenSignalHandler(int signum) {
+ // If we get SIGSEGV or some other random signal who knows what's happening
+ // and we should just kill everybody immediately.
+ // This is a list of all of the signals that mean some form of "nicely stop".
+ KillChildren(signum == SIGHUP || signum == SIGINT || signum == SIGQUIT ||
+ signum == SIGABRT || signum == SIGPIPE || signum == SIGTERM ||
+ signum == SIGXCPU);
+}
+
+// Returns the currently running child with PID pid or an empty unique_ptr.
+const unique_ptr<Child> &FindChild(pid_t pid) {
+ for (auto it = children.begin(); it != children.end(); ++it) {
+ if (pid == (*it)->pid()) {
+ return *it;
+ }
+ }
+
+ if (pid == core->pid()) {
+ return core;
+ }
+
+ static const unique_ptr<Child> kNothing;
+ return kNothing;
+}
+
+// Gets set up as a libevent handler for SIGCHLD.
+// Handles calling Child::ProcessDied() on the appropriate one.
+void SigCHLDReceived(int /*fd*/, short /*events*/, void *) {
+ // In a while loop in case we miss any SIGCHLDs.
+ while (true) {
+ siginfo_t infop;
+ infop.si_pid = 0;
+ if (waitid(P_ALL, 0, &infop, WEXITED | WSTOPPED | WNOHANG) != 0) {
+ PLOG(WARNING, "waitid failed");
+ continue;
+ }
+ // If there are no more child process deaths to process.
+ if (infop.si_pid == 0) {
+ return;
+ }
+
+ pid_t pid = infop.si_pid;
+ int status = infop.si_status;
+ const unique_ptr<Child> &child = FindChild(pid);
+ if (child) {
+ switch (infop.si_code) {
+ case CLD_EXITED:
+ LOG(WARNING, "child %d (%s) exited with status %d\n",
+ pid, child->name(), status);
+ break;
+ case CLD_DUMPED:
+ LOG(INFO, "child %d actually dumped core. "
+ "falling through to killed by signal case\n", pid);
+ case CLD_KILLED:
+ // If somebody (possibly us) sent it SIGTERM that means that they just
+ // want it to stop, so it stopping isn't a WARNING.
+ LOG((status == SIGTERM) ? DEBUG : WARNING,
+ "child %d (%s) was killed by signal %d (%s)\n",
+ pid, child->name(), status, aos_strsignal(status));
+ break;
+ case CLD_STOPPED:
+ LOG(WARNING, "child %d (%s) was stopped by signal %d "
+ "(giving it a SIGCONT(%d))\n",
+ pid, child->name(), status, SIGCONT);
+ kill(pid, SIGCONT);
+ continue;
+ default:
+ LOG(WARNING, "something happened to child %d (%s) (killing it)\n",
+ pid, child->name());
+ kill(pid, SIGKILL);
+ continue;
+ }
+ } else {
+ LOG(WARNING, "couldn't find a Child for pid %d\n", pid);
+ return;
+ }
+
+ if (child == core) {
+ LOG(FATAL, "core died\n");
+ }
+ child->ProcessDied();
+ }
+}
+
+// This is used for communicating the name of the file to read processes to
+// start from main to Run.
+const char *child_list_file;
+
+void Run(void *watch);
+void Main() {
+ logging::Init();
+
+ if (setpgid(0 /*self*/, 0 /*make PGID the same as PID*/) != 0) {
+ PLOG(FATAL, "setpgid(0, 0) failed");
+ }
+
+ // Make sure that we kill all children when we exit.
+ atexit(ExitHandler);
+ // Do it on some signals too (ones that we otherwise tend to receive and then
+ // leave all of our children going).
+ signal(SIGHUP, KillChildrenSignalHandler);
+ signal(SIGINT, KillChildrenSignalHandler);
+ signal(SIGQUIT, KillChildrenSignalHandler);
+ signal(SIGILL, KillChildrenSignalHandler);
+ signal(SIGABRT, KillChildrenSignalHandler);
+ signal(SIGFPE, KillChildrenSignalHandler);
+ signal(SIGSEGV, KillChildrenSignalHandler);
+ signal(SIGPIPE, KillChildrenSignalHandler);
+ signal(SIGTERM, KillChildrenSignalHandler);
+ signal(SIGBUS, KillChildrenSignalHandler);
+ signal(SIGXCPU, KillChildrenSignalHandler);
+
+ libevent_base = EventBaseUniquePtr(event_base_new());
+
+ std::string core_touch_file = "/tmp/starter.";
+ core_touch_file += std::to_string(static_cast<intmax_t>(getpid()));
+ core_touch_file += ".core_touch_file";
+ const int result =
+ ::aos::util::RunCommand(("touch '" + core_touch_file + "'").c_str());
+ if (result == -1) {
+ PLOG(FATAL, "running `touch '%s'` failed\n", core_touch_file.c_str());
+ } else if (!WIFEXITED(result) || WEXITSTATUS(result) != 0) {
+ LOG(FATAL, "`touch '%s'` gave result %x\n", core_touch_file.c_str(),
+ result);
+ }
+ FileWatch core_touch_file_watch(core_touch_file, Run, NULL);
+ core = unique_ptr<Child>(
+ new Child("core " + core_touch_file));
+
+ FILE *pid_file = fopen("/tmp/starter.pid", "w");
+ if (pid_file == NULL) {
+ PLOG(FATAL, "fopen(\"/tmp/starter.pid\", \"w\") failed");
+ } else {
+ if (fprintf(pid_file, "%d", core->pid()) == -1) {
+ PLOG(WARNING, "fprintf(%p, \"%%d\", %d) failed",
+ pid_file, core->pid());
+ }
+ fclose(pid_file);
+ }
+
+ LOG(INFO, "waiting for %s to appear\n", core_touch_file.c_str());
+
+ event_base_dispatch(libevent_base.get());
+ LOG(FATAL, "event_base_dispatch(%p) returned\n", libevent_base.get());
+}
+
+// This is the callback for when core creates the file indicating that it has
+// started.
+void Run(void *watch) {
+ // Make it so it doesn't keep on seeing random changes in /tmp.
+ static_cast<FileWatch *>(watch)->RemoveWatch();
+
+ // It's safe now because core is up.
+ aos::InitNRT();
+
+ std::ifstream list_file(child_list_file);
+
+ while (true) {
+ std::string child_name;
+ getline(list_file, child_name);
+ if ((list_file.rdstate() & std::ios_base::eofbit) != 0) {
+ break;
+ }
+ if (list_file.rdstate() != 0) {
+ LOG(FATAL, "reading input file %s failed\n", child_list_file);
+ }
+ children.push_back(unique_ptr<Child>(new Child(child_name)));
+ }
+
+ EventUniquePtr sigchld(event_new(libevent_base.get(), SIGCHLD,
+ EV_SIGNAL | EV_PERSIST,
+ SigCHLDReceived, NULL));
+ event_add(sigchld.release(), NULL);
+}
+
+const char *kArgsHelp = "[OPTION]... START_LIST\n"
+ "Start all of the robot code binaries in START_LIST.\n"
+ "\n"
+ "START_LIST is the file to read binaries (looked up on PATH) to run.\n"
+ " --help display this help and exit\n";
+void PrintHelp() {
+ fprintf(stderr, "Usage: %s %s", program_invocation_name, kArgsHelp);
+}
+
+} // namespace starter
+} // namespace aos
+
+int main(int argc, char *argv[]) {
+ if (argc != 2) {
+ aos::starter::PrintHelp();
+ exit(EXIT_FAILURE);
+ }
+ if (strcmp(argv[1], "--help") == 0) {
+ aos::starter::PrintHelp();
+ exit(EXIT_SUCCESS);
+ }
+
+ aos::starter::child_list_file = argv[1];
+
+ aos::starter::Main();
+}
diff --git a/aos/linux_code/starter/starter.gyp b/aos/linux_code/starter/starter.gyp
new file mode 100644
index 0000000..9a16647
--- /dev/null
+++ b/aos/linux_code/starter/starter.gyp
@@ -0,0 +1,28 @@
+{
+ 'targets': [
+ {
+ 'target_name': 'starter_exe',
+ 'type': 'executable',
+ 'sources': [
+ 'starter.cc',
+ ],
+ 'dependencies': [
+ '<(AOS)/linux_code/linux_code.gyp:init',
+ '<(EXTERNALS):libevent',
+ '<(AOS)/build/aos.gyp:logging',
+ '<(AOS)/common/common.gyp:once',
+ '<(AOS)/common/common.gyp:time',
+ '<(AOS)/common/libc/libc.gyp:aos_strsignal',
+ '<(AOS)/common/util/util.gyp:run_command',
+ ],
+ 'copies': [
+ {
+ 'destination': '<(rsync_dir)',
+ 'files': [
+ 'starter.sh',
+ ],
+ },
+ ],
+ },
+ ],
+}
diff --git a/aos/linux_code/starter/starter.sh b/aos/linux_code/starter/starter.sh
new file mode 100755
index 0000000..b81bf62
--- /dev/null
+++ b/aos/linux_code/starter/starter.sh
@@ -0,0 +1,9 @@
+#!/bin/bash
+
+# NI already has a core pattern, so we probably shouldn't change it.
+#echo '/home/driver/tmp/robot_logs/%e-%s-%p-%t.coredump' > /proc/sys/kernel/core_pattern
+
+while true; do
+ export PATH=$PATH:/home/admin/robot_code
+ starter_exe /home/admin/robot_code/start_list.txt
+done
diff --git a/aos/linux_code/starter/testing_list.txt b/aos/linux_code/starter/testing_list.txt
new file mode 100644
index 0000000..96d412d
--- /dev/null
+++ b/aos/linux_code/starter/testing_list.txt
@@ -0,0 +1,3 @@
+../bin/LogReader
+../bin/JoystickCode
+../bin/AutoMode
diff --git a/aos/prime/README.txt b/aos/prime/README.txt
new file mode 100644
index 0000000..07c414b
--- /dev/null
+++ b/aos/prime/README.txt
@@ -0,0 +1,10 @@
+see ../README.txt for stuff affecting crio and linux code
+
+This folder has code for the linux box that talks to the cRIO etc. It is called the prime. We have multiple explanations for that name:
+ It is the primary controller.
+ PRIME/Primary Robot Intelligent Management Entity
+ Represents Optimus Prime, one of the good transformers who battle the bad ones that 254 names robots after.
+ It is easy to type and doesn't conflict with anything else common for tab-completion.
+ It's not hardware-specific.
+
+The code that would be useful on any linux box is in ../linux_code/.
diff --git a/aos/prime/input/input.gyp b/aos/prime/input/input.gyp
new file mode 100644
index 0000000..25ba45d
--- /dev/null
+++ b/aos/prime/input/input.gyp
@@ -0,0 +1,21 @@
+{
+ 'targets': [
+ {
+ 'target_name': 'joystick_input',
+ 'type': 'static_library',
+ 'sources': [
+ 'joystick_input.cc',
+ ],
+ 'dependencies': [
+ '<(AOS)/common/input/input.gyp:driver_station_data',
+ '<(AOS)/common/messages/messages.gyp:robot_state',
+ '<(AOS)/common/network/network.gyp:socket',
+ '<(AOS)/build/aos.gyp:logging',
+ '<(AOS)/common/logging/logging.gyp:queue_logging',
+ ],
+ 'export_dependent_settings': [
+ '<(AOS)/common/input/input.gyp:driver_station_data',
+ ],
+ },
+ ],
+}
diff --git a/aos/prime/input/joystick_input.cc b/aos/prime/input/joystick_input.cc
new file mode 100644
index 0000000..cf9e7c0
--- /dev/null
+++ b/aos/prime/input/joystick_input.cc
@@ -0,0 +1,66 @@
+#include "aos/prime/input/joystick_input.h"
+
+#include <string.h>
+
+#include "aos/common/messages/robot_state.q.h"
+#include "aos/common/logging/logging.h"
+#include "aos/common/logging/queue_logging.h"
+
+namespace aos {
+namespace input {
+
+void JoystickInput::Run() {
+ driver_station::Data data;
+ while (true) {
+ joystick_state.FetchAnother();
+
+ data.Update(*joystick_state);
+
+ {
+ using driver_station::JoystickFeature;
+ using driver_station::ButtonLocation;
+ for (int joystick = 1; joystick <= JoystickFeature::kJoysticks;
+ ++joystick) {
+ for (int button = 1; button <= ButtonLocation::kButtons; ++button) {
+ ButtonLocation location(joystick, button);
+ if (data.PosEdge(location)) {
+ LOG(INFO, "PosEdge(%d, %d)\n", joystick, button);
+ }
+ if (data.NegEdge(location)) {
+ LOG(INFO, "NegEdge(%d, %d)\n", joystick, button);
+ }
+ }
+ if (data.GetPOV(joystick) != data.GetOldPOV(joystick)) {
+ LOG(INFO, "POV %d %d->%d\n", joystick, data.GetOldPOV(joystick),
+ data.GetPOV(joystick));
+ }
+ }
+ }
+ {
+ using driver_station::ControlBit;
+ if (data.PosEdge(ControlBit::kFmsAttached)) {
+ LOG(INFO, "PosEdge(kFmsAttached)\n");
+ }
+ if (data.NegEdge(ControlBit::kFmsAttached)) {
+ LOG(INFO, "NegEdge(kFmsAttached)\n");
+ }
+ if (data.PosEdge(ControlBit::kAutonomous)) {
+ LOG(INFO, "PosEdge(kAutonomous)\n");
+ }
+ if (data.NegEdge(ControlBit::kAutonomous)) {
+ LOG(INFO, "NegEdge(kAutonomous)\n");
+ }
+ if (data.PosEdge(ControlBit::kEnabled)) {
+ LOG(INFO, "PosEdge(kEnabled)\n");
+ }
+ if (data.NegEdge(ControlBit::kEnabled)) {
+ LOG(INFO, "NegEdge(kEnabled)\n");
+ }
+ }
+
+ RunIteration(data);
+ }
+}
+
+} // namespace input
+} // namespace aos
diff --git a/aos/prime/input/joystick_input.h b/aos/prime/input/joystick_input.h
new file mode 100644
index 0000000..ec97580
--- /dev/null
+++ b/aos/prime/input/joystick_input.h
@@ -0,0 +1,32 @@
+#ifndef AOS_LINUX_CODE_INPUT_JOYSTICKS_INPUT_H_
+#define AOS_LINUX_CODE_INPUT_JOYSTICKS_INPUT_H_
+
+#include "aos/common/input/driver_station_data.h"
+
+namespace aos {
+namespace input {
+
+// A class for handling joystick packet values.
+// It will call RunIteration each time a new packet is received.
+//
+// This class automatically handles updating ::aos::joystick_state and logging
+// (at INFO) button edges.
+class JoystickInput {
+ public:
+ void Run();
+
+ private:
+ // Subclasses should do whatever they want with data here.
+ virtual void RunIteration(const driver_station::Data &data) = 0;
+};
+
+// Class which will proxy joystick information from UDP packets to the queues.
+class JoystickProxy {
+ public:
+ void Run();
+};
+
+} // namespace input
+} // namespace aos
+
+#endif // AOS_LINUX_CODE_INPUT_JOYSTICKS_INPUT_H_
diff --git a/bot3/actors/actors.gyp b/bot3/actors/actors.gyp
new file mode 100644
index 0000000..794939a
--- /dev/null
+++ b/bot3/actors/actors.gyp
@@ -0,0 +1,10 @@
+{
+ 'targets': [
+ {
+ 'target_name': 'binaries',
+ 'type': 'none',
+ 'dependencies': [
+ ],
+ },
+ ],
+}
diff --git a/bot3/autonomous/auto.cc b/bot3/autonomous/auto.cc
new file mode 100644
index 0000000..0a2f99a
--- /dev/null
+++ b/bot3/autonomous/auto.cc
@@ -0,0 +1,47 @@
+#include <stdio.h>
+
+#include <memory>
+
+#include "aos/common/util/phased_loop.h"
+#include "aos/common/time.h"
+#include "aos/common/util/trapezoid_profile.h"
+#include "aos/common/logging/logging.h"
+#include "aos/common/logging/queue_logging.h"
+
+#include "bot3/autonomous/auto.q.h"
+#include "bot3/control_loops/drivetrain/drivetrain.q.h"
+#include "frc971/actors/drivetrain_actor.h"
+
+using ::aos::time::Time;
+using ::bot3::control_loops::drivetrain_queue;
+
+namespace bot3 {
+namespace autonomous {
+
+struct ProfileParams {
+ double velocity;
+ double acceleration;
+};
+
+namespace time = ::aos::time;
+
+bool ShouldExitAuto() {
+ ::bot3::autonomous::autonomous.FetchLatest();
+ bool ans = !::bot3::autonomous::autonomous->run_auto;
+ if (ans) {
+ LOG(INFO, "Time to exit auto mode\n");
+ }
+ return ans;
+}
+
+void HandleAuto() {
+ ::aos::time::Time start_time = ::aos::time::Time::Now();
+ LOG(INFO, "Starting auto mode at %f\n", start_time.ToSeconds());
+ ::std::unique_ptr<::frc971::actors::DrivetrainAction> drive;
+ LOG(INFO, "Doing nothing in auto.\n");
+
+ if (ShouldExitAuto()) return;
+}
+
+} // namespace autonomous
+} // namespace bot3
diff --git a/bot3/autonomous/auto.h b/bot3/autonomous/auto.h
new file mode 100644
index 0000000..896d22c
--- /dev/null
+++ b/bot3/autonomous/auto.h
@@ -0,0 +1,12 @@
+#ifndef BOT3_AUTONOMOUS_AUTO_H_
+#define BOT3_AUTONOMOUS_AUTO_H_
+
+namespace bot3 {
+namespace autonomous {
+
+void HandleAuto();
+
+} // namespace autonomous
+} // namespace bot3
+
+#endif // BOT3_AUTONOMOUS_AUTO_H_
diff --git a/bot3/autonomous/auto.q b/bot3/autonomous/auto.q
new file mode 100644
index 0000000..3636d31
--- /dev/null
+++ b/bot3/autonomous/auto.q
@@ -0,0 +1,8 @@
+package bot3.autonomous;
+
+message AutoControl {
+ // True if auto mode should be running, false otherwise.
+ bool run_auto;
+};
+
+queue AutoControl autonomous;
diff --git a/bot3/autonomous/auto_main.cc b/bot3/autonomous/auto_main.cc
new file mode 100644
index 0000000..f9b978c
--- /dev/null
+++ b/bot3/autonomous/auto_main.cc
@@ -0,0 +1,42 @@
+#include <stdio.h>
+
+#include "aos/common/time.h"
+#include "aos/linux_code/init.h"
+#include "aos/common/logging/logging.h"
+#include "bot3/autonomous/auto.q.h"
+#include "bot3/autonomous/auto.h"
+
+using ::aos::time::Time;
+
+int main(int /*argc*/, char * /*argv*/[]) {
+ ::aos::Init();
+
+ LOG(INFO, "Auto main started\n");
+ ::bot3::autonomous::autonomous.FetchLatest();
+ while (!::bot3::autonomous::autonomous.get()) {
+ ::bot3::autonomous::autonomous.FetchNextBlocking();
+ LOG(INFO, "Got another auto packet\n");
+ }
+
+ while (true) {
+ while (!::bot3::autonomous::autonomous->run_auto) {
+ ::bot3::autonomous::autonomous.FetchNextBlocking();
+ LOG(INFO, "Got another auto packet\n");
+ }
+ LOG(INFO, "Starting auto mode\n");
+ ::aos::time::Time start_time = ::aos::time::Time::Now();
+ ::bot3::autonomous::HandleAuto();
+
+ ::aos::time::Time elapsed_time = ::aos::time::Time::Now() - start_time;
+ LOG(INFO, "Auto mode exited in %f, waiting for it to finish.\n",
+ elapsed_time.ToSeconds());
+ while (::bot3::autonomous::autonomous->run_auto) {
+ ::bot3::autonomous::autonomous.FetchNextBlocking();
+ LOG(INFO, "Got another auto packet\n");
+ }
+ LOG(INFO, "Waiting for auto to start back up.\n");
+ }
+ ::aos::Cleanup();
+ return 0;
+}
+
diff --git a/bot3/autonomous/autonomous.gyp b/bot3/autonomous/autonomous.gyp
new file mode 100644
index 0000000..140fb5b
--- /dev/null
+++ b/bot3/autonomous/autonomous.gyp
@@ -0,0 +1,46 @@
+{
+ 'targets': [
+ {
+ 'target_name': 'auto_queue',
+ 'type': 'static_library',
+ 'sources': ['auto.q'],
+ 'variables': {
+ 'header_path': 'bot3/autonomous',
+ },
+ 'includes': ['../../aos/build/queues.gypi'],
+ },
+ {
+ 'target_name': 'auto_lib',
+ 'type': 'static_library',
+ 'sources': [
+ 'auto.cc',
+ ],
+ 'dependencies': [
+ 'auto_queue',
+ '<(AOS)/common/controls/controls.gyp:control_loop',
+ '<(DEPTH)/bot3/control_loops/drivetrain/drivetrain.gyp:drivetrain_queue',
+ '<(AOS)/common/common.gyp:time',
+ '<(AOS)/common/util/util.gyp:phased_loop',
+ '<(AOS)/common/util/util.gyp:trapezoid_profile',
+ '<(AOS)/build/aos.gyp:logging',
+ #'<(DEPTH)/frc971/actors/actors.gyp:drivetrain_action_lib',
+ '<(AOS)/common/logging/logging.gyp:queue_logging',
+ ],
+ 'export_dependent_settings': [
+ '<(AOS)/common/controls/controls.gyp:control_loop',
+ ],
+ },
+ {
+ 'target_name': 'auto_bot3',
+ 'type': 'executable',
+ 'sources': [
+ 'auto_main.cc',
+ ],
+ 'dependencies': [
+ '<(AOS)/linux_code/linux_code.gyp:init',
+ 'auto_queue',
+ 'auto_lib',
+ ],
+ },
+ ],
+}
diff --git a/bot3/bot3.gyp b/bot3/bot3.gyp
new file mode 100644
index 0000000..6e94f53
--- /dev/null
+++ b/bot3/bot3.gyp
@@ -0,0 +1,23 @@
+{
+ 'targets': [
+ {
+ 'target_name': 'joystick_reader_bot3',
+ 'type': 'executable',
+ 'sources': [
+ 'joystick_reader.cc',
+ ],
+ 'dependencies': [
+ '<(AOS)/prime/input/input.gyp:joystick_input',
+ '<(AOS)/linux_code/linux_code.gyp:init',
+ '<(AOS)/build/aos.gyp:logging',
+ '<(AOS)/common/common.gyp:time',
+ '<(AOS)/common/util/util.gyp:log_interval',
+ '<(AOS)/common/actions/actions.gyp:action_lib',
+
+ '<(DEPTH)/frc971/queues/queues.gyp:gyro',
+ '<(DEPTH)/bot3/control_loops/drivetrain/drivetrain.gyp:drivetrain_queue',
+ '<(DEPTH)/bot3/autonomous/autonomous.gyp:auto_queue',
+ ],
+ },
+ ],
+}
diff --git a/bot3/control_loops/drivetrain/drivetrain.cc b/bot3/control_loops/drivetrain/drivetrain.cc
new file mode 100644
index 0000000..0c191d1
--- /dev/null
+++ b/bot3/control_loops/drivetrain/drivetrain.cc
@@ -0,0 +1,762 @@
+#include "bot3/control_loops/drivetrain/drivetrain.h"
+
+#include <stdio.h>
+#include <sched.h>
+#include <cmath>
+#include <memory>
+#include "Eigen/Dense"
+
+#include "aos/common/logging/logging.h"
+#include "aos/common/controls/polytope.h"
+#include "aos/common/commonmath.h"
+#include "aos/common/logging/queue_logging.h"
+#include "aos/common/logging/matrix_logging.h"
+
+#include "frc971/control_loops/state_feedback_loop.h"
+#include "frc971/control_loops/coerce_goal.h"
+#include "bot3/control_loops/drivetrain/polydrivetrain_cim_plant.h"
+#include "bot3/control_loops/drivetrain/drivetrain.q.h"
+#include "frc971/queues/gyro.q.h"
+#include "frc971/shifter_hall_effect.h"
+#include "bot3/control_loops/drivetrain/drivetrain_dog_motor_plant.h"
+#include "bot3/control_loops/drivetrain/polydrivetrain_dog_motor_plant.h"
+
+// A consistent way to mark code that goes away without shifters.
+#define HAVE_SHIFTERS 0
+
+using ::frc971::sensors::gyro_reading;
+
+namespace bot3 {
+namespace control_loops {
+
+class DrivetrainMotorsSS {
+ public:
+ class LimitedDrivetrainLoop : public StateFeedbackLoop<4, 2, 2> {
+ public:
+ LimitedDrivetrainLoop(StateFeedbackLoop<4, 2, 2> &&loop)
+ : StateFeedbackLoop<4, 2, 2>(::std::move(loop)),
+ U_Poly_((Eigen::Matrix<double, 4, 2>() << 1, 0, -1, 0, 0, 1, 0, -1)
+ .finished(),
+ (Eigen::Matrix<double, 4, 1>() << 12.0, 12.0, 12.0, 12.0)
+ .finished()) {
+ ::aos::controls::HPolytope<0>::Init();
+ T << 1, -1, 1, 1;
+ T_inverse = T.inverse();
+ }
+
+ bool output_was_capped() const { return output_was_capped_; }
+
+ private:
+ virtual void CapU() {
+ const Eigen::Matrix<double, 4, 1> error = R() - X_hat();
+
+ if (::std::abs(U(0, 0)) > 12.0 || ::std::abs(U(1, 0)) > 12.0) {
+ mutable_U() =
+ U() * 12.0 / ::std::max(::std::abs(U(0, 0)), ::std::abs(U(1, 0)));
+ LOG_MATRIX(DEBUG, "U is now", U());
+ // TODO(Austin): Figure out why the polytope stuff wasn't working and
+ // remove this hack.
+ output_was_capped_ = true;
+ return;
+
+ LOG_MATRIX(DEBUG, "U at start", U());
+ LOG_MATRIX(DEBUG, "R at start", R());
+ LOG_MATRIX(DEBUG, "Xhat at start", X_hat());
+
+ Eigen::Matrix<double, 2, 2> position_K;
+ position_K << K(0, 0), K(0, 2), K(1, 0), K(1, 2);
+ Eigen::Matrix<double, 2, 2> velocity_K;
+ velocity_K << K(0, 1), K(0, 3), K(1, 1), K(1, 3);
+
+ Eigen::Matrix<double, 2, 1> position_error;
+ position_error << error(0, 0), error(2, 0);
+ const auto drive_error = T_inverse * position_error;
+ Eigen::Matrix<double, 2, 1> velocity_error;
+ velocity_error << error(1, 0), error(3, 0);
+ LOG_MATRIX(DEBUG, "error", error);
+
+ const auto &poly = U_Poly_;
+ const Eigen::Matrix<double, 4, 2> pos_poly_H =
+ poly.H() * position_K * T;
+ const Eigen::Matrix<double, 4, 1> pos_poly_k =
+ poly.k() - poly.H() * velocity_K * velocity_error;
+ const ::aos::controls::HPolytope<2> pos_poly(pos_poly_H, pos_poly_k);
+
+ Eigen::Matrix<double, 2, 1> adjusted_pos_error;
+ {
+ const auto &P = drive_error;
+
+ Eigen::Matrix<double, 1, 2> L45;
+ L45 << ::aos::sign(P(1, 0)), -::aos::sign(P(0, 0));
+ const double w45 = 0;
+
+ Eigen::Matrix<double, 1, 2> LH;
+ if (::std::abs(P(0, 0)) > ::std::abs(P(1, 0))) {
+ LH << 0, 1;
+ } else {
+ LH << 1, 0;
+ }
+ const double wh = LH.dot(P);
+
+ Eigen::Matrix<double, 2, 2> standard;
+ standard << L45, LH;
+ Eigen::Matrix<double, 2, 1> W;
+ W << w45, wh;
+ const Eigen::Matrix<double, 2, 1> intersection =
+ standard.inverse() * W;
+
+ bool is_inside_h;
+ const auto adjusted_pos_error_h = frc971::control_loops::DoCoerceGoal(
+ pos_poly, LH, wh, drive_error, &is_inside_h);
+ const auto adjusted_pos_error_45 =
+ frc971::control_loops::DoCoerceGoal(pos_poly, L45, w45,
+ intersection, nullptr);
+ if (pos_poly.IsInside(intersection)) {
+ adjusted_pos_error = adjusted_pos_error_h;
+ } else {
+ if (is_inside_h) {
+ if (adjusted_pos_error_h.norm() > adjusted_pos_error_45.norm()) {
+ adjusted_pos_error = adjusted_pos_error_h;
+ } else {
+ adjusted_pos_error = adjusted_pos_error_45;
+ }
+ } else {
+ adjusted_pos_error = adjusted_pos_error_45;
+ }
+ }
+ }
+
+ LOG_MATRIX(DEBUG, "adjusted_pos_error", adjusted_pos_error);
+ mutable_U() =
+ velocity_K * velocity_error + position_K * T * adjusted_pos_error;
+ LOG_MATRIX(DEBUG, "U is now", U());
+ } else {
+ output_was_capped_ = false;
+ }
+ }
+
+ const ::aos::controls::HPolytope<2> U_Poly_;
+ Eigen::Matrix<double, 2, 2> T, T_inverse;
+ bool output_was_capped_ = false;
+ ;
+ };
+
+ DrivetrainMotorsSS()
+ : loop_(new LimitedDrivetrainLoop(
+ ::bot3::control_loops::MakeDrivetrainLoop())),
+ filtered_offset_(0.0),
+ gyro_(0.0),
+ left_goal_(0.0),
+ right_goal_(0.0),
+ raw_left_(0.0),
+ raw_right_(0.0) {
+ // Low gear on both.
+ loop_->set_controller_index(0);
+ }
+
+ void SetGoal(double left, double left_velocity, double right,
+ double right_velocity) {
+ left_goal_ = left;
+ right_goal_ = right;
+ loop_->mutable_R() << left, left_velocity, right, right_velocity;
+ }
+ void SetRawPosition(double left, double right) {
+ raw_right_ = right;
+ raw_left_ = left;
+ Eigen::Matrix<double, 2, 1> Y;
+ Y << left + filtered_offset_, right - filtered_offset_;
+ loop_->Correct(Y);
+ }
+ void SetPosition(double left, double right, double gyro) {
+ // Decay the offset quickly because this gyro is great.
+ const double offset = (right - left - gyro * kDrivetrainTurnWidth) / 2.0;
+ filtered_offset_ = 0.25 * offset + 0.75 * filtered_offset_;
+ gyro_ = gyro;
+ SetRawPosition(left, right);
+ }
+
+ void SetExternalMotors(double left_voltage, double right_voltage) {
+ loop_->mutable_U() << left_voltage, right_voltage;
+ }
+
+ void Update(bool stop_motors, bool enable_control_loop) {
+ if (enable_control_loop) {
+ loop_->Update(stop_motors);
+ } else {
+ if (stop_motors) {
+ loop_->mutable_U().setZero();
+ loop_->mutable_U_uncapped().setZero();
+ }
+ loop_->UpdateObserver();
+ }
+ ::Eigen::Matrix<double, 4, 1> E = loop_->R() - loop_->X_hat();
+ LOG_MATRIX(DEBUG, "E", E);
+ }
+
+ double GetEstimatedRobotSpeed() const {
+ // lets just call the average of left and right velocities close enough
+ return (loop_->X_hat(1, 0) + loop_->X_hat(3, 0)) / 2;
+ }
+
+ double GetEstimatedLeftEncoder() const { return loop_->X_hat(0, 0); }
+
+ double GetEstimatedRightEncoder() const { return loop_->X_hat(2, 0); }
+
+ bool OutputWasCapped() const { return loop_->output_was_capped(); }
+
+ void SendMotors(DrivetrainQueue::Output *output) const {
+ if (output) {
+ output->left_voltage = loop_->U(0, 0);
+ output->right_voltage = loop_->U(1, 0);
+ output->left_high = false;
+ output->right_high = false;
+ }
+ }
+
+ const LimitedDrivetrainLoop &loop() const { return *loop_; }
+
+ private:
+ ::std::unique_ptr<LimitedDrivetrainLoop> loop_;
+
+ double filtered_offset_;
+ double gyro_;
+ double left_goal_;
+ double right_goal_;
+ double raw_left_;
+ double raw_right_;
+};
+
+class PolyDrivetrain {
+ public:
+ enum Gear { HIGH, LOW, SHIFTING_UP, SHIFTING_DOWN };
+ // Stall Torque in N m
+ static constexpr double kStallTorque = 2.42;
+ // Stall Current in Amps
+ static constexpr double kStallCurrent = 133.0;
+ // Free Speed in RPM. Used number from last year.
+ static constexpr double kFreeSpeed = 4650.0;
+ // Free Current in Amps
+ static constexpr double kFreeCurrent = 2.7;
+ // Moment of inertia of the drivetrain in kg m^2
+ // Just borrowed from last year.
+ static constexpr double J = 10;
+ // Mass of the robot, in kg.
+ static constexpr double m = 68;
+ // Radius of the robot, in meters (from last year).
+ static constexpr double rb = 0.9603 / 2.0;
+ static constexpr double kWheelRadius = 0.0515938;
+ // Resistance of the motor, divided by the number of motors.
+ static constexpr double kR =
+ (12.0 / kStallCurrent / 2 + 0.03) / (0.93 * 0.93);
+ // Motor velocity constant
+ static constexpr double Kv =
+ ((kFreeSpeed / 60.0 * 2.0 * M_PI) / (12.0 - kR * kFreeCurrent));
+ // Torque constant
+ static constexpr double Kt = kStallTorque / kStallCurrent;
+
+ PolyDrivetrain()
+ : U_Poly_((Eigen::Matrix<double, 4, 2>() << /*[[*/ 1, 0 /*]*/,
+ /*[*/ -1, 0 /*]*/,
+ /*[*/ 0, 1 /*]*/,
+ /*[*/ 0, -1 /*]]*/).finished(),
+ (Eigen::Matrix<double, 4, 1>() << /*[[*/ 12 /*]*/,
+ /*[*/ 12 /*]*/,
+ /*[*/ 12 /*]*/,
+ /*[*/ 12 /*]]*/).finished()),
+ loop_(new StateFeedbackLoop<2, 2, 2>(
+ ::bot3::control_loops::MakeVelocityDrivetrainLoop())),
+ ttrust_(1.1),
+ wheel_(0.0),
+ throttle_(0.0),
+ quickturn_(false),
+ stale_count_(0),
+ position_time_delta_(0.01),
+ left_gear_(LOW),
+ right_gear_(LOW),
+ counter_(0) {
+ last_position_.Zero();
+ position_.Zero();
+ }
+ static bool IsInGear(Gear gear) { return gear == LOW || gear == HIGH; }
+
+ static double MotorSpeed(
+ const frc971::constants::ShifterHallEffect &hall_effect,
+ double shifter_position, double velocity) {
+ // TODO(austin): G_high, G_low and kWheelRadius
+ const double avg_hall_effect =
+ (hall_effect.clear_high + hall_effect.clear_low) / 2.0;
+
+ if (shifter_position > avg_hall_effect) {
+ return velocity / kDrivetrainHighGearRatio / kWheelRadius;
+ } else {
+ return velocity / kDrivetrainLowGearRatio / kWheelRadius;
+ }
+ }
+
+ Gear ComputeGear(const frc971::constants::ShifterHallEffect &hall_effect,
+ double velocity, Gear current) {
+ const double low_omega = MotorSpeed(hall_effect, 0.0, ::std::abs(velocity));
+ const double high_omega =
+ MotorSpeed(hall_effect, 1.0, ::std::abs(velocity));
+
+ double high_torque = ((12.0 - high_omega / Kv) * Kt / kR);
+ double low_torque = ((12.0 - low_omega / Kv) * Kt / kR);
+ double high_power = high_torque * high_omega;
+ double low_power = low_torque * low_omega;
+
+ // TODO(aschuh): Do this right!
+ if ((current == HIGH || high_power > low_power + 160) &&
+ ::std::abs(velocity) > 0.14) {
+ return HIGH;
+ } else {
+ return LOW;
+ }
+ }
+
+ void SetGoal(double wheel, double throttle, bool quickturn, bool highgear) {
+ const double kWheelNonLinearity = 0.5;
+ // Apply a sin function that's scaled to make it feel better.
+ const double angular_range = M_PI_2 * kWheelNonLinearity;
+
+ wheel_ = sin(angular_range * wheel) / sin(angular_range);
+ wheel_ = sin(angular_range * wheel_) / sin(angular_range);
+ wheel_ *= 2.3;
+ quickturn_ = quickturn;
+
+ static const double kThrottleDeadband = 0.05;
+ if (::std::abs(throttle) < kThrottleDeadband) {
+ throttle_ = 0;
+ } else {
+ throttle_ = copysign((::std::abs(throttle) - kThrottleDeadband) /
+ (1.0 - kThrottleDeadband),
+ throttle);
+ }
+
+ // TODO(austin): Fix the upshift logic to include states.
+ Gear requested_gear;
+ if (false) {
+ const double current_left_velocity =
+ (position_.left_encoder - last_position_.left_encoder) /
+ position_time_delta_;
+ const double current_right_velocity =
+ (position_.right_encoder - last_position_.right_encoder) /
+ position_time_delta_;
+
+ Gear left_requested = ComputeGear(kDrivetrainLeftShifter,
+ current_left_velocity, left_gear_);
+ Gear right_requested = ComputeGear(kDrivetrainRightShifter,
+ current_right_velocity, right_gear_);
+ requested_gear =
+ (left_requested == HIGH || right_requested == HIGH) ? HIGH : LOW;
+ } else {
+ requested_gear = highgear ? HIGH : LOW;
+ }
+
+ const Gear shift_up = kDrivetrainClutchTransmission ? HIGH : SHIFTING_UP;
+ const Gear shift_down = kDrivetrainClutchTransmission ? LOW : SHIFTING_DOWN;
+
+ if (left_gear_ != requested_gear) {
+ if (IsInGear(left_gear_)) {
+ if (requested_gear == HIGH) {
+ left_gear_ = shift_up;
+ } else {
+ left_gear_ = shift_down;
+ }
+ } else {
+ if (requested_gear == HIGH && left_gear_ == SHIFTING_DOWN) {
+ left_gear_ = SHIFTING_UP;
+ } else if (requested_gear == LOW && left_gear_ == SHIFTING_UP) {
+ left_gear_ = SHIFTING_DOWN;
+ }
+ }
+ }
+ if (right_gear_ != requested_gear) {
+ if (IsInGear(right_gear_)) {
+ if (requested_gear == HIGH) {
+ right_gear_ = shift_up;
+ } else {
+ right_gear_ = shift_down;
+ }
+ } else {
+ if (requested_gear == HIGH && right_gear_ == SHIFTING_DOWN) {
+ right_gear_ = SHIFTING_UP;
+ } else if (requested_gear == LOW && right_gear_ == SHIFTING_UP) {
+ right_gear_ = SHIFTING_DOWN;
+ }
+ }
+ }
+ }
+ void SetPosition(const DrivetrainQueue::Position *position) {
+ if (position == NULL) {
+ ++stale_count_;
+ } else {
+ last_position_ = position_;
+ position_ = *position;
+ position_time_delta_ = (stale_count_ + 1) * 0.01;
+ stale_count_ = 0;
+ }
+
+#if HAVE_SHIFTERS
+ if (position) {
+ GearLogging gear_logging;
+ // Switch to the correct controller.
+ const double left_middle_shifter_position =
+ (kDrivetrainLeftShifter.clear_high +
+ kDrivetrainLeftShifter.clear_low) /
+ 2.0;
+ const double right_middle_shifter_position =
+ (kDrivetrainRightShifter.clear_high +
+ kDrivetrainRightShifter.clear_low) /
+ 2.0;
+
+ if (position->left_shifter_position < left_middle_shifter_position ||
+ left_gear_ == LOW) {
+ if (position->right_shifter_position < right_middle_shifter_position ||
+ right_gear_ == LOW) {
+ gear_logging.left_loop_high = false;
+ gear_logging.right_loop_high = false;
+ loop_->set_controller_index(gear_logging.controller_index = 0);
+ } else {
+ gear_logging.left_loop_high = false;
+ gear_logging.right_loop_high = true;
+ loop_->set_controller_index(gear_logging.controller_index = 1);
+ }
+ } else {
+ if (position->right_shifter_position < right_middle_shifter_position ||
+ right_gear_ == LOW) {
+ gear_logging.left_loop_high = true;
+ gear_logging.right_loop_high = false;
+ loop_->set_controller_index(gear_logging.controller_index = 2);
+ } else {
+ gear_logging.left_loop_high = true;
+ gear_logging.right_loop_high = true;
+ loop_->set_controller_index(gear_logging.controller_index = 3);
+ }
+ }
+
+ // TODO(austin): Constants.
+ if (position->left_shifter_position > kDrivetrainLeftShifter.clear_high &&
+ left_gear_ == SHIFTING_UP) {
+ left_gear_ = HIGH;
+ }
+ if (position->left_shifter_position < kDrivetrainLeftShifter.clear_low &&
+ left_gear_ == SHIFTING_DOWN) {
+ left_gear_ = LOW;
+ }
+ if (position->right_shifter_position >
+ kDrivetrainRightShifter.clear_high &&
+ right_gear_ == SHIFTING_UP) {
+ right_gear_ = HIGH;
+ }
+ if (position->right_shifter_position <
+ kDrivetrainRightShifter.clear_low &&
+ right_gear_ == SHIFTING_DOWN) {
+ right_gear_ = LOW;
+ }
+
+ gear_logging.left_state = left_gear_;
+ gear_logging.right_state = right_gear_;
+ LOG_STRUCT(DEBUG, "state", gear_logging);
+ }
+#endif
+ }
+
+ double FilterVelocity(double throttle) {
+ const Eigen::Matrix<double, 2, 2> FF =
+ loop_->B().inverse() *
+ (Eigen::Matrix<double, 2, 2>::Identity() - loop_->A());
+
+ constexpr int kHighGearController = 3;
+ const Eigen::Matrix<double, 2, 2> FF_high =
+ loop_->controller(kHighGearController).plant.B().inverse() *
+ (Eigen::Matrix<double, 2, 2>::Identity() -
+ loop_->controller(kHighGearController).plant.A());
+
+ ::Eigen::Matrix<double, 1, 2> FF_sum = FF.colwise().sum();
+ int min_FF_sum_index;
+ const double min_FF_sum = FF_sum.minCoeff(&min_FF_sum_index);
+ const double min_K_sum = loop_->K().col(min_FF_sum_index).sum();
+ const double high_min_FF_sum = FF_high.col(0).sum();
+
+ const double adjusted_ff_voltage = ::aos::Clip(
+ throttle * 12.0 * min_FF_sum / high_min_FF_sum, -12.0, 12.0);
+ return (adjusted_ff_voltage +
+ ttrust_ * min_K_sum * (loop_->X_hat(0, 0) + loop_->X_hat(1, 0)) /
+ 2.0) /
+ (ttrust_ * min_K_sum + min_FF_sum);
+ }
+
+ double MaxVelocity() {
+ const Eigen::Matrix<double, 2, 2> FF =
+ loop_->B().inverse() *
+ (Eigen::Matrix<double, 2, 2>::Identity() - loop_->A());
+
+ constexpr int kHighGearController = 3;
+ const Eigen::Matrix<double, 2, 2> FF_high =
+ loop_->controller(kHighGearController).plant.B().inverse() *
+ (Eigen::Matrix<double, 2, 2>::Identity() -
+ loop_->controller(kHighGearController).plant.A());
+
+ ::Eigen::Matrix<double, 1, 2> FF_sum = FF.colwise().sum();
+ int min_FF_sum_index;
+ const double min_FF_sum = FF_sum.minCoeff(&min_FF_sum_index);
+ // const double min_K_sum = loop_->K().col(min_FF_sum_index).sum();
+ const double high_min_FF_sum = FF_high.col(0).sum();
+
+ const double adjusted_ff_voltage =
+ ::aos::Clip(12.0 * min_FF_sum / high_min_FF_sum, -12.0, 12.0);
+ return adjusted_ff_voltage / min_FF_sum;
+ }
+
+ void Update() {
+ // TODO(austin): Observer for the current velocity instead of difference
+ // calculations.
+ ++counter_;
+#if HAVE_SHIFTERS
+ const double current_left_velocity =
+ (position_.left_encoder - last_position_.left_encoder) /
+ position_time_delta_;
+ const double current_right_velocity =
+ (position_.right_encoder - last_position_.right_encoder) /
+ position_time_delta_;
+ const double left_motor_speed =
+ MotorSpeed(kDrivetrainLeftShifter, position_.left_shifter_position,
+ current_left_velocity);
+ const double right_motor_speed =
+ MotorSpeed(kDrivetrainRightShifter, position_.right_shifter_position,
+ current_right_velocity);
+
+ {
+ CIMLogging logging;
+
+ // Reset the CIM model to the current conditions to be ready for when we
+ // shift.
+ if (IsInGear(left_gear_)) {
+ logging.left_in_gear = true;
+ } else {
+ logging.left_in_gear = false;
+ }
+ logging.left_motor_speed = left_motor_speed;
+ logging.left_velocity = current_left_velocity;
+ if (IsInGear(right_gear_)) {
+ logging.right_in_gear = true;
+ } else {
+ logging.right_in_gear = false;
+ }
+ logging.right_motor_speed = right_motor_speed;
+ logging.right_velocity = current_right_velocity;
+
+ LOG_STRUCT(DEBUG, "currently", logging);
+ }
+#endif
+
+#if HAVE_SHIFTERS
+ if (IsInGear(left_gear_) && IsInGear(right_gear_)) {
+#else
+ {
+#endif
+ // FF * X = U (steady state)
+ const Eigen::Matrix<double, 2, 2> FF =
+ loop_->B().inverse() *
+ (Eigen::Matrix<double, 2, 2>::Identity() - loop_->A());
+
+ // Invert the plant to figure out how the velocity filter would have to
+ // work
+ // out in order to filter out the forwards negative inertia.
+ // This math assumes that the left and right power and velocity are
+ // equals,
+ // and that the plant is the same on the left and right.
+ const double fvel = FilterVelocity(throttle_);
+
+ const double sign_svel = wheel_ * ((fvel > 0.0) ? 1.0 : -1.0);
+ double steering_velocity;
+ if (quickturn_) {
+ steering_velocity = wheel_ * MaxVelocity();
+ } else {
+ steering_velocity = ::std::abs(fvel) * wheel_;
+ }
+ const double left_velocity = fvel - steering_velocity;
+ const double right_velocity = fvel + steering_velocity;
+ LOG(DEBUG, "l=%f r=%f\n", left_velocity, right_velocity);
+
+ // Integrate velocity to get the position.
+ // This position is used to get integral control.
+ loop_->mutable_R() << left_velocity, right_velocity;
+
+ if (!quickturn_) {
+ // K * R = w
+ Eigen::Matrix<double, 1, 2> equality_k;
+ equality_k << 1 + sign_svel, -(1 - sign_svel);
+ const double equality_w = 0.0;
+
+ // Construct a constraint on R by manipulating the constraint on U
+ ::aos::controls::HPolytope<2> R_poly = ::aos::controls::HPolytope<2>(
+ U_Poly_.H() * (loop_->K() + FF),
+ U_Poly_.k() + U_Poly_.H() * loop_->K() * loop_->X_hat());
+
+ // Limit R back inside the box.
+ loop_->mutable_R() = frc971::control_loops::CoerceGoal(
+ R_poly, equality_k, equality_w, loop_->R());
+ }
+
+ const Eigen::Matrix<double, 2, 1> FF_volts = FF * loop_->R();
+ const Eigen::Matrix<double, 2, 1> U_ideal =
+ loop_->K() * (loop_->R() - loop_->X_hat()) + FF_volts;
+
+ for (int i = 0; i < 2; i++) {
+ loop_->mutable_U()[i] = ::aos::Clip(U_ideal[i], -12, 12);
+ }
+
+ // TODO(austin): Model this better.
+ // TODO(austin): Feed back?
+ loop_->mutable_X_hat() =
+ loop_->A() * loop_->X_hat() + loop_->B() * loop_->U();
+#if HAVE_SHIFTERS
+ } else {
+ // Any motor is not in gear. Speed match.
+ ::Eigen::Matrix<double, 1, 1> R_left;
+ ::Eigen::Matrix<double, 1, 1> R_right;
+ R_left(0, 0) = left_motor_speed;
+ R_right(0, 0) = right_motor_speed;
+
+ const double wiggle =
+ (static_cast<double>((counter_ % 20) / 10) - 0.5) * 5.0;
+
+ loop_->mutable_U(0, 0) =
+ ::aos::Clip((R_left / Kv)(0, 0) + (IsInGear(left_gear_) ? 0 : wiggle),
+ -12.0, 12.0);
+ loop_->mutable_U(1, 0) = ::aos::Clip(
+ (R_right / Kv)(0, 0) + (IsInGear(right_gear_) ? 0 : wiggle), -12.0,
+ 12.0);
+ loop_->mutable_U() *= 12.0 / ::aos::robot_state->voltage_battery;
+#endif
+ }
+ }
+
+ void SendMotors(DrivetrainQueue::Output *output) {
+ if (output != NULL) {
+ output->left_voltage = loop_->U(0, 0);
+ output->right_voltage = loop_->U(1, 0);
+ output->left_high = left_gear_ == HIGH || left_gear_ == SHIFTING_UP;
+ output->right_high = right_gear_ == HIGH || right_gear_ == SHIFTING_UP;
+ }
+ }
+
+ private:
+ const ::aos::controls::HPolytope<2> U_Poly_;
+
+ ::std::unique_ptr<StateFeedbackLoop<2, 2, 2>> loop_;
+
+ const double ttrust_;
+ double wheel_;
+ double throttle_;
+ bool quickturn_;
+ int stale_count_;
+ double position_time_delta_;
+ Gear left_gear_;
+ Gear right_gear_;
+ DrivetrainQueue::Position last_position_;
+ DrivetrainQueue::Position position_;
+ int counter_;
+};
+constexpr double PolyDrivetrain::kStallTorque;
+constexpr double PolyDrivetrain::kStallCurrent;
+constexpr double PolyDrivetrain::kFreeSpeed;
+constexpr double PolyDrivetrain::kFreeCurrent;
+constexpr double PolyDrivetrain::J;
+constexpr double PolyDrivetrain::m;
+constexpr double PolyDrivetrain::rb;
+constexpr double PolyDrivetrain::kWheelRadius;
+constexpr double PolyDrivetrain::kR;
+constexpr double PolyDrivetrain::Kv;
+constexpr double PolyDrivetrain::Kt;
+
+void DrivetrainLoop::RunIteration(const DrivetrainQueue::Goal *goal,
+ const DrivetrainQueue::Position *position,
+ DrivetrainQueue::Output *output,
+ DrivetrainQueue::Status *status) {
+ // TODO(aschuh): These should be members of the class.
+ static DrivetrainMotorsSS dt_closedloop;
+ static PolyDrivetrain dt_openloop;
+
+ bool bad_pos = false;
+ if (position == nullptr) {
+ LOG_INTERVAL(no_position_);
+ bad_pos = true;
+ }
+ no_position_.Print();
+
+ bool control_loop_driving = false;
+ if (goal) {
+ double wheel = goal->steering;
+ double throttle = goal->throttle;
+ bool quickturn = goal->quickturn;
+#if HAVE_SHIFTERS
+ bool highgear = goal->highgear;
+#endif
+
+ control_loop_driving = goal->control_loop_driving;
+ double left_goal = goal->left_goal;
+ double right_goal = goal->right_goal;
+
+ dt_closedloop.SetGoal(left_goal, goal->left_velocity_goal, right_goal,
+ goal->right_velocity_goal);
+#if HAVE_SHIFTERS
+ dt_openloop.SetGoal(wheel, throttle, quickturn, highgear);
+#else
+ dt_openloop.SetGoal(wheel, throttle, quickturn, false);
+#endif
+ }
+
+ if (!bad_pos) {
+ const double left_encoder = position->left_encoder;
+ const double right_encoder = position->right_encoder;
+ if (gyro_reading.FetchLatest()) {
+ LOG_STRUCT(DEBUG, "using", *gyro_reading.get());
+ dt_closedloop.SetPosition(left_encoder, right_encoder,
+ gyro_reading->angle);
+ } else {
+ dt_closedloop.SetRawPosition(left_encoder, right_encoder);
+ }
+ }
+ dt_openloop.SetPosition(position);
+ dt_openloop.Update();
+
+ if (control_loop_driving) {
+ dt_closedloop.Update(output == NULL, true);
+ dt_closedloop.SendMotors(output);
+ } else {
+ dt_openloop.SendMotors(output);
+ if (output) {
+ dt_closedloop.SetExternalMotors(output->left_voltage,
+ output->right_voltage);
+ }
+ dt_closedloop.Update(output == NULL, false);
+ }
+
+ // set the output status of the control loop state
+ if (status) {
+ bool done = false;
+ if (goal) {
+ done = ((::std::abs(goal->left_goal -
+ dt_closedloop.GetEstimatedLeftEncoder()) <
+ kDrivetrainDoneDistance) &&
+ (::std::abs(goal->right_goal -
+ dt_closedloop.GetEstimatedRightEncoder()) <
+ kDrivetrainDoneDistance));
+ }
+ status->is_done = done;
+ status->robot_speed = dt_closedloop.GetEstimatedRobotSpeed();
+ status->filtered_left_position = dt_closedloop.GetEstimatedLeftEncoder();
+ status->filtered_right_position = dt_closedloop.GetEstimatedRightEncoder();
+
+ status->filtered_left_velocity = dt_closedloop.loop().X_hat(1, 0);
+ status->filtered_right_velocity = dt_closedloop.loop().X_hat(3, 0);
+ status->output_was_capped = dt_closedloop.OutputWasCapped();
+ status->uncapped_left_voltage = dt_closedloop.loop().U_uncapped(0, 0);
+ status->uncapped_right_voltage = dt_closedloop.loop().U_uncapped(1, 0);
+ }
+}
+
+} // namespace control_loops
+} // namespace bot3
diff --git a/bot3/control_loops/drivetrain/drivetrain.gyp b/bot3/control_loops/drivetrain/drivetrain.gyp
new file mode 100644
index 0000000..92678d1
--- /dev/null
+++ b/bot3/control_loops/drivetrain/drivetrain.gyp
@@ -0,0 +1,105 @@
+{
+ 'targets': [
+ {
+ 'target_name': 'replay_drivetrain_bot3',
+ 'type': 'executable',
+ 'variables': {
+ 'no_rsync': 1,
+ },
+ 'sources': [
+ 'replay_drivetrain.cc',
+ ],
+ 'dependencies': [
+ 'drivetrain_queue',
+ '<(AOS)/common/controls/controls.gyp:replay_control_loop',
+ '<(AOS)/linux_code/linux_code.gyp:init',
+ ],
+ },
+ {
+ 'target_name': 'drivetrain_queue',
+ 'type': 'static_library',
+ 'sources': ['drivetrain.q'],
+ 'variables': {
+ 'header_path': 'bot3/control_loops/drivetrain',
+ },
+ 'dependencies': [
+ '<(AOS)/common/controls/controls.gyp:control_loop_queues',
+ ],
+ 'export_dependent_settings': [
+ '<(AOS)/common/controls/controls.gyp:control_loop_queues',
+ ],
+ 'includes': ['../../../aos/build/queues.gypi'],
+ },
+ {
+ 'target_name': 'polydrivetrain_plants',
+ 'type': 'static_library',
+ 'sources': [
+ 'polydrivetrain_dog_motor_plant.cc',
+ 'drivetrain_dog_motor_plant.cc',
+ ],
+ 'dependencies': [
+ '<(DEPTH)/frc971/control_loops/control_loops.gyp:state_feedback_loop',
+ ],
+ 'export_dependent_settings': [
+ '<(DEPTH)/frc971/control_loops/control_loops.gyp:state_feedback_loop',
+ ],
+ },
+ {
+ 'target_name': 'drivetrain_lib',
+ 'type': 'static_library',
+ 'sources': [
+ 'drivetrain.cc',
+ 'polydrivetrain_cim_plant.cc',
+ 'drivetrain_dog_motor_plant.cc',
+ 'polydrivetrain_dog_motor_plant.cc',
+ ],
+ 'dependencies': [
+ 'drivetrain_queue',
+ '<(AOS)/common/controls/controls.gyp:control_loop',
+ '<(AOS)/common/controls/controls.gyp:polytope',
+ '<(DEPTH)/frc971/control_loops/control_loops.gyp:state_feedback_loop',
+ '<(DEPTH)/frc971/control_loops/control_loops.gyp:coerce_goal',
+ '<(DEPTH)/frc971/queues/queues.gyp:gyro',
+ '<(AOS)/common/util/util.gyp:log_interval',
+ '<(AOS)/common/logging/logging.gyp:queue_logging',
+ '<(AOS)/common/logging/logging.gyp:matrix_logging',
+ ],
+ 'export_dependent_settings': [
+ '<(AOS)/common/controls/controls.gyp:polytope',
+ '<(DEPTH)/frc971/control_loops/control_loops.gyp:state_feedback_loop',
+ '<(DEPTH)/frc971/control_loops/control_loops.gyp:coerce_goal',
+ '<(AOS)/common/controls/controls.gyp:control_loop',
+ 'drivetrain_queue',
+ ],
+ },
+ {
+ 'target_name': 'drivetrain_lib_test_bot3',
+ 'type': 'executable',
+ 'sources': [
+ 'drivetrain_lib_test.cc',
+ ],
+ 'dependencies': [
+ '<(EXTERNALS):gtest',
+ 'drivetrain_queue',
+ 'drivetrain_lib',
+ '<(AOS)/common/controls/controls.gyp:control_loop_test',
+ '<(DEPTH)/frc971/control_loops/control_loops.gyp:state_feedback_loop',
+ '<(DEPTH)/frc971/queues/queues.gyp:gyro',
+ '<(AOS)/common/common.gyp:queues',
+ '<(AOS)/common/network/network.gyp:team_number',
+ ],
+ },
+ {
+ 'target_name': 'drivetrain_bot3',
+ 'type': 'executable',
+ 'sources': [
+ 'drivetrain_main.cc',
+ ],
+ 'dependencies': [
+ '<(AOS)/linux_code/linux_code.gyp:init',
+ 'drivetrain_lib',
+ 'drivetrain_queue',
+ ],
+ },
+ ],
+}
diff --git a/bot3/control_loops/drivetrain/drivetrain.h b/bot3/control_loops/drivetrain/drivetrain.h
new file mode 100644
index 0000000..551349d
--- /dev/null
+++ b/bot3/control_loops/drivetrain/drivetrain.h
@@ -0,0 +1,60 @@
+#ifndef BOT3_CONTROL_LOOPS_DRIVETRAIN_H_
+#define BOT3_CONTROL_LOOPS_DRIVETRAIN_H_
+
+#include "Eigen/Dense"
+
+#include "aos/common/controls/polytope.h"
+#include "aos/common/controls/control_loop.h"
+#include "aos/common/controls/polytope.h"
+#include "aos/common/util/log_interval.h"
+
+#include "frc971/shifter_hall_effect.h"
+#include "bot3/control_loops/drivetrain/drivetrain.q.h"
+
+namespace bot3 {
+namespace control_loops {
+
+// Constants
+// TODO(comran): Get actual constants.
+constexpr double kDrivetrainTurnWidth = 0.5;
+constexpr double kDrivetrainDoneDistance = 0.02;
+constexpr double kDrivetrainEncoderRatio = 20.0 / 50.0;
+constexpr double kDrivetrainHighGearRatio =
+ kDrivetrainEncoderRatio * 20.0 / 50.0;
+constexpr double kDrivetrainLowGearRatio = kDrivetrainHighGearRatio;
+const bool kDrivetrainClutchTransmission = false;
+const ::frc971::constants::ShifterHallEffect kDrivetrainRightShifter{
+ 555, 657, 660, 560, 0.2, 0.7};
+const ::frc971::constants::ShifterHallEffect kDrivetrainLeftShifter{
+ 555, 660, 644, 552, 0.2, 0.7};
+// End constants
+
+class DrivetrainLoop
+ : public aos::controls::ControlLoop<control_loops::DrivetrainQueue> {
+ public:
+ // Constructs a control loop which can take a Drivetrain or defaults to the
+ // drivetrain at bot3::control_loops::drivetrain
+ explicit DrivetrainLoop(control_loops::DrivetrainQueue *my_drivetrain =
+ &control_loops::drivetrain_queue)
+ : aos::controls::ControlLoop<control_loops::DrivetrainQueue>(
+ my_drivetrain) {
+ ::aos::controls::HPolytope<0>::Init();
+ }
+
+ protected:
+ // Executes one cycle of the control loop.
+ virtual void RunIteration(
+ const control_loops::DrivetrainQueue::Goal *goal,
+ const control_loops::DrivetrainQueue::Position *position,
+ control_loops::DrivetrainQueue::Output *output,
+ control_loops::DrivetrainQueue::Status *status);
+
+ typedef ::aos::util::SimpleLogInterval SimpleLogInterval;
+ SimpleLogInterval no_position_ = SimpleLogInterval(
+ ::aos::time::Time::InSeconds(0.25), WARNING, "no position");
+};
+
+} // namespace control_loops
+} // namespace bot3
+
+#endif // BOT3_CONTROL_LOOPS_DRIVETRAIN_H_
diff --git a/bot3/control_loops/drivetrain/drivetrain.q b/bot3/control_loops/drivetrain/drivetrain.q
new file mode 100644
index 0000000..f6eb030
--- /dev/null
+++ b/bot3/control_loops/drivetrain/drivetrain.q
@@ -0,0 +1,71 @@
+package bot3.control_loops;
+
+import "aos/common/controls/control_loops.q";
+
+struct GearLogging {
+ int8_t controller_index;
+ bool left_loop_high;
+ bool right_loop_high;
+ int8_t left_state;
+ int8_t right_state;
+};
+
+struct CIMLogging {
+ bool left_in_gear;
+ bool right_in_gear;
+ double left_motor_speed;
+ double right_motor_speed;
+ double left_velocity;
+ double right_velocity;
+};
+
+queue_group DrivetrainQueue {
+ implements aos.control_loops.ControlLoop;
+
+ message Goal {
+ double steering;
+ double throttle;
+ //bool highgear;
+ bool quickturn;
+ bool control_loop_driving;
+ double left_goal;
+ double left_velocity_goal;
+ double right_goal;
+ double right_velocity_goal;
+ };
+
+ message Position {
+ double left_encoder;
+ double right_encoder;
+ //double left_shifter_position;
+ //double right_shifter_position;
+ };
+
+ message Output {
+ double left_voltage;
+ double right_voltage;
+ bool left_high;
+ bool right_high;
+ };
+
+ message Status {
+ double robot_speed;
+ double filtered_left_position;
+ double filtered_right_position;
+ double filtered_left_velocity;
+ double filtered_right_velocity;
+
+ double uncapped_left_voltage;
+ double uncapped_right_voltage;
+ bool output_was_capped;
+
+ bool is_done;
+ };
+
+ queue Goal goal;
+ queue Position position;
+ queue Output output;
+ queue Status status;
+};
+
+queue_group DrivetrainQueue drivetrain_queue;
diff --git a/bot3/control_loops/drivetrain/drivetrain_dog_motor_plant.cc b/bot3/control_loops/drivetrain/drivetrain_dog_motor_plant.cc
new file mode 100644
index 0000000..e24fa71
--- /dev/null
+++ b/bot3/control_loops/drivetrain/drivetrain_dog_motor_plant.cc
@@ -0,0 +1,133 @@
+#include "bot3/control_loops/drivetrain/drivetrain_dog_motor_plant.h"
+
+#include <vector>
+
+#include "frc971/control_loops/state_feedback_loop.h"
+
+namespace bot3 {
+namespace control_loops {
+
+StateFeedbackPlantCoefficients<4, 2, 2> MakeDrivetrainLowLowPlantCoefficients() {
+ Eigen::Matrix<double, 4, 4> A;
+ A << 1.0, 0.00494348612824, 0.0, 1.24048455404e-05, 0.0, 0.977484092703, 0.0, 0.00492433284048, 0.0, 1.24048455404e-05, 1.0, 0.00494348612824, 0.0, 0.00492433284048, 0.0, 0.977484092703;
+ Eigen::Matrix<double, 4, 2> B;
+ B << 2.09180756177e-05, -4.59153636043e-06, 0.00833405032795, -0.00182269526971, -4.59153636043e-06, 2.09180756177e-05, -0.00182269526971, 0.00833405032795;
+ Eigen::Matrix<double, 2, 4> C;
+ C << 1, 0, 0, 0, 0, 0, 1, 0;
+ Eigen::Matrix<double, 2, 2> D;
+ D << 0, 0, 0, 0;
+ Eigen::Matrix<double, 2, 1> U_max;
+ U_max << 12.0, 12.0;
+ Eigen::Matrix<double, 2, 1> U_min;
+ U_min << -12.0, -12.0;
+ return StateFeedbackPlantCoefficients<4, 2, 2>(A, B, C, D, U_max, U_min);
+}
+
+StateFeedbackPlantCoefficients<4, 2, 2> MakeDrivetrainLowHighPlantCoefficients() {
+ Eigen::Matrix<double, 4, 4> A;
+ A << 1.0, 0.00494348612824, 0.0, 1.24048455404e-05, 0.0, 0.977484092703, 0.0, 0.00492433284048, 0.0, 1.24048455404e-05, 1.0, 0.00494348612824, 0.0, 0.00492433284048, 0.0, 0.977484092703;
+ Eigen::Matrix<double, 4, 2> B;
+ B << 2.09180756177e-05, -4.59153636043e-06, 0.00833405032795, -0.00182269526971, -4.59153636043e-06, 2.09180756177e-05, -0.00182269526971, 0.00833405032795;
+ Eigen::Matrix<double, 2, 4> C;
+ C << 1, 0, 0, 0, 0, 0, 1, 0;
+ Eigen::Matrix<double, 2, 2> D;
+ D << 0, 0, 0, 0;
+ Eigen::Matrix<double, 2, 1> U_max;
+ U_max << 12.0, 12.0;
+ Eigen::Matrix<double, 2, 1> U_min;
+ U_min << -12.0, -12.0;
+ return StateFeedbackPlantCoefficients<4, 2, 2>(A, B, C, D, U_max, U_min);
+}
+
+StateFeedbackPlantCoefficients<4, 2, 2> MakeDrivetrainHighLowPlantCoefficients() {
+ Eigen::Matrix<double, 4, 4> A;
+ A << 1.0, 0.00494348612824, 0.0, 1.24048455404e-05, 0.0, 0.977484092703, 0.0, 0.00492433284048, 0.0, 1.24048455404e-05, 1.0, 0.00494348612824, 0.0, 0.00492433284048, 0.0, 0.977484092703;
+ Eigen::Matrix<double, 4, 2> B;
+ B << 2.09180756177e-05, -4.59153636043e-06, 0.00833405032795, -0.00182269526971, -4.59153636043e-06, 2.09180756177e-05, -0.00182269526971, 0.00833405032795;
+ Eigen::Matrix<double, 2, 4> C;
+ C << 1, 0, 0, 0, 0, 0, 1, 0;
+ Eigen::Matrix<double, 2, 2> D;
+ D << 0, 0, 0, 0;
+ Eigen::Matrix<double, 2, 1> U_max;
+ U_max << 12.0, 12.0;
+ Eigen::Matrix<double, 2, 1> U_min;
+ U_min << -12.0, -12.0;
+ return StateFeedbackPlantCoefficients<4, 2, 2>(A, B, C, D, U_max, U_min);
+}
+
+StateFeedbackPlantCoefficients<4, 2, 2> MakeDrivetrainHighHighPlantCoefficients() {
+ Eigen::Matrix<double, 4, 4> A;
+ A << 1.0, 0.00494348612824, 0.0, 1.24048455404e-05, 0.0, 0.977484092703, 0.0, 0.00492433284048, 0.0, 1.24048455404e-05, 1.0, 0.00494348612824, 0.0, 0.00492433284048, 0.0, 0.977484092703;
+ Eigen::Matrix<double, 4, 2> B;
+ B << 2.09180756177e-05, -4.59153636043e-06, 0.00833405032795, -0.00182269526971, -4.59153636043e-06, 2.09180756177e-05, -0.00182269526971, 0.00833405032795;
+ Eigen::Matrix<double, 2, 4> C;
+ C << 1, 0, 0, 0, 0, 0, 1, 0;
+ Eigen::Matrix<double, 2, 2> D;
+ D << 0, 0, 0, 0;
+ Eigen::Matrix<double, 2, 1> U_max;
+ U_max << 12.0, 12.0;
+ Eigen::Matrix<double, 2, 1> U_min;
+ U_min << -12.0, -12.0;
+ return StateFeedbackPlantCoefficients<4, 2, 2>(A, B, C, D, U_max, U_min);
+}
+
+StateFeedbackController<4, 2, 2> MakeDrivetrainLowLowController() {
+ Eigen::Matrix<double, 4, 2> L;
+ L << 1.2774840927, 0.00492433284048, 79.1440456622, 1.05150722636, 0.00492433284048, 1.2774840927, 1.05150722636, 79.1440456622;
+ Eigen::Matrix<double, 2, 4> K;
+ K << 160.19677247, 15.5358468938, 1.60401194139, 1.33861213093, 1.60401194142, 1.33861213094, 160.19677247, 15.5358468938;
+ Eigen::Matrix<double, 4, 4> A_inv;
+ A_inv << 1.0, -0.00505742153256, 0.0, 1.27875036472e-05, 0.0, 1.02306051542, 0.0, -0.00515393603998, 0.0, 1.27875036472e-05, 1.0, -0.00505742153256, 0.0, -0.00515393603998, 0.0, 1.02306051542;
+ return StateFeedbackController<4, 2, 2>(L, K, A_inv, MakeDrivetrainLowLowPlantCoefficients());
+}
+
+StateFeedbackController<4, 2, 2> MakeDrivetrainLowHighController() {
+ Eigen::Matrix<double, 4, 2> L;
+ L << 1.2774840927, 0.00492433284048, 79.1440456622, 1.05150722636, 0.00492433284048, 1.2774840927, 1.05150722636, 79.1440456622;
+ Eigen::Matrix<double, 2, 4> K;
+ K << 160.19677247, 15.5358468938, 1.60401194139, 1.33861213093, 1.60401194142, 1.33861213094, 160.19677247, 15.5358468938;
+ Eigen::Matrix<double, 4, 4> A_inv;
+ A_inv << 1.0, -0.00505742153256, 0.0, 1.27875036472e-05, 0.0, 1.02306051542, 0.0, -0.00515393603998, 0.0, 1.27875036472e-05, 1.0, -0.00505742153256, 0.0, -0.00515393603998, 0.0, 1.02306051542;
+ return StateFeedbackController<4, 2, 2>(L, K, A_inv, MakeDrivetrainLowHighPlantCoefficients());
+}
+
+StateFeedbackController<4, 2, 2> MakeDrivetrainHighLowController() {
+ Eigen::Matrix<double, 4, 2> L;
+ L << 1.2774840927, 0.00492433284048, 79.1440456622, 1.05150722636, 0.00492433284048, 1.2774840927, 1.05150722636, 79.1440456622;
+ Eigen::Matrix<double, 2, 4> K;
+ K << 160.19677247, 15.5358468938, 1.60401194139, 1.33861213093, 1.60401194142, 1.33861213094, 160.19677247, 15.5358468938;
+ Eigen::Matrix<double, 4, 4> A_inv;
+ A_inv << 1.0, -0.00505742153256, 0.0, 1.27875036472e-05, 0.0, 1.02306051542, 0.0, -0.00515393603998, 0.0, 1.27875036472e-05, 1.0, -0.00505742153256, 0.0, -0.00515393603998, 0.0, 1.02306051542;
+ return StateFeedbackController<4, 2, 2>(L, K, A_inv, MakeDrivetrainHighLowPlantCoefficients());
+}
+
+StateFeedbackController<4, 2, 2> MakeDrivetrainHighHighController() {
+ Eigen::Matrix<double, 4, 2> L;
+ L << 1.2774840927, 0.00492433284048, 79.1440456622, 1.05150722636, 0.00492433284048, 1.2774840927, 1.05150722636, 79.1440456622;
+ Eigen::Matrix<double, 2, 4> K;
+ K << 160.19677247, 15.5358468938, 1.60401194139, 1.33861213093, 1.60401194142, 1.33861213094, 160.19677247, 15.5358468938;
+ Eigen::Matrix<double, 4, 4> A_inv;
+ A_inv << 1.0, -0.00505742153256, 0.0, 1.27875036472e-05, 0.0, 1.02306051542, 0.0, -0.00515393603998, 0.0, 1.27875036472e-05, 1.0, -0.00505742153256, 0.0, -0.00515393603998, 0.0, 1.02306051542;
+ return StateFeedbackController<4, 2, 2>(L, K, A_inv, MakeDrivetrainHighHighPlantCoefficients());
+}
+
+StateFeedbackPlant<4, 2, 2> MakeDrivetrainPlant() {
+ ::std::vector< ::std::unique_ptr<StateFeedbackPlantCoefficients<4, 2, 2>>> plants(4);
+ plants[0] = ::std::unique_ptr<StateFeedbackPlantCoefficients<4, 2, 2>>(new StateFeedbackPlantCoefficients<4, 2, 2>(MakeDrivetrainLowLowPlantCoefficients()));
+ plants[1] = ::std::unique_ptr<StateFeedbackPlantCoefficients<4, 2, 2>>(new StateFeedbackPlantCoefficients<4, 2, 2>(MakeDrivetrainLowHighPlantCoefficients()));
+ plants[2] = ::std::unique_ptr<StateFeedbackPlantCoefficients<4, 2, 2>>(new StateFeedbackPlantCoefficients<4, 2, 2>(MakeDrivetrainHighLowPlantCoefficients()));
+ plants[3] = ::std::unique_ptr<StateFeedbackPlantCoefficients<4, 2, 2>>(new StateFeedbackPlantCoefficients<4, 2, 2>(MakeDrivetrainHighHighPlantCoefficients()));
+ return StateFeedbackPlant<4, 2, 2>(&plants);
+}
+
+StateFeedbackLoop<4, 2, 2> MakeDrivetrainLoop() {
+ ::std::vector< ::std::unique_ptr<StateFeedbackController<4, 2, 2>>> controllers(4);
+ controllers[0] = ::std::unique_ptr<StateFeedbackController<4, 2, 2>>(new StateFeedbackController<4, 2, 2>(MakeDrivetrainLowLowController()));
+ controllers[1] = ::std::unique_ptr<StateFeedbackController<4, 2, 2>>(new StateFeedbackController<4, 2, 2>(MakeDrivetrainLowHighController()));
+ controllers[2] = ::std::unique_ptr<StateFeedbackController<4, 2, 2>>(new StateFeedbackController<4, 2, 2>(MakeDrivetrainHighLowController()));
+ controllers[3] = ::std::unique_ptr<StateFeedbackController<4, 2, 2>>(new StateFeedbackController<4, 2, 2>(MakeDrivetrainHighHighController()));
+ return StateFeedbackLoop<4, 2, 2>(&controllers);
+}
+
+} // namespace control_loops
+} // namespace bot3
diff --git a/bot3/control_loops/drivetrain/drivetrain_dog_motor_plant.h b/bot3/control_loops/drivetrain/drivetrain_dog_motor_plant.h
new file mode 100644
index 0000000..f5d5896
--- /dev/null
+++ b/bot3/control_loops/drivetrain/drivetrain_dog_motor_plant.h
@@ -0,0 +1,32 @@
+#ifndef BOT3_CONTROL_LOOPS_DRIVETRAIN_DRIVETRAIN_DOG_MOTOR_PLANT_H_
+#define BOT3_CONTROL_LOOPS_DRIVETRAIN_DRIVETRAIN_DOG_MOTOR_PLANT_H_
+
+#include "frc971/control_loops/state_feedback_loop.h"
+
+namespace bot3 {
+namespace control_loops {
+
+StateFeedbackPlantCoefficients<4, 2, 2> MakeDrivetrainLowLowPlantCoefficients();
+
+StateFeedbackController<4, 2, 2> MakeDrivetrainLowLowController();
+
+StateFeedbackPlantCoefficients<4, 2, 2> MakeDrivetrainLowHighPlantCoefficients();
+
+StateFeedbackController<4, 2, 2> MakeDrivetrainLowHighController();
+
+StateFeedbackPlantCoefficients<4, 2, 2> MakeDrivetrainHighLowPlantCoefficients();
+
+StateFeedbackController<4, 2, 2> MakeDrivetrainHighLowController();
+
+StateFeedbackPlantCoefficients<4, 2, 2> MakeDrivetrainHighHighPlantCoefficients();
+
+StateFeedbackController<4, 2, 2> MakeDrivetrainHighHighController();
+
+StateFeedbackPlant<4, 2, 2> MakeDrivetrainPlant();
+
+StateFeedbackLoop<4, 2, 2> MakeDrivetrainLoop();
+
+} // namespace control_loops
+} // namespace bot3
+
+#endif // BOT3_CONTROL_LOOPS_DRIVETRAIN_DRIVETRAIN_DOG_MOTOR_PLANT_H_
diff --git a/bot3/control_loops/drivetrain/drivetrain_lib_test.cc b/bot3/control_loops/drivetrain/drivetrain_lib_test.cc
new file mode 100644
index 0000000..b9e828e
--- /dev/null
+++ b/bot3/control_loops/drivetrain/drivetrain_lib_test.cc
@@ -0,0 +1,296 @@
+#include <unistd.h>
+
+#include <memory>
+
+#include "gtest/gtest.h"
+#include "aos/common/network/team_number.h"
+#include "aos/common/queue_testutils.h"
+#include "aos/common/controls/polytope.h"
+#include "aos/common/controls/control_loop_test.h"
+
+#include "bot3/control_loops/drivetrain/drivetrain.q.h"
+#include "bot3/control_loops/drivetrain/drivetrain.h"
+#include "frc971/control_loops/state_feedback_loop.h"
+#include "frc971/control_loops/coerce_goal.h"
+#include "bot3/control_loops/drivetrain/drivetrain_dog_motor_plant.h"
+#include "frc971/queues/gyro.q.h"
+
+namespace bot3 {
+namespace control_loops {
+namespace testing {
+
+class Environment : public ::testing::Environment {
+ public:
+ virtual ~Environment() {}
+ // how to set up the environment.
+ virtual void SetUp() {
+ aos::controls::HPolytope<0>::Init();
+ }
+};
+::testing::Environment* const holder_env =
+ ::testing::AddGlobalTestEnvironment(new Environment);
+
+class TeamNumberEnvironment : public ::testing::Environment {
+ public:
+ // Override this to define how to set up the environment.
+ virtual void SetUp() { aos::network::OverrideTeamNumber(971); }
+};
+
+::testing::Environment* const team_number_env =
+ ::testing::AddGlobalTestEnvironment(new TeamNumberEnvironment);
+
+// Class which simulates the drivetrain and sends out queue messages containing
+// the position.
+class DrivetrainSimulation {
+ public:
+ // Constructs a motor simulation.
+ // TODO(aschuh) Do we want to test the clutch one too?
+ DrivetrainSimulation()
+ : drivetrain_plant_(
+ new StateFeedbackPlant<4, 2, 2>(MakeDrivetrainPlant())),
+ my_drivetrain_queue_(".bot3.control_loops.drivetrain",
+ 0x8a8dde77, ".bot3.control_loops.drivetrain.goal",
+ ".bot3.control_loops.drivetrain.position",
+ ".bot3.control_loops.drivetrain.output",
+ ".bot3.control_loops.drivetrain.status") {
+ Reinitialize();
+ }
+
+ // Resets the plant.
+ void Reinitialize() {
+ drivetrain_plant_->mutable_X(0, 0) = 0.0;
+ drivetrain_plant_->mutable_X(1, 0) = 0.0;
+ drivetrain_plant_->mutable_Y() =
+ drivetrain_plant_->C() * drivetrain_plant_->X();
+ last_left_position_ = drivetrain_plant_->Y(0, 0);
+ last_right_position_ = drivetrain_plant_->Y(1, 0);
+ }
+
+ // Returns the position of the drivetrain.
+ double GetLeftPosition() const { return drivetrain_plant_->Y(0, 0); }
+ double GetRightPosition() const { return drivetrain_plant_->Y(1, 0); }
+
+ // Sends out the position queue messages.
+ void SendPositionMessage() {
+ const double left_encoder = GetLeftPosition();
+ const double right_encoder = GetRightPosition();
+
+ ::aos::ScopedMessagePtr<control_loops::DrivetrainQueue::Position> position =
+ my_drivetrain_queue_.position.MakeMessage();
+ position->left_encoder = left_encoder;
+ position->right_encoder = right_encoder;
+ position.Send();
+ }
+
+ // Simulates the drivetrain moving for one timestep.
+ void Simulate() {
+ last_left_position_ = drivetrain_plant_->Y(0, 0);
+ last_right_position_ = drivetrain_plant_->Y(1, 0);
+ EXPECT_TRUE(my_drivetrain_queue_.output.FetchLatest());
+ drivetrain_plant_->mutable_U() << my_drivetrain_queue_.output->left_voltage,
+ my_drivetrain_queue_.output->right_voltage;
+ drivetrain_plant_->Update();
+ }
+
+ ::std::unique_ptr<StateFeedbackPlant<4, 2, 2>> drivetrain_plant_;
+ private:
+ DrivetrainQueue my_drivetrain_queue_;
+ double last_left_position_;
+ double last_right_position_;
+};
+
+class DrivetrainTest : public ::aos::testing::ControlLoopTest {
+ protected:
+ // Create a new instance of the test queue so that it invalidates the queue
+ // that it points to. Otherwise, we will have a pointer to shared memory that
+ // is no longer valid.
+ DrivetrainQueue my_drivetrain_queue_;
+
+ // Create a loop and simulation plant.
+ DrivetrainLoop drivetrain_motor_;
+ DrivetrainSimulation drivetrain_motor_plant_;
+
+ DrivetrainTest() : my_drivetrain_queue_(".bot3.control_loops.drivetrain",
+ 0x8a8dde77,
+ ".bot3.control_loops.drivetrain.goal",
+ ".bot3.control_loops.drivetrain.position",
+ ".bot3.control_loops.drivetrain.output",
+ ".bot3.control_loops.drivetrain.status"),
+ drivetrain_motor_(&my_drivetrain_queue_),
+ drivetrain_motor_plant_() {
+ ::frc971::sensors::gyro_reading.Clear();
+ }
+
+ void VerifyNearGoal() {
+ my_drivetrain_queue_.goal.FetchLatest();
+ my_drivetrain_queue_.position.FetchLatest();
+ EXPECT_NEAR(my_drivetrain_queue_.goal->left_goal,
+ drivetrain_motor_plant_.GetLeftPosition(),
+ 1e-2);
+ EXPECT_NEAR(my_drivetrain_queue_.goal->right_goal,
+ drivetrain_motor_plant_.GetRightPosition(),
+ 1e-2);
+ }
+
+ virtual ~DrivetrainTest() {
+ ::frc971::sensors::gyro_reading.Clear();
+ }
+};
+
+// Tests that the drivetrain converges on a goal.
+TEST_F(DrivetrainTest, ConvergesCorrectly) {
+ my_drivetrain_queue_.goal.MakeWithBuilder().control_loop_driving(true)
+ .left_goal(-1.0)
+ .right_goal(1.0).Send();
+ for (int i = 0; i < 200; ++i) {
+ drivetrain_motor_plant_.SendPositionMessage();
+ drivetrain_motor_.Iterate();
+ drivetrain_motor_plant_.Simulate();
+ SimulateTimestep(true);
+ }
+ VerifyNearGoal();
+}
+
+// Tests that it survives disabling.
+TEST_F(DrivetrainTest, SurvivesDisabling) {
+ my_drivetrain_queue_.goal.MakeWithBuilder().control_loop_driving(true)
+ .left_goal(-1.0)
+ .right_goal(1.0).Send();
+ for (int i = 0; i < 500; ++i) {
+ drivetrain_motor_plant_.SendPositionMessage();
+ drivetrain_motor_.Iterate();
+ drivetrain_motor_plant_.Simulate();
+ if (i > 20 && i < 200) {
+ SimulateTimestep(false);
+ } else {
+ SimulateTimestep(true);
+ }
+ }
+ VerifyNearGoal();
+}
+
+// Tests that never having a goal doesn't break.
+TEST_F(DrivetrainTest, NoGoalStart) {
+ for (int i = 0; i < 20; ++i) {
+ drivetrain_motor_plant_.SendPositionMessage();
+ drivetrain_motor_.Iterate();
+ drivetrain_motor_plant_.Simulate();
+ }
+}
+
+// Tests that never having a goal, but having driver's station messages, doesn't
+// break.
+TEST_F(DrivetrainTest, NoGoalWithRobotState) {
+ for (int i = 0; i < 20; ++i) {
+ drivetrain_motor_plant_.SendPositionMessage();
+ drivetrain_motor_.Iterate();
+ drivetrain_motor_plant_.Simulate();
+ SimulateTimestep(true);
+ }
+}
+
+::aos::controls::HPolytope<2> MakeBox(double x1_min, double x1_max,
+ double x2_min, double x2_max) {
+ Eigen::Matrix<double, 4, 2> box_H;
+ box_H << /*[[*/ 1.0, 0.0 /*]*/,
+ /*[*/-1.0, 0.0 /*]*/,
+ /*[*/ 0.0, 1.0 /*]*/,
+ /*[*/ 0.0,-1.0 /*]]*/;
+ Eigen::Matrix<double, 4, 1> box_k;
+ box_k << /*[[*/ x1_max /*]*/,
+ /*[*/-x1_min /*]*/,
+ /*[*/ x2_max /*]*/,
+ /*[*/-x2_min /*]]*/;
+ ::aos::controls::HPolytope<2> t_poly(box_H, box_k);
+ return t_poly;
+}
+
+class CoerceGoalTest : public ::testing::Test {
+ public:
+ EIGEN_MAKE_ALIGNED_OPERATOR_NEW
+};
+
+// WHOOOHH!
+TEST_F(CoerceGoalTest, Inside) {
+ ::aos::controls::HPolytope<2> box = MakeBox(1, 2, 1, 2);
+
+ Eigen::Matrix<double, 1, 2> K;
+ K << /*[[*/ 1, -1 /*]]*/;
+
+ Eigen::Matrix<double, 2, 1> R;
+ R << /*[[*/ 1.5, 1.5 /*]]*/;
+
+ Eigen::Matrix<double, 2, 1> output =
+ ::frc971::control_loops::CoerceGoal(box, K, 0, R);
+
+ EXPECT_EQ(R(0, 0), output(0, 0));
+ EXPECT_EQ(R(1, 0), output(1, 0));
+}
+
+TEST_F(CoerceGoalTest, Outside_Inside_Intersect) {
+ ::aos::controls::HPolytope<2> box = MakeBox(1, 2, 1, 2);
+
+ Eigen::Matrix<double, 1, 2> K;
+ K << 1, -1;
+
+ Eigen::Matrix<double, 2, 1> R;
+ R << 5, 5;
+
+ Eigen::Matrix<double, 2, 1> output =
+ ::frc971::control_loops::CoerceGoal(box, K, 0, R);
+
+ EXPECT_EQ(2.0, output(0, 0));
+ EXPECT_EQ(2.0, output(1, 0));
+}
+
+TEST_F(CoerceGoalTest, Outside_Inside_no_Intersect) {
+ ::aos::controls::HPolytope<2> box = MakeBox(3, 4, 1, 2);
+
+ Eigen::Matrix<double, 1, 2> K;
+ K << 1, -1;
+
+ Eigen::Matrix<double, 2, 1> R;
+ R << 5, 5;
+
+ Eigen::Matrix<double, 2, 1> output =
+ ::frc971::control_loops::CoerceGoal(box, K, 0, R);
+
+ EXPECT_EQ(3.0, output(0, 0));
+ EXPECT_EQ(2.0, output(1, 0));
+}
+
+TEST_F(CoerceGoalTest, Middle_Of_Edge) {
+ ::aos::controls::HPolytope<2> box = MakeBox(0, 4, 1, 2);
+
+ Eigen::Matrix<double, 1, 2> K;
+ K << -1, 1;
+
+ Eigen::Matrix<double, 2, 1> R;
+ R << 5, 5;
+
+ Eigen::Matrix<double, 2, 1> output =
+ ::frc971::control_loops::CoerceGoal(box, K, 0, R);
+
+ EXPECT_EQ(2.0, output(0, 0));
+ EXPECT_EQ(2.0, output(1, 0));
+}
+
+TEST_F(CoerceGoalTest, PerpendicularLine) {
+ ::aos::controls::HPolytope<2> box = MakeBox(1, 2, 1, 2);
+
+ Eigen::Matrix<double, 1, 2> K;
+ K << 1, 1;
+
+ Eigen::Matrix<double, 2, 1> R;
+ R << 5, 5;
+
+ Eigen::Matrix<double, 2, 1> output =
+ ::frc971::control_loops::CoerceGoal(box, K, 0, R);
+
+ EXPECT_EQ(1.0, output(0, 0));
+ EXPECT_EQ(1.0, output(1, 0));
+}
+
+} // namespace testing
+} // namespace control_loops
+} // namespace bot3
diff --git a/bot3/control_loops/drivetrain/drivetrain_main.cc b/bot3/control_loops/drivetrain/drivetrain_main.cc
new file mode 100644
index 0000000..f06acc0
--- /dev/null
+++ b/bot3/control_loops/drivetrain/drivetrain_main.cc
@@ -0,0 +1,11 @@
+#include "bot3/control_loops/drivetrain/drivetrain.h"
+
+#include "aos/linux_code/init.h"
+
+int main() {
+ ::aos::Init();
+ bot3::control_loops::DrivetrainLoop drivetrain;
+ drivetrain.Run();
+ ::aos::Cleanup();
+ return 0;
+}
diff --git a/bot3/control_loops/drivetrain/polydrivetrain_cim_plant.cc b/bot3/control_loops/drivetrain/polydrivetrain_cim_plant.cc
new file mode 100644
index 0000000..86eb915
--- /dev/null
+++ b/bot3/control_loops/drivetrain/polydrivetrain_cim_plant.cc
@@ -0,0 +1,49 @@
+#include "bot3/control_loops/drivetrain/polydrivetrain_cim_plant.h"
+
+#include <vector>
+
+#include "frc971/control_loops/state_feedback_loop.h"
+
+namespace bot3 {
+namespace control_loops {
+
+StateFeedbackPlantCoefficients<1, 1, 1> MakeCIMPlantCoefficients() {
+ Eigen::Matrix<double, 1, 1> A;
+ A << 0.783924473544;
+ Eigen::Matrix<double, 1, 1> B;
+ B << 8.94979586973;
+ Eigen::Matrix<double, 1, 1> C;
+ C << 1;
+ Eigen::Matrix<double, 1, 1> D;
+ D << 0;
+ Eigen::Matrix<double, 1, 1> U_max;
+ U_max << 12.0;
+ Eigen::Matrix<double, 1, 1> U_min;
+ U_min << -12.0;
+ return StateFeedbackPlantCoefficients<1, 1, 1>(A, B, C, D, U_max, U_min);
+}
+
+StateFeedbackController<1, 1, 1> MakeCIMController() {
+ Eigen::Matrix<double, 1, 1> L;
+ L << 0.773924473544;
+ Eigen::Matrix<double, 1, 1> K;
+ K << 0.086473980503;
+ Eigen::Matrix<double, 1, 1> A_inv;
+ A_inv << 1.2756330919;
+ return StateFeedbackController<1, 1, 1>(L, K, A_inv, MakeCIMPlantCoefficients());
+}
+
+StateFeedbackPlant<1, 1, 1> MakeCIMPlant() {
+ ::std::vector< ::std::unique_ptr<StateFeedbackPlantCoefficients<1, 1, 1>>> plants(1);
+ plants[0] = ::std::unique_ptr<StateFeedbackPlantCoefficients<1, 1, 1>>(new StateFeedbackPlantCoefficients<1, 1, 1>(MakeCIMPlantCoefficients()));
+ return StateFeedbackPlant<1, 1, 1>(&plants);
+}
+
+StateFeedbackLoop<1, 1, 1> MakeCIMLoop() {
+ ::std::vector< ::std::unique_ptr<StateFeedbackController<1, 1, 1>>> controllers(1);
+ controllers[0] = ::std::unique_ptr<StateFeedbackController<1, 1, 1>>(new StateFeedbackController<1, 1, 1>(MakeCIMController()));
+ return StateFeedbackLoop<1, 1, 1>(&controllers);
+}
+
+} // namespace control_loops
+} // namespace bot3
diff --git a/bot3/control_loops/drivetrain/polydrivetrain_cim_plant.h b/bot3/control_loops/drivetrain/polydrivetrain_cim_plant.h
new file mode 100644
index 0000000..dcbd577
--- /dev/null
+++ b/bot3/control_loops/drivetrain/polydrivetrain_cim_plant.h
@@ -0,0 +1,20 @@
+#ifndef BOT3_CONTROL_LOOPS_DRIVETRAIN_POLYDRIVETRAIN_CIM_PLANT_H_
+#define BOT3_CONTROL_LOOPS_DRIVETRAIN_POLYDRIVETRAIN_CIM_PLANT_H_
+
+#include "frc971/control_loops/state_feedback_loop.h"
+
+namespace bot3 {
+namespace control_loops {
+
+StateFeedbackPlantCoefficients<1, 1, 1> MakeCIMPlantCoefficients();
+
+StateFeedbackController<1, 1, 1> MakeCIMController();
+
+StateFeedbackPlant<1, 1, 1> MakeCIMPlant();
+
+StateFeedbackLoop<1, 1, 1> MakeCIMLoop();
+
+} // namespace control_loops
+} // namespace bot3
+
+#endif // BOT3_CONTROL_LOOPS_DRIVETRAIN_POLYDRIVETRAIN_CIM_PLANT_H_
diff --git a/bot3/control_loops/drivetrain/polydrivetrain_dog_motor_plant.cc b/bot3/control_loops/drivetrain/polydrivetrain_dog_motor_plant.cc
new file mode 100644
index 0000000..95913ff
--- /dev/null
+++ b/bot3/control_loops/drivetrain/polydrivetrain_dog_motor_plant.cc
@@ -0,0 +1,133 @@
+#include "bot3/control_loops/drivetrain/polydrivetrain_dog_motor_plant.h"
+
+#include <vector>
+
+#include "frc971/control_loops/state_feedback_loop.h"
+
+namespace bot3 {
+namespace control_loops {
+
+StateFeedbackPlantCoefficients<2, 2, 2> MakeVelocityDrivetrainLowLowPlantCoefficients() {
+ Eigen::Matrix<double, 2, 2> A;
+ A << 0.955499400541, 0.00962691403749, 0.00962691403749, 0.955499400541;
+ Eigen::Matrix<double, 2, 2> B;
+ B << 0.0164714763931, -0.00356331126397, -0.00356331126397, 0.0164714763931;
+ Eigen::Matrix<double, 2, 2> C;
+ C << 1.0, 0.0, 0.0, 1.0;
+ Eigen::Matrix<double, 2, 2> D;
+ D << 0.0, 0.0, 0.0, 0.0;
+ Eigen::Matrix<double, 2, 1> U_max;
+ U_max << 12.0, 12.0;
+ Eigen::Matrix<double, 2, 1> U_min;
+ U_min << -12.0, -12.0;
+ return StateFeedbackPlantCoefficients<2, 2, 2>(A, B, C, D, U_max, U_min);
+}
+
+StateFeedbackPlantCoefficients<2, 2, 2> MakeVelocityDrivetrainLowHighPlantCoefficients() {
+ Eigen::Matrix<double, 2, 2> A;
+ A << 0.955499400541, 0.00962691403749, 0.00962691403749, 0.955499400541;
+ Eigen::Matrix<double, 2, 2> B;
+ B << 0.0164714763931, -0.00356331126397, -0.00356331126397, 0.0164714763931;
+ Eigen::Matrix<double, 2, 2> C;
+ C << 1.0, 0.0, 0.0, 1.0;
+ Eigen::Matrix<double, 2, 2> D;
+ D << 0.0, 0.0, 0.0, 0.0;
+ Eigen::Matrix<double, 2, 1> U_max;
+ U_max << 12.0, 12.0;
+ Eigen::Matrix<double, 2, 1> U_min;
+ U_min << -12.0, -12.0;
+ return StateFeedbackPlantCoefficients<2, 2, 2>(A, B, C, D, U_max, U_min);
+}
+
+StateFeedbackPlantCoefficients<2, 2, 2> MakeVelocityDrivetrainHighLowPlantCoefficients() {
+ Eigen::Matrix<double, 2, 2> A;
+ A << 0.955499400541, 0.00962691403749, 0.00962691403749, 0.955499400541;
+ Eigen::Matrix<double, 2, 2> B;
+ B << 0.0164714763931, -0.00356331126397, -0.00356331126397, 0.0164714763931;
+ Eigen::Matrix<double, 2, 2> C;
+ C << 1.0, 0.0, 0.0, 1.0;
+ Eigen::Matrix<double, 2, 2> D;
+ D << 0.0, 0.0, 0.0, 0.0;
+ Eigen::Matrix<double, 2, 1> U_max;
+ U_max << 12.0, 12.0;
+ Eigen::Matrix<double, 2, 1> U_min;
+ U_min << -12.0, -12.0;
+ return StateFeedbackPlantCoefficients<2, 2, 2>(A, B, C, D, U_max, U_min);
+}
+
+StateFeedbackPlantCoefficients<2, 2, 2> MakeVelocityDrivetrainHighHighPlantCoefficients() {
+ Eigen::Matrix<double, 2, 2> A;
+ A << 0.955499400541, 0.00962691403749, 0.00962691403749, 0.955499400541;
+ Eigen::Matrix<double, 2, 2> B;
+ B << 0.0164714763931, -0.00356331126397, -0.00356331126397, 0.0164714763931;
+ Eigen::Matrix<double, 2, 2> C;
+ C << 1.0, 0.0, 0.0, 1.0;
+ Eigen::Matrix<double, 2, 2> D;
+ D << 0.0, 0.0, 0.0, 0.0;
+ Eigen::Matrix<double, 2, 1> U_max;
+ U_max << 12.0, 12.0;
+ Eigen::Matrix<double, 2, 1> U_min;
+ U_min << -12.0, -12.0;
+ return StateFeedbackPlantCoefficients<2, 2, 2>(A, B, C, D, U_max, U_min);
+}
+
+StateFeedbackController<2, 2, 2> MakeVelocityDrivetrainLowLowController() {
+ Eigen::Matrix<double, 2, 2> L;
+ L << 0.935499400541, 0.00962691403749, 0.00962691403749, 0.935499400541;
+ Eigen::Matrix<double, 2, 2> K;
+ K << 22.7750288573, 5.51143253556, 5.51143253556, 22.7750288573;
+ Eigen::Matrix<double, 2, 2> A_inv;
+ A_inv << 1.04667938127, -0.010545576923, -0.010545576923, 1.04667938127;
+ return StateFeedbackController<2, 2, 2>(L, K, A_inv, MakeVelocityDrivetrainLowLowPlantCoefficients());
+}
+
+StateFeedbackController<2, 2, 2> MakeVelocityDrivetrainLowHighController() {
+ Eigen::Matrix<double, 2, 2> L;
+ L << 0.935499400541, 0.00962691403749, 0.00962691403749, 0.935499400541;
+ Eigen::Matrix<double, 2, 2> K;
+ K << 22.7750288573, 5.51143253556, 5.51143253556, 22.7750288573;
+ Eigen::Matrix<double, 2, 2> A_inv;
+ A_inv << 1.04667938127, -0.010545576923, -0.010545576923, 1.04667938127;
+ return StateFeedbackController<2, 2, 2>(L, K, A_inv, MakeVelocityDrivetrainLowHighPlantCoefficients());
+}
+
+StateFeedbackController<2, 2, 2> MakeVelocityDrivetrainHighLowController() {
+ Eigen::Matrix<double, 2, 2> L;
+ L << 0.935499400541, 0.00962691403749, 0.00962691403749, 0.935499400541;
+ Eigen::Matrix<double, 2, 2> K;
+ K << 22.7750288573, 5.51143253556, 5.51143253556, 22.7750288573;
+ Eigen::Matrix<double, 2, 2> A_inv;
+ A_inv << 1.04667938127, -0.010545576923, -0.010545576923, 1.04667938127;
+ return StateFeedbackController<2, 2, 2>(L, K, A_inv, MakeVelocityDrivetrainHighLowPlantCoefficients());
+}
+
+StateFeedbackController<2, 2, 2> MakeVelocityDrivetrainHighHighController() {
+ Eigen::Matrix<double, 2, 2> L;
+ L << 0.935499400541, 0.00962691403749, 0.00962691403749, 0.935499400541;
+ Eigen::Matrix<double, 2, 2> K;
+ K << 22.7750288573, 5.51143253556, 5.51143253556, 22.7750288573;
+ Eigen::Matrix<double, 2, 2> A_inv;
+ A_inv << 1.04667938127, -0.010545576923, -0.010545576923, 1.04667938127;
+ return StateFeedbackController<2, 2, 2>(L, K, A_inv, MakeVelocityDrivetrainHighHighPlantCoefficients());
+}
+
+StateFeedbackPlant<2, 2, 2> MakeVelocityDrivetrainPlant() {
+ ::std::vector< ::std::unique_ptr<StateFeedbackPlantCoefficients<2, 2, 2>>> plants(4);
+ plants[0] = ::std::unique_ptr<StateFeedbackPlantCoefficients<2, 2, 2>>(new StateFeedbackPlantCoefficients<2, 2, 2>(MakeVelocityDrivetrainLowLowPlantCoefficients()));
+ plants[1] = ::std::unique_ptr<StateFeedbackPlantCoefficients<2, 2, 2>>(new StateFeedbackPlantCoefficients<2, 2, 2>(MakeVelocityDrivetrainLowHighPlantCoefficients()));
+ plants[2] = ::std::unique_ptr<StateFeedbackPlantCoefficients<2, 2, 2>>(new StateFeedbackPlantCoefficients<2, 2, 2>(MakeVelocityDrivetrainHighLowPlantCoefficients()));
+ plants[3] = ::std::unique_ptr<StateFeedbackPlantCoefficients<2, 2, 2>>(new StateFeedbackPlantCoefficients<2, 2, 2>(MakeVelocityDrivetrainHighHighPlantCoefficients()));
+ return StateFeedbackPlant<2, 2, 2>(&plants);
+}
+
+StateFeedbackLoop<2, 2, 2> MakeVelocityDrivetrainLoop() {
+ ::std::vector< ::std::unique_ptr<StateFeedbackController<2, 2, 2>>> controllers(4);
+ controllers[0] = ::std::unique_ptr<StateFeedbackController<2, 2, 2>>(new StateFeedbackController<2, 2, 2>(MakeVelocityDrivetrainLowLowController()));
+ controllers[1] = ::std::unique_ptr<StateFeedbackController<2, 2, 2>>(new StateFeedbackController<2, 2, 2>(MakeVelocityDrivetrainLowHighController()));
+ controllers[2] = ::std::unique_ptr<StateFeedbackController<2, 2, 2>>(new StateFeedbackController<2, 2, 2>(MakeVelocityDrivetrainHighLowController()));
+ controllers[3] = ::std::unique_ptr<StateFeedbackController<2, 2, 2>>(new StateFeedbackController<2, 2, 2>(MakeVelocityDrivetrainHighHighController()));
+ return StateFeedbackLoop<2, 2, 2>(&controllers);
+}
+
+} // namespace control_loops
+} // namespace bot3
diff --git a/bot3/control_loops/drivetrain/polydrivetrain_dog_motor_plant.h b/bot3/control_loops/drivetrain/polydrivetrain_dog_motor_plant.h
new file mode 100644
index 0000000..191d1aa
--- /dev/null
+++ b/bot3/control_loops/drivetrain/polydrivetrain_dog_motor_plant.h
@@ -0,0 +1,32 @@
+#ifndef BOT3_CONTROL_LOOPS_DRIVETRAIN_POLYDRIVETRAIN_DOG_MOTOR_PLANT_H_
+#define BOT3_CONTROL_LOOPS_DRIVETRAIN_POLYDRIVETRAIN_DOG_MOTOR_PLANT_H_
+
+#include "frc971/control_loops/state_feedback_loop.h"
+
+namespace bot3 {
+namespace control_loops {
+
+StateFeedbackPlantCoefficients<2, 2, 2> MakeVelocityDrivetrainLowLowPlantCoefficients();
+
+StateFeedbackController<2, 2, 2> MakeVelocityDrivetrainLowLowController();
+
+StateFeedbackPlantCoefficients<2, 2, 2> MakeVelocityDrivetrainLowHighPlantCoefficients();
+
+StateFeedbackController<2, 2, 2> MakeVelocityDrivetrainLowHighController();
+
+StateFeedbackPlantCoefficients<2, 2, 2> MakeVelocityDrivetrainHighLowPlantCoefficients();
+
+StateFeedbackController<2, 2, 2> MakeVelocityDrivetrainHighLowController();
+
+StateFeedbackPlantCoefficients<2, 2, 2> MakeVelocityDrivetrainHighHighPlantCoefficients();
+
+StateFeedbackController<2, 2, 2> MakeVelocityDrivetrainHighHighController();
+
+StateFeedbackPlant<2, 2, 2> MakeVelocityDrivetrainPlant();
+
+StateFeedbackLoop<2, 2, 2> MakeVelocityDrivetrainLoop();
+
+} // namespace control_loops
+} // namespace bot3
+
+#endif // BOT3_CONTROL_LOOPS_DRIVETRAIN_POLYDRIVETRAIN_DOG_MOTOR_PLANT_H_
diff --git a/bot3/control_loops/drivetrain/replay_drivetrain.cc b/bot3/control_loops/drivetrain/replay_drivetrain.cc
new file mode 100644
index 0000000..f2fb87c
--- /dev/null
+++ b/bot3/control_loops/drivetrain/replay_drivetrain.cc
@@ -0,0 +1,24 @@
+#include "aos/common/controls/replay_control_loop.h"
+#include "aos/linux_code/init.h"
+
+#include "bot3/control_loops/drivetrain/drivetrain.q.h"
+
+// Reads one or more log files and sends out all the queue messages (in the
+// correct order and at the correct time) to feed a "live" drivetrain process.
+
+int main(int argc, char **argv) {
+ if (argc <= 1) {
+ fprintf(stderr, "Need at least one file to replay!\n");
+ return EXIT_FAILURE;
+ }
+
+ ::aos::InitNRT();
+
+ ::aos::controls::ControlLoopReplayer<::bot3::control_loops::DrivetrainQueue>
+ replayer(&::bot3::control_loops::drivetrain_queue, "drivetrain");
+ for (int i = 1; i < argc; ++i) {
+ replayer.ProcessFile(argv[i]);
+ }
+
+ ::aos::Cleanup();
+}
diff --git a/bot3/control_loops/intake/intake.cc b/bot3/control_loops/intake/intake.cc
new file mode 100644
index 0000000..935730b
--- /dev/null
+++ b/bot3/control_loops/intake/intake.cc
@@ -0,0 +1,36 @@
+#include "bot3/control_loops/intake/intake.h"
+
+#include "bot3/control_loops/intake/intake.q.h"
+
+namespace bot3 {
+namespace control_loops {
+
+Intake::Intake(control_loops::IntakeQueue *intake)
+ : aos::controls::ControlLoop<control_loops::IntakeQueue>(intake) {}
+
+void Intake::RunIteration(
+ const control_loops::IntakeQueue::Goal *goal,
+ const control_loops::IntakeQueue::Position * /*position*/,
+ control_loops::IntakeQueue::Output *output,
+ control_loops::IntakeQueue::Status * /*status*/) {
+
+ if (output != nullptr) {
+ output->Zero();
+
+ const int16_t intake_movement = goal->movement;
+
+ if (intake_movement > 0) {
+ // Suck.
+ output->intake = kIntakeVoltageFullPower;
+ } else if (intake_movement < 0) {
+ // Spit.
+ output->intake = -kIntakeVoltageFullPower;
+ } else {
+ // Stationary.
+ output->intake = 0.0;
+ }
+ }
+}
+
+} // namespace control_loops
+} // namespace bot3
diff --git a/bot3/control_loops/intake/intake.gyp b/bot3/control_loops/intake/intake.gyp
new file mode 100644
index 0000000..41f1b7f
--- /dev/null
+++ b/bot3/control_loops/intake/intake.gyp
@@ -0,0 +1,60 @@
+{
+ 'targets': [
+ {
+ 'target_name': 'intake_queue',
+ 'type': 'static_library',
+ 'sources': ['intake.q'],
+ 'variables': {
+ 'header_path': 'bot3/control_loops/intake',
+ },
+ 'dependencies': [
+ '<(AOS)/common/controls/controls.gyp:control_loop_queues',
+ ],
+ 'export_dependent_settings': [
+ '<(AOS)/common/controls/controls.gyp:control_loop_queues',
+ ],
+ 'includes': ['../../../aos/build/queues.gypi'],
+ },
+ {
+ 'target_name': 'intake_lib',
+ 'type': 'static_library',
+ 'sources': [
+ 'intake.cc',
+ ],
+ 'dependencies': [
+ 'intake_queue',
+ '<(AOS)/common/controls/controls.gyp:control_loop',
+ ],
+ 'export_dependent_settings': [
+ 'intake_queue',
+ '<(AOS)/common/controls/controls.gyp:control_loop',
+ ],
+ },
+ {
+ 'target_name': 'intake_lib_test',
+ 'type': 'executable',
+ 'sources': [
+ 'intake_lib_test.cc',
+ ],
+ 'dependencies': [
+ '<(EXTERNALS):gtest',
+ 'intake_lib',
+ '<(DEPTH)/frc971/control_loops/control_loops.gyp:state_feedback_loop',
+ '<(AOS)/common/controls/controls.gyp:control_loop_test',
+ '<(AOS)/common/common.gyp:time',
+ '<(DEPTH)/frc971/control_loops/control_loops.gyp:team_number_test_environment',
+ ],
+ },
+ {
+ 'target_name': 'intake',
+ 'type': 'executable',
+ 'sources': [
+ 'intake_main.cc',
+ ],
+ 'dependencies': [
+ '<(AOS)/linux_code/linux_code.gyp:init',
+ 'intake_lib',
+ ],
+ },
+ ],
+}
diff --git a/bot3/control_loops/intake/intake.h b/bot3/control_loops/intake/intake.h
new file mode 100644
index 0000000..43cd9fc
--- /dev/null
+++ b/bot3/control_loops/intake/intake.h
@@ -0,0 +1,28 @@
+#ifndef BOT3_CONTROL_LOOPS_INTAKE_H_
+#define BOT3_CONTROL_LOOPS_INTAKE_H_
+
+#include "aos/common/controls/control_loop.h"
+
+#include "bot3/control_loops/intake/intake.q.h"
+
+namespace bot3 {
+namespace control_loops {
+
+constexpr double kIntakeVoltageFullPower = 12.0;
+
+class Intake : public aos::controls::ControlLoop<control_loops::IntakeQueue> {
+ public:
+ explicit Intake(
+ control_loops::IntakeQueue *intake_queue = &control_loops::intake_queue);
+
+ protected:
+ void RunIteration(const control_loops::IntakeQueue::Goal *goal,
+ const control_loops::IntakeQueue::Position *position,
+ control_loops::IntakeQueue::Output *output,
+ control_loops::IntakeQueue::Status *status) override;
+};
+
+} // namespace control_loops
+} // namespace bot3
+
+#endif // BOT3_CONTROL_LOOPS_INTAKE_H_
diff --git a/bot3/control_loops/intake/intake.q b/bot3/control_loops/intake/intake.q
new file mode 100644
index 0000000..3047f6b
--- /dev/null
+++ b/bot3/control_loops/intake/intake.q
@@ -0,0 +1,27 @@
+package bot3.control_loops;
+
+import "aos/common/controls/control_loops.q";
+
+queue_group IntakeQueue {
+ implements aos.control_loops.ControlLoop;
+
+ message Goal {
+ // Positive = suck, negative = spit, zero = stationary.
+ int16_t movement;
+ };
+
+ message Position {};
+
+ message Output {
+ double intake;
+ };
+
+ message Status {};
+
+ queue Goal goal;
+ queue Position position;
+ queue Output output;
+ queue Status status;
+};
+
+queue_group IntakeQueue intake_queue;
diff --git a/bot3/control_loops/intake/intake_lib_test.cc b/bot3/control_loops/intake/intake_lib_test.cc
new file mode 100644
index 0000000..6fe6672
--- /dev/null
+++ b/bot3/control_loops/intake/intake_lib_test.cc
@@ -0,0 +1,123 @@
+#include "bot3/control_loops/intake/intake.h"
+
+#include <math.h>
+#include <unistd.h>
+
+#include "gtest/gtest.h"
+#include "aos/common/queue.h"
+#include "aos/common/commonmath.h"
+#include "aos/common/controls/control_loop_test.h"
+#include "bot3/control_loops/intake/intake.q.h"
+
+using ::aos::time::Time;
+
+namespace bot3 {
+namespace control_loops {
+namespace testing {
+
+// Class which simulates the elevator and sends out queue messages with the
+// position.
+class IntakeSimulation {
+ public:
+ // Constructs a simulation.
+ IntakeSimulation()
+ : queue_(".bot3.control_loops.intake_queue", 0x627ceeeb,
+ ".bot3.control_loops.intake_queue.goal",
+ ".bot3.control_loops.intake_queue.position",
+ ".bot3.control_loops.intake_queue.output",
+ ".bot3.control_loops.intake_queue.status") {
+ }
+
+ // Simulates for a single timestep.
+ void Simulate() {
+ EXPECT_TRUE(queue_.output.FetchLatest());
+ }
+
+ private:
+ IntakeQueue queue_;
+};
+
+class IntakeTest : public ::aos::testing::ControlLoopTest {
+ protected:
+ IntakeTest()
+ : queue_(".bot3.control_loops.intake_queue", 0x627ceeeb,
+ ".bot3.control_loops.intake_queue.goal",
+ ".bot3.control_loops.intake_queue.position",
+ ".bot3.control_loops.intake_queue.output",
+ ".bot3.control_loops.intake_queue.status"),
+ intake_(&queue_),
+ plant_() {
+ set_team_id(971);
+ }
+
+ // Runs one iteration of the whole simulation.
+ void RunIteration(bool enabled = true) {
+ SendMessages(enabled);
+
+ queue_.position.MakeMessage().Send();
+
+ intake_.Iterate();
+
+ TickTime();
+ }
+
+ // Runs iterations until the specified amount of simulated time has elapsed.
+ void RunForTime(const Time &run_for, bool enabled = true) {
+ const auto start_time = Time::Now();
+ while (Time::Now() < start_time + run_for) {
+ RunIteration(enabled);
+ }
+ }
+
+ // Create a new instance of the test queue so that it invalidates the queue
+ // that it points to. Otherwise, we will have a pointed to shared memory that
+ // is no longer valid.
+ IntakeQueue queue_;
+
+ // Create a control loop.
+ Intake intake_;
+ IntakeSimulation plant_;
+};
+
+// Tests that the loop does nothing when the goal is zero.
+TEST_F(IntakeTest, DoesNothing) {
+ ASSERT_TRUE(queue_.goal.MakeWithBuilder()
+ .movement(0)
+ .Send());
+
+ // Run for a bit.
+ RunForTime(1.0);
+
+ ASSERT_TRUE(queue_.output.FetchLatest());
+ EXPECT_EQ(queue_.output->intake, 0.0);
+}
+
+// Tests that the intake sucks with a positive goal.
+TEST_F(IntakeTest, SuckingGoal) {
+ ASSERT_TRUE(queue_.goal.MakeWithBuilder()
+ .movement(1)
+ .Send());
+
+ // Run for a bit.
+ RunForTime(1.0);
+
+ ASSERT_TRUE(queue_.output.FetchLatest());
+ EXPECT_GT(queue_.output->intake, 0.0);
+}
+
+// Tests that the intake spits with a negative goal.
+TEST_F(IntakeTest, SpittingGoal) {
+ ASSERT_TRUE(queue_.goal.MakeWithBuilder()
+ .movement(-1)
+ .Send());
+
+ // Run for a bit.
+ RunForTime(1.0);
+
+ ASSERT_TRUE(queue_.output.FetchLatest());
+ EXPECT_LT(queue_.output->intake, 0.0);
+}
+
+} // namespace testing
+} // namespace control_loops
+} // namespace bot3
diff --git a/bot3/control_loops/intake/intake_main.cc b/bot3/control_loops/intake/intake_main.cc
new file mode 100644
index 0000000..ccc5fab
--- /dev/null
+++ b/bot3/control_loops/intake/intake_main.cc
@@ -0,0 +1,11 @@
+#include "bot3/control_loops/intake/intake.h"
+
+#include "aos/linux_code/init.h"
+
+int main() {
+ ::aos::Init();
+ ::bot3::control_loops::Intake intake;
+ intake.Run();
+ ::aos::Cleanup();
+ return 0;
+}
diff --git a/bot3/control_loops/python/control_loop.py b/bot3/control_loops/python/control_loop.py
new file mode 100644
index 0000000..881880f
--- /dev/null
+++ b/bot3/control_loops/python/control_loop.py
@@ -0,0 +1,338 @@
+import controls
+import numpy
+
+class Constant(object):
+ def __init__ (self, name, formatt, value):
+ self.name = name
+ self.formatt = formatt
+ self.value = value
+ self.formatToType = {}
+ self.formatToType['%f'] = "double";
+ self.formatToType['%d'] = "int";
+ def __str__ (self):
+ return str("\nstatic constexpr %s %s = "+ self.formatt +";\n") % \
+ (self.formatToType[self.formatt], self.name, self.value)
+
+
+class ControlLoopWriter(object):
+ def __init__(self, gain_schedule_name, loops, namespaces=None, write_constants=False):
+ """Constructs a control loop writer.
+
+ Args:
+ gain_schedule_name: string, Name of the overall controller.
+ loops: array[ControlLoop], a list of control loops to gain schedule
+ in order.
+ namespaces: array[string], a list of names of namespaces to nest in
+ order. If None, the default will be used.
+ """
+ self._gain_schedule_name = gain_schedule_name
+ self._loops = loops
+ if namespaces:
+ self._namespaces = namespaces
+ else:
+ self._namespaces = ['frc971', 'control_loops']
+
+ self._namespace_start = '\n'.join(
+ ['namespace %s {' % name for name in self._namespaces])
+
+ self._namespace_end = '\n'.join(
+ ['} // namespace %s' % name for name in reversed(self._namespaces)])
+
+ self._constant_list = []
+
+ def AddConstant(self, constant):
+ """Adds a constant to write.
+
+ Args:
+ constant: Constant, the constant to add to the header.
+ """
+ self._constant_list.append(constant)
+
+ def _TopDirectory(self):
+ return self._namespaces[0]
+
+ def _HeaderGuard(self, header_file):
+ return (self._TopDirectory().upper() + '_CONTROL_LOOPS_' +
+ header_file.upper().replace('.', '_').replace('/', '_') +
+ '_')
+
+ def Write(self, header_file, cc_file):
+ """Writes the loops to the specified files."""
+ self.WriteHeader(header_file)
+ self.WriteCC(header_file, cc_file)
+
+ def _GenericType(self, typename):
+ """Returns a loop template using typename for the type."""
+ num_states = self._loops[0].A.shape[0]
+ num_inputs = self._loops[0].B.shape[1]
+ num_outputs = self._loops[0].C.shape[0]
+ return '%s<%d, %d, %d>' % (
+ typename, num_states, num_inputs, num_outputs)
+
+ def _ControllerType(self):
+ """Returns a template name for StateFeedbackController."""
+ return self._GenericType('StateFeedbackController')
+
+ def _LoopType(self):
+ """Returns a template name for StateFeedbackLoop."""
+ return self._GenericType('StateFeedbackLoop')
+
+ def _PlantType(self):
+ """Returns a template name for StateFeedbackPlant."""
+ return self._GenericType('StateFeedbackPlant')
+
+ def _CoeffType(self):
+ """Returns a template name for StateFeedbackPlantCoefficients."""
+ return self._GenericType('StateFeedbackPlantCoefficients')
+
+ def WriteHeader(self, header_file, double_appendage=False, MoI_ratio=0.0):
+ """Writes the header file to the file named header_file.
+ Set double_appendage to true in order to include a ratio of
+ moments of inertia constant. Currently, only used for 2014 claw."""
+ with open(header_file, 'w') as fd:
+ header_guard = self._HeaderGuard(header_file)
+ fd.write('#ifndef %s\n'
+ '#define %s\n\n' % (header_guard, header_guard))
+ fd.write('#include \"frc971/control_loops/state_feedback_loop.h\"\n')
+ fd.write('\n')
+
+ fd.write(self._namespace_start)
+
+ for const in self._constant_list:
+ fd.write(str(const))
+
+ fd.write('\n\n')
+ for loop in self._loops:
+ fd.write(loop.DumpPlantHeader())
+ fd.write('\n')
+ fd.write(loop.DumpControllerHeader())
+ fd.write('\n')
+
+ fd.write('%s Make%sPlant();\n\n' %
+ (self._PlantType(), self._gain_schedule_name))
+
+ fd.write('%s Make%sLoop();\n\n' %
+ (self._LoopType(), self._gain_schedule_name))
+
+ fd.write(self._namespace_end)
+ fd.write('\n\n')
+ fd.write("#endif // %s\n" % header_guard)
+
+ def WriteCC(self, header_file_name, cc_file):
+ """Writes the cc file to the file named cc_file."""
+ with open(cc_file, 'w') as fd:
+ fd.write('#include \"%s/control_loops/%s\"\n' %
+ (self._TopDirectory(), header_file_name))
+ fd.write('\n')
+ fd.write('#include <vector>\n')
+ fd.write('\n')
+ fd.write('#include \"frc971/control_loops/state_feedback_loop.h\"\n')
+ fd.write('\n')
+ fd.write(self._namespace_start)
+ fd.write('\n\n')
+ for loop in self._loops:
+ fd.write(loop.DumpPlant())
+ fd.write('\n')
+
+ for loop in self._loops:
+ fd.write(loop.DumpController())
+ fd.write('\n')
+
+ fd.write('%s Make%sPlant() {\n' %
+ (self._PlantType(), self._gain_schedule_name))
+ fd.write(' ::std::vector< ::std::unique_ptr<%s>> plants(%d);\n' % (
+ self._CoeffType(), len(self._loops)))
+ for index, loop in enumerate(self._loops):
+ fd.write(' plants[%d] = ::std::unique_ptr<%s>(new %s(%s));\n' %
+ (index, self._CoeffType(), self._CoeffType(),
+ loop.PlantFunction()))
+ fd.write(' return %s(&plants);\n' % self._PlantType())
+ fd.write('}\n\n')
+
+ fd.write('%s Make%sLoop() {\n' %
+ (self._LoopType(), self._gain_schedule_name))
+ fd.write(' ::std::vector< ::std::unique_ptr<%s>> controllers(%d);\n' % (
+ self._ControllerType(), len(self._loops)))
+ for index, loop in enumerate(self._loops):
+ fd.write(' controllers[%d] = ::std::unique_ptr<%s>(new %s(%s));\n' %
+ (index, self._ControllerType(), self._ControllerType(),
+ loop.ControllerFunction()))
+ fd.write(' return %s(&controllers);\n' % self._LoopType())
+ fd.write('}\n\n')
+
+ fd.write(self._namespace_end)
+ fd.write('\n')
+
+
+class ControlLoop(object):
+ def __init__(self, name):
+ """Constructs a control loop object.
+
+ Args:
+ name: string, The name of the loop to use when writing the C++ files.
+ """
+ self._name = name
+
+ def ContinuousToDiscrete(self, A_continuous, B_continuous, dt):
+ """Calculates the discrete time values for A and B.
+
+ Args:
+ A_continuous: numpy.matrix, The continuous time A matrix
+ B_continuous: numpy.matrix, The continuous time B matrix
+ dt: float, The time step of the control loop
+
+ Returns:
+ (A, B), numpy.matrix, the control matricies.
+ """
+ return controls.c2d(A_continuous, B_continuous, dt)
+
+ def InitializeState(self):
+ """Sets X, Y, and X_hat to zero defaults."""
+ self.X = numpy.zeros((self.A.shape[0], 1))
+ self.Y = self.C * self.X
+ self.X_hat = numpy.zeros((self.A.shape[0], 1))
+
+ def PlaceControllerPoles(self, poles):
+ """Places the controller poles.
+
+ Args:
+ poles: array, An array of poles. Must be complex conjegates if they have
+ any imaginary portions.
+ """
+ self.K = controls.dplace(self.A, self.B, poles)
+
+ def PlaceObserverPoles(self, poles):
+ """Places the observer poles.
+
+ Args:
+ poles: array, An array of poles. Must be complex conjegates if they have
+ any imaginary portions.
+ """
+ self.L = controls.dplace(self.A.T, self.C.T, poles).T
+
+ def Update(self, U):
+ """Simulates one time step with the provided U."""
+ #U = numpy.clip(U, self.U_min, self.U_max)
+ self.X = self.A * self.X + self.B * U
+ self.Y = self.C * self.X + self.D * U
+
+ def PredictObserver(self, U):
+ """Runs the predict step of the observer update."""
+ self.X_hat = (self.A * self.X_hat + self.B * U)
+
+ def CorrectObserver(self, U):
+ """Runs the correct step of the observer update."""
+ self.X_hat += numpy.linalg.inv(self.A) * self.L * (
+ self.Y - self.C * self.X_hat - self.D * U)
+
+ def UpdateObserver(self, U):
+ """Updates the observer given the provided U."""
+ self.X_hat = (self.A * self.X_hat + self.B * U +
+ self.L * (self.Y - self.C * self.X_hat - self.D * U))
+
+ def _DumpMatrix(self, matrix_name, matrix):
+ """Dumps the provided matrix into a variable called matrix_name.
+
+ Args:
+ matrix_name: string, The variable name to save the matrix to.
+ matrix: The matrix to dump.
+
+ Returns:
+ string, The C++ commands required to populate a variable named matrix_name
+ with the contents of matrix.
+ """
+ ans = [' Eigen::Matrix<double, %d, %d> %s;\n' % (
+ matrix.shape[0], matrix.shape[1], matrix_name)]
+ first = True
+ for x in xrange(matrix.shape[0]):
+ for y in xrange(matrix.shape[1]):
+ element = matrix[x, y]
+ if first:
+ ans.append(' %s << ' % matrix_name)
+ first = False
+ else:
+ ans.append(', ')
+ ans.append(str(element))
+
+ ans.append(';\n')
+ return ''.join(ans)
+
+ def DumpPlantHeader(self):
+ """Writes out a c++ header declaration which will create a Plant object.
+
+ Returns:
+ string, The header declaration for the function.
+ """
+ num_states = self.A.shape[0]
+ num_inputs = self.B.shape[1]
+ num_outputs = self.C.shape[0]
+ return 'StateFeedbackPlantCoefficients<%d, %d, %d> Make%sPlantCoefficients();\n' % (
+ num_states, num_inputs, num_outputs, self._name)
+
+ def DumpPlant(self):
+ """Writes out a c++ function which will create a PlantCoefficients object.
+
+ Returns:
+ string, The function which will create the object.
+ """
+ num_states = self.A.shape[0]
+ num_inputs = self.B.shape[1]
+ num_outputs = self.C.shape[0]
+ ans = ['StateFeedbackPlantCoefficients<%d, %d, %d>'
+ ' Make%sPlantCoefficients() {\n' % (
+ num_states, num_inputs, num_outputs, self._name)]
+
+ ans.append(self._DumpMatrix('A', self.A))
+ ans.append(self._DumpMatrix('B', self.B))
+ ans.append(self._DumpMatrix('C', self.C))
+ ans.append(self._DumpMatrix('D', self.D))
+ ans.append(self._DumpMatrix('U_max', self.U_max))
+ ans.append(self._DumpMatrix('U_min', self.U_min))
+
+ ans.append(' return StateFeedbackPlantCoefficients<%d, %d, %d>'
+ '(A, B, C, D, U_max, U_min);\n' % (num_states, num_inputs,
+ num_outputs))
+ ans.append('}\n')
+ return ''.join(ans)
+
+ def PlantFunction(self):
+ """Returns the name of the plant coefficient function."""
+ return 'Make%sPlantCoefficients()' % self._name
+
+ def ControllerFunction(self):
+ """Returns the name of the controller function."""
+ return 'Make%sController()' % self._name
+
+ def DumpControllerHeader(self):
+ """Writes out a c++ header declaration which will create a Controller object.
+
+ Returns:
+ string, The header declaration for the function.
+ """
+ num_states = self.A.shape[0]
+ num_inputs = self.B.shape[1]
+ num_outputs = self.C.shape[0]
+ return 'StateFeedbackController<%d, %d, %d> %s;\n' % (
+ num_states, num_inputs, num_outputs, self.ControllerFunction())
+
+ def DumpController(self):
+ """Returns a c++ function which will create a Controller object.
+
+ Returns:
+ string, The function which will create the object.
+ """
+ num_states = self.A.shape[0]
+ num_inputs = self.B.shape[1]
+ num_outputs = self.C.shape[0]
+ ans = ['StateFeedbackController<%d, %d, %d> %s {\n' % (
+ num_states, num_inputs, num_outputs, self.ControllerFunction())]
+
+ ans.append(self._DumpMatrix('L', self.L))
+ ans.append(self._DumpMatrix('K', self.K))
+ ans.append(self._DumpMatrix('A_inv', numpy.linalg.inv(self.A)))
+
+ ans.append(' return StateFeedbackController<%d, %d, %d>'
+ '(L, K, A_inv, Make%sPlantCoefficients());\n' % (
+ num_states, num_inputs, num_outputs, self._name))
+ ans.append('}\n')
+ return ''.join(ans)
diff --git a/bot3/control_loops/python/controls.py b/bot3/control_loops/python/controls.py
new file mode 100644
index 0000000..b66bd56
--- /dev/null
+++ b/bot3/control_loops/python/controls.py
@@ -0,0 +1,153 @@
+#!/usr/bin/python
+
+"""
+Control loop pole placement library.
+
+This library will grow to support many different pole placement methods.
+Currently it only supports direct pole placement.
+"""
+
+__author__ = 'Austin Schuh (austin.linux@gmail.com)'
+
+import numpy
+import slycot
+import scipy.signal.cont2discrete
+
+class Error (Exception):
+ """Base class for all control loop exceptions."""
+
+
+class PolePlacementError(Error):
+ """Exception raised when pole placement fails."""
+
+
+# TODO(aschuh): dplace should take a control system object.
+# There should also exist a function to manipulate laplace expressions, and
+# something to plot bode plots and all that.
+def dplace(A, B, poles, alpha=1e-6):
+ """Set the poles of (A - BF) to poles.
+
+ Args:
+ A: numpy.matrix(n x n), The A matrix.
+ B: numpy.matrix(n x m), The B matrix.
+ poles: array(imaginary numbers), The poles to use. Complex conjugates poles
+ must be in pairs.
+
+ Raises:
+ ValueError: Arguments were the wrong shape or there were too many poles.
+ PolePlacementError: Pole placement failed.
+
+ Returns:
+ numpy.matrix(m x n), K
+ """
+ # See http://www.icm.tu-bs.de/NICONET/doc/SB01BD.html for a description of the
+ # fortran code that this is cleaning up the interface to.
+ n = A.shape[0]
+ if A.shape[1] != n:
+ raise ValueError("A must be square")
+ if B.shape[0] != n:
+ raise ValueError("B must have the same number of states as A.")
+ m = B.shape[1]
+
+ num_poles = len(poles)
+ if num_poles > n:
+ raise ValueError("Trying to place more poles than states.")
+
+ out = slycot.sb01bd(n=n,
+ m=m,
+ np=num_poles,
+ alpha=alpha,
+ A=A,
+ B=B,
+ w=numpy.array(poles),
+ dico='D')
+
+ A_z = numpy.matrix(out[0])
+ num_too_small_eigenvalues = out[2]
+ num_assigned_eigenvalues = out[3]
+ num_uncontrollable_eigenvalues = out[4]
+ K = numpy.matrix(-out[5])
+ Z = numpy.matrix(out[6])
+
+ if num_too_small_eigenvalues != 0:
+ raise PolePlacementError("Number of eigenvalues that are too small "
+ "and are therefore unmodified is %d." %
+ num_too_small_eigenvalues)
+ if num_assigned_eigenvalues != num_poles:
+ raise PolePlacementError("Did not place all the eigenvalues that were "
+ "requested. Only placed %d eigenvalues." %
+ num_assigned_eigenvalues)
+ if num_uncontrollable_eigenvalues != 0:
+ raise PolePlacementError("Found %d uncontrollable eigenvlaues." %
+ num_uncontrollable_eigenvalues)
+
+ return K
+
+
+def c2d(A, B, dt):
+ """Converts from continuous time state space representation to discrete time.
+ Returns (A, B). C and D are unchanged."""
+
+ ans_a, ans_b, _, _, _ = scipy.signal.cont2discrete((A, B, None, None), dt)
+ return numpy.matrix(ans_a), numpy.matrix(ans_b)
+
+def ctrb(A, B):
+ """Returns the controlability matrix.
+
+ This matrix must have full rank for all the states to be controlable.
+ """
+ n = A.shape[0]
+ output = B
+ intermediate = B
+ for i in xrange(0, n):
+ intermediate = A * intermediate
+ output = numpy.concatenate((output, intermediate), axis=1)
+
+ return output
+
+def dlqr(A, B, Q, R):
+ """Solves for the optimal lqr controller.
+
+ x(n+1) = A * x(n) + B * u(n)
+ J = sum(0, inf, x.T * Q * x + u.T * R * u)
+ """
+
+ # P = (A.T * P * A) - (A.T * P * B * numpy.linalg.inv(R + B.T * P *B) * (A.T * P.T * B).T + Q
+
+ P, rcond, w, S, T = slycot.sb02od(
+ n=A.shape[0], m=B.shape[1], A=A, B=B, Q=Q, R=R, dico='D')
+
+ F = numpy.linalg.inv(R + B.T * P *B) * B.T * P * A
+ return F
+
+def kalman(A, B, C, Q, R):
+ """Solves for the steady state kalman gain and covariance matricies.
+
+ Args:
+ A, B, C: SS matricies.
+ Q: The model uncertantity
+ R: The measurement uncertainty
+
+ Returns:
+ KalmanGain, Covariance.
+ """
+ P = Q
+
+ I = numpy.matrix(numpy.eye(P.shape[0]))
+ At = A.T
+ Ct = C.T
+ i = 0
+
+ while True:
+ last_P = P
+ P_prior = A * P * At + Q
+ S = C * P_prior * Ct + R
+ K = P_prior * Ct * numpy.linalg.inv(S)
+ P = (I - K * C) * P_prior
+
+ diff = P - last_P
+ i += 1
+ if numpy.linalg.norm(diff) / numpy.linalg.norm(P) < 1e-9:
+ break
+
+ return K, P
diff --git a/bot3/control_loops/python/drivetrain.py b/bot3/control_loops/python/drivetrain.py
new file mode 100644
index 0000000..8ed62d6
--- /dev/null
+++ b/bot3/control_loops/python/drivetrain.py
@@ -0,0 +1,242 @@
+#!/usr/bin/python
+
+import control_loop
+import controls
+import numpy
+import sys
+from matplotlib import pylab
+
+
+class CIM(control_loop.ControlLoop):
+ def __init__(self):
+ super(CIM, self).__init__("CIM")
+ # Stall Torque in N m
+ self.stall_torque = 2.42
+ # Stall Current in Amps
+ self.stall_current = 133
+ # Free Speed in RPM
+ self.free_speed = 4650.0
+ # Free Current in Amps
+ self.free_current = 2.7
+ # Moment of inertia of the CIM in kg m^2
+ self.J = 0.0001
+ # Resistance of the motor, divided by 2 to account for the 2 motors
+ self.R = 12.0 / self.stall_current
+ # Motor velocity constant
+ self.Kv = ((self.free_speed / 60.0 * 2.0 * numpy.pi) /
+ (12.0 - self.R * self.free_current))
+ # Torque constant
+ self.Kt = self.stall_torque / self.stall_current
+ # Control loop time step
+ self.dt = 0.005
+
+ # State feedback matrices
+ self.A_continuous = numpy.matrix(
+ [[-self.Kt / self.Kv / (self.J * self.R)]])
+ self.B_continuous = numpy.matrix(
+ [[self.Kt / (self.J * self.R)]])
+ self.C = numpy.matrix([[1]])
+ self.D = numpy.matrix([[0]])
+
+ self.A, self.B = self.ContinuousToDiscrete(self.A_continuous,
+ self.B_continuous, self.dt)
+
+ self.PlaceControllerPoles([0.01])
+ self.PlaceObserverPoles([0.01])
+
+ self.U_max = numpy.matrix([[12.0]])
+ self.U_min = numpy.matrix([[-12.0]])
+
+ self.InitializeState()
+
+
+class Drivetrain(control_loop.ControlLoop):
+ def __init__(self, name="Drivetrain", left_low=True, right_low=True):
+ super(Drivetrain, self).__init__(name)
+ # Stall Torque in N m
+ self.stall_torque = 2.42
+ # Stall Current in Amps
+ self.stall_current = 133.0
+ # Free Speed in RPM. Used number from last year.
+ self.free_speed = 4650.0
+ # Free Current in Amps
+ self.free_current = 2.7
+ # Moment of inertia of the drivetrain in kg m^2
+ # Just borrowed from last year.
+ self.J = 10
+ # Mass of the robot, in kg.
+ self.m = 68
+ # Radius of the robot, in meters (from last year).
+ self.rb = 0.9603 / 2.0
+ # Radius of the wheels, in meters.
+ self.r = .0515938
+ # Resistance of the motor, divided by the number of motors.
+ self.R = 12.0 / self.stall_current / 2
+ # Motor velocity constant
+ self.Kv = ((self.free_speed / 60.0 * 2.0 * numpy.pi) /
+ (12.0 - self.R * self.free_current))
+ # Torque constant
+ self.Kt = self.stall_torque / self.stall_current
+ # Gear ratios
+ self.G_const = 28.0 / 50.0 * 20.0 / 64.0
+
+ self.G_low = self.G_const
+ self.G_high = self.G_const
+
+ if left_low:
+ self.Gl = self.G_low
+ else:
+ self.Gl = self.G_high
+ if right_low:
+ self.Gr = self.G_low
+ else:
+ self.Gr = self.G_high
+
+ # Control loop time step
+ self.dt = 0.005
+
+ # These describe the way that a given side of a robot will be influenced
+ # by the other side. Units of 1 / kg.
+ self.msp = 1.0 / self.m + self.rb * self.rb / self.J
+ self.msn = 1.0 / self.m - self.rb * self.rb / self.J
+ # The calculations which we will need for A and B.
+ self.tcl = -self.Kt / self.Kv / (self.Gl * self.Gl * self.R * self.r * self.r)
+ self.tcr = -self.Kt / self.Kv / (self.Gr * self.Gr * self.R * self.r * self.r)
+ self.mpl = self.Kt / (self.Gl * self.R * self.r)
+ self.mpr = self.Kt / (self.Gr * self.R * self.r)
+
+ # State feedback matrices
+ # X will be of the format
+ # [[positionl], [velocityl], [positionr], velocityr]]
+ self.A_continuous = numpy.matrix(
+ [[0, 1, 0, 0],
+ [0, self.msp * self.tcl, 0, self.msn * self.tcr],
+ [0, 0, 0, 1],
+ [0, self.msn * self.tcl, 0, self.msp * self.tcr]])
+ self.B_continuous = numpy.matrix(
+ [[0, 0],
+ [self.msp * self.mpl, self.msn * self.mpr],
+ [0, 0],
+ [self.msn * self.mpl, self.msp * self.mpr]])
+ self.C = numpy.matrix([[1, 0, 0, 0],
+ [0, 0, 1, 0]])
+ self.D = numpy.matrix([[0, 0],
+ [0, 0]])
+
+ #print "THE NUMBER I WANT" + str(numpy.linalg.inv(self.A_continuous) * -self.B_continuous * numpy.matrix([[12.0], [12.0]]))
+ self.A, self.B = self.ContinuousToDiscrete(
+ self.A_continuous, self.B_continuous, self.dt)
+
+ # Poles from last year.
+ self.hp = 0.65
+ self.lp = 0.83
+ self.PlaceControllerPoles([self.hp, self.lp, self.hp, self.lp])
+ print self.K
+ q_pos = 0.07
+ q_vel = 1.0
+ self.Q = numpy.matrix([[(1.0 / (q_pos ** 2.0)), 0.0, 0.0, 0.0],
+ [0.0, (1.0 / (q_vel ** 2.0)), 0.0, 0.0],
+ [0.0, 0.0, (1.0 / (q_pos ** 2.0)), 0.0],
+ [0.0, 0.0, 0.0, (1.0 / (q_vel ** 2.0))]])
+
+ self.R = numpy.matrix([[(1.0 / (12.0 ** 2.0)), 0.0],
+ [0.0, (1.0 / (12.0 ** 2.0))]])
+ self.K = controls.dlqr(self.A, self.B, self.Q, self.R)
+ print self.A
+ print self.B
+ print self.K
+ print numpy.linalg.eig(self.A - self.B * self.K)[0]
+
+ self.hlp = 0.3
+ self.llp = 0.4
+ self.PlaceObserverPoles([self.hlp, self.hlp, self.llp, self.llp])
+
+ self.U_max = numpy.matrix([[12.0], [12.0]])
+ self.U_min = numpy.matrix([[-12.0], [-12.0]])
+ self.InitializeState()
+
+def main(argv):
+ # Simulate the response of the system to a step input.
+ drivetrain = Drivetrain()
+ simulated_left = []
+ simulated_right = []
+ for _ in xrange(100):
+ drivetrain.Update(numpy.matrix([[12.0], [12.0]]))
+ simulated_left.append(drivetrain.X[0, 0])
+ simulated_right.append(drivetrain.X[2, 0])
+
+ #pylab.plot(range(100), simulated_left)
+ #pylab.plot(range(100), simulated_right)
+ #pylab.show()
+
+ # Simulate forwards motion.
+ drivetrain = Drivetrain()
+ close_loop_left = []
+ close_loop_right = []
+ R = numpy.matrix([[1.0], [0.0], [1.0], [0.0]])
+ for _ in xrange(100):
+ U = numpy.clip(drivetrain.K * (R - drivetrain.X_hat),
+ drivetrain.U_min, drivetrain.U_max)
+ drivetrain.UpdateObserver(U)
+ drivetrain.Update(U)
+ close_loop_left.append(drivetrain.X[0, 0])
+ close_loop_right.append(drivetrain.X[2, 0])
+
+ #pylab.plot(range(100), close_loop_left)
+ #pylab.plot(range(100), close_loop_right)
+ #pylab.show()
+
+ # Try turning in place
+ drivetrain = Drivetrain()
+ close_loop_left = []
+ close_loop_right = []
+ R = numpy.matrix([[-1.0], [0.0], [1.0], [0.0]])
+ for _ in xrange(100):
+ U = numpy.clip(drivetrain.K * (R - drivetrain.X_hat),
+ drivetrain.U_min, drivetrain.U_max)
+ drivetrain.UpdateObserver(U)
+ drivetrain.Update(U)
+ close_loop_left.append(drivetrain.X[0, 0])
+ close_loop_right.append(drivetrain.X[2, 0])
+
+ #pylab.plot(range(100), close_loop_left)
+ #pylab.plot(range(100), close_loop_right)
+ #pylab.show()
+
+ # Try turning just one side.
+ drivetrain = Drivetrain()
+ close_loop_left = []
+ close_loop_right = []
+ R = numpy.matrix([[0.0], [0.0], [1.0], [0.0]])
+ for _ in xrange(100):
+ U = numpy.clip(drivetrain.K * (R - drivetrain.X_hat),
+ drivetrain.U_min, drivetrain.U_max)
+ drivetrain.UpdateObserver(U)
+ drivetrain.Update(U)
+ close_loop_left.append(drivetrain.X[0, 0])
+ close_loop_right.append(drivetrain.X[2, 0])
+
+ #pylab.plot(range(100), close_loop_left)
+ #pylab.plot(range(100), close_loop_right)
+ #pylab.show()
+
+ # Write the generated constants out to a file.
+ print "Output one"
+ drivetrain_low_low = Drivetrain(name="DrivetrainLowLow", left_low=True, right_low=True)
+ drivetrain_low_high = Drivetrain(name="DrivetrainLowHigh", left_low=True, right_low=False)
+ drivetrain_high_low = Drivetrain(name="DrivetrainHighLow", left_low=False, right_low=True)
+ drivetrain_high_high = Drivetrain(name="DrivetrainHighHigh", left_low=False, right_low=False)
+
+ if len(argv) != 5:
+ print "Expected .h file name and .cc file name"
+ else:
+ dog_loop_writer = control_loop.ControlLoopWriter(
+ "Drivetrain", [drivetrain_low_low, drivetrain_low_high,
+ drivetrain_high_low, drivetrain_high_high])
+ if argv[1][-3:] == '.cc':
+ dog_loop_writer.Write(argv[2], argv[1])
+ else:
+ dog_loop_writer.Write(argv[1], argv[2])
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv))
diff --git a/bot3/control_loops/python/elevator.py b/bot3/control_loops/python/elevator.py
new file mode 100644
index 0000000..2d72ff2
--- /dev/null
+++ b/bot3/control_loops/python/elevator.py
@@ -0,0 +1,247 @@
+#!/usr/bin/python
+
+import control_loop
+import controls
+import polytope
+import polydrivetrain
+import numpy
+import sys
+import matplotlib
+from matplotlib import pylab
+
+class Elevator(control_loop.ControlLoop):
+ def __init__(self, name="Elevator", mass=None):
+ super(Elevator, self).__init__(name)
+ # Stall Torque in N m
+ self.stall_torque = 2.42
+ # Stall Current in Amps
+ self.stall_current = 133
+ # Free Speed in RPM
+ self.free_speed = 4650.0
+ # Free Current in Amps
+ self.free_current = 2.7
+ # Mass of the elevator
+ # TODO(comran): Get actual value.
+ self.mass = 13.0
+ # Resistance of the motor
+ self.R = 12.0 / self.stall_current
+ # Motor velocity constant
+ self.Kv = ((self.free_speed / 60.0 * 2.0 * numpy.pi) /
+ (12.0 - self.R * self.free_current))
+ # Torque constant
+ self.Kt = self.stall_torque / self.stall_current
+ # Gear ratio
+ # TODO(comran): Get actual value.
+ self.G = (56.0 / 12.0) * (84.0 / 14.0)
+ # Pulley diameter
+ # TODO(comran): Get actual value.
+ self.r = 32 * 0.005 / numpy.pi / 2.0
+ # Control loop time step
+ self.dt = 0.005
+
+ # Elevator spring constant (N/m)
+ # TODO(comran): Get actual value.
+ self.spring = 800.0
+
+ # State is [average position, average velocity]
+ # Input is [V]
+
+ # TODO(comran): Change everything below.
+
+ C1 = self.spring / (self.mass * 0.5)
+ C2 = self.Kt * self.G / (self.mass * 0.5 * self.r * self.R)
+ C3 = self.G * self.G * self.Kt / (
+ self.R * self.r * self.r * self.mass * 0.5 * self.Kv)
+
+ self.A_continuous = numpy.matrix(
+ [[0, 1, 0, 0],
+ [0, -C3, 0, 0],
+ [0, 0, 0, 1],
+ [0, 0, -C1 * 2.0, -C3]])
+
+ print "Full speed is", C2 / C3 * 12.0
+
+ # Start with the unmodified input
+ self.B_continuous = numpy.matrix(
+ [[0, 0],
+ [C2 / 2.0, C2 / 2.0],
+ [0, 0],
+ [C2 / 2.0, -C2 / 2.0]])
+
+ self.C = numpy.matrix([[1, 0, 1, 0],
+ [1, 0, -1, 0]])
+ self.D = numpy.matrix([[0, 0],
+ [0, 0]])
+
+ self.A, self.B = self.ContinuousToDiscrete(
+ self.A_continuous, self.B_continuous, self.dt)
+
+ print self.A
+
+ controlability = controls.ctrb(self.A, self.B);
+ print "Rank of augmented controlability matrix.", numpy.linalg.matrix_rank(
+ controlability)
+
+ q_pos = 0.02
+ q_vel = 0.400
+ q_pos_diff = 0.01
+ q_vel_diff = 0.45
+ self.Q = numpy.matrix([[(1.0 / (q_pos ** 2.0)), 0.0, 0.0, 0.0],
+ [0.0, (1.0 / (q_vel ** 2.0)), 0.0, 0.0],
+ [0.0, 0.0, (1.0 / (q_pos_diff ** 2.0)), 0.0],
+ [0.0, 0.0, 0.0, (1.0 / (q_vel_diff ** 2.0))]])
+
+ self.R = numpy.matrix([[(1.0 / (12.0 ** 2.0)), 0.0],
+ [0.0, 1.0 / (12.0 ** 2.0)]])
+ self.K = controls.dlqr(self.A, self.B, self.Q, self.R)
+ print self.K
+
+ print numpy.linalg.eig(self.A - self.B * self.K)[0]
+
+ self.rpl = 0.20
+ self.ipl = 0.05
+ self.PlaceObserverPoles([self.rpl + 1j * self.ipl,
+ self.rpl + 1j * self.ipl,
+ self.rpl - 1j * self.ipl,
+ self.rpl - 1j * self.ipl])
+
+ # The box formed by U_min and U_max must encompass all possible values,
+ # or else Austin's code gets angry.
+ self.U_max = numpy.matrix([[12.0], [12.0]])
+ self.U_min = numpy.matrix([[-12.0], [-12.0]])
+
+ self.InitializeState()
+
+
+def CapU(U):
+ if U[0, 0] - U[1, 0] > 24:
+ return numpy.matrix([[12], [-12]])
+ elif U[0, 0] - U[1, 0] < -24:
+ return numpy.matrix([[-12], [12]])
+ else:
+ max_u = max(U[0, 0], U[1, 0])
+ min_u = min(U[0, 0], U[1, 0])
+ if max_u > 12:
+ return U - (max_u - 12)
+ if min_u < -12:
+ return U - (min_u + 12)
+ return U
+
+
+def run_test(elevator, initial_X, goal, max_separation_error=0.01,
+ show_graph=True, iterations=200, controller_elevator=None,
+ observer_elevator=None):
+ """Runs the elevator plant with an initial condition and goal.
+
+ The tests themselves are not terribly sophisticated; I just test for
+ whether the goal has been reached and whether the separation goes
+ outside of the initial and goal values by more than max_separation_error.
+ Prints out something for a failure of either condition and returns
+ False if tests fail.
+ Args:
+ elevator: elevator object to use.
+ initial_X: starting state.
+ goal: goal state.
+ show_graph: Whether or not to display a graph showing the changing
+ states and voltages.
+ iterations: Number of timesteps to run the model for.
+ controller_elevator: elevator object to get K from, or None if we should
+ use elevator.
+ observer_elevator: elevator object to use for the observer, or None if we
+ should use the actual state.
+ """
+
+ elevator.X = initial_X
+
+ if controller_elevator is None:
+ controller_elevator = elevator
+
+ if observer_elevator is not None:
+ observer_elevator.X_hat = initial_X + 0.01
+ observer_elevator.X_hat = initial_X
+
+ # Various lists for graphing things.
+ t = []
+ x_avg = []
+ x_sep = []
+ x_hat_avg = []
+ x_hat_sep = []
+ v_avg = []
+ v_sep = []
+ u_left = []
+ u_right = []
+
+ sep_plot_gain = 100.0
+
+ for i in xrange(iterations):
+ X_hat = elevator.X
+ if observer_elevator is not None:
+ X_hat = observer_elevator.X_hat
+ x_hat_avg.append(observer_elevator.X_hat[0, 0])
+ x_hat_sep.append(observer_elevator.X_hat[2, 0] * sep_plot_gain)
+ U = controller_elevator.K * (goal - X_hat)
+ U = CapU(U)
+ x_avg.append(elevator.X[0, 0])
+ v_avg.append(elevator.X[1, 0])
+ x_sep.append(elevator.X[2, 0] * sep_plot_gain)
+ v_sep.append(elevator.X[3, 0])
+ if observer_elevator is not None:
+ observer_elevator.PredictObserver(U)
+ elevator.Update(U)
+ if observer_elevator is not None:
+ observer_elevator.Y = elevator.Y
+ observer_elevator.CorrectObserver(U)
+
+ t.append(i * elevator.dt)
+ u_left.append(U[0, 0])
+ u_right.append(U[1, 0])
+
+ print numpy.linalg.inv(elevator.A)
+ print "delta time is ", elevator.dt
+ print "Velocity at t=0 is ", x_avg[0], v_avg[0], x_sep[0], v_sep[0]
+ print "Velocity at t=1+dt is ", x_avg[1], v_avg[1], x_sep[1], v_sep[1]
+
+ if show_graph:
+ pylab.subplot(2, 1, 1)
+ pylab.plot(t, x_avg, label='x avg')
+ pylab.plot(t, x_sep, label='x sep')
+ if observer_elevator is not None:
+ pylab.plot(t, x_hat_avg, label='x_hat avg')
+ pylab.plot(t, x_hat_sep, label='x_hat sep')
+ pylab.legend()
+
+ pylab.subplot(2, 1, 2)
+ pylab.plot(t, u_left, label='u left')
+ pylab.plot(t, u_right, label='u right')
+ pylab.legend()
+ pylab.show()
+
+
+def main(argv):
+ loaded_mass = 25
+ #loaded_mass = 0
+ elevator = Elevator(mass=13 + loaded_mass)
+ elevator_controller = Elevator(mass=13 + 15)
+ observer_elevator = Elevator(mass=13 + 15)
+ #observer_elevator = None
+
+ # Test moving the elevator with constant separation.
+ initial_X = numpy.matrix([[0.0], [0.0], [0.01], [0.0]])
+ #initial_X = numpy.matrix([[0.0], [0.0], [0.00], [0.0]])
+ R = numpy.matrix([[1.0], [0.0], [0.0], [0.0]])
+ run_test(elevator, initial_X, R, controller_elevator=elevator_controller,
+ observer_elevator=observer_elevator)
+
+ # Write the generated constants out to a file.
+ if len(argv) != 3:
+ print "Expected .h file name and .cc file name for the elevator."
+ else:
+ elevator = Elevator("Elevator")
+ loop_writer = control_loop.ControlLoopWriter("Elevator", [elevator])
+ if argv[1][-3:] == '.cc':
+ loop_writer.Write(argv[2], argv[1])
+ else:
+ loop_writer.Write(argv[1], argv[2])
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv))
diff --git a/bot3/control_loops/python/elevator3.py b/bot3/control_loops/python/elevator3.py
new file mode 100755
index 0000000..120e8cc
--- /dev/null
+++ b/bot3/control_loops/python/elevator3.py
@@ -0,0 +1,273 @@
+#!/usr/bin/python
+
+import control_loop
+import controls
+import numpy
+import sys
+import matplotlib
+from matplotlib import pylab
+
+class Elevator(control_loop.ControlLoop):
+ def __init__(self, name="Elevator", mass=None):
+ super(Elevator, self).__init__(name)
+ # Stall Torque in N m
+ self.stall_torque = 2.402
+ # Stall Current in Amps
+ self.stall_current = 126.145
+ # Free Speed in RPM
+ self.free_speed = 5015.562
+ # Free Current in Amps
+ self.free_current = 1.170
+ # Mass of the Elevator
+ if mass is None:
+ self.mass = 5.0
+ else:
+ self.mass = mass
+
+ # Number of motors
+ self.num_motors = 2.0
+ # Resistance of the motor
+ self.resistance = 12.0 / self.stall_current
+ # Motor velocity constant
+ self.Kv = ((self.free_speed / 60.0 * 2.0 * numpy.pi) /
+ (12.0 - self.resistance * self.free_current))
+ # Torque constant
+ self.Kt = (self.num_motors * self.stall_torque) / self.stall_current
+ # Gear ratio
+ self.G = 8
+ # Radius of pulley
+ self.r = 0.0254
+
+ # Control loop time step
+ self.dt = 0.005
+
+ # State is [position, velocity]
+ # Input is [Voltage]
+
+ C1 = self.Kt * self.G * self.G / (self.Kv * self.resistance * self.r * self.r * self.mass)
+ C2 = self.G * self.Kt / (self.resistance * self.r * self.mass)
+
+ self.A_continuous = numpy.matrix(
+ [[0, 1],
+ [0, -C1]])
+
+ # Start with the unmodified input
+ self.B_continuous = numpy.matrix(
+ [[0],
+ [C2]])
+
+ self.C = numpy.matrix([[1, 0]])
+ self.D = numpy.matrix([[0]])
+
+ self.A, self.B = self.ContinuousToDiscrete(
+ self.A_continuous, self.B_continuous, self.dt)
+
+ controlability = controls.ctrb(self.A, self.B);
+
+ q_pos = 0.015
+ q_vel = 0.5
+ self.Q = numpy.matrix([[(1.0 / (q_pos ** 2.0)), 0.0],
+ [0.0, (1.0 / (q_vel ** 2.0))]])
+
+ self.R = numpy.matrix([[(1.0 / (12.0 ** 2.0))]])
+ self.K = controls.dlqr(self.A, self.B, self.Q, self.R)
+
+ print 'K', self.K
+ print 'Poles are', numpy.linalg.eig(self.A - self.B * self.K)[0]
+
+ self.rpl = 0.30
+ self.ipl = 0.10
+ self.PlaceObserverPoles([self.rpl + 1j * self.ipl,
+ self.rpl - 1j * self.ipl])
+
+ # print 'L is', self.L
+
+ q_pos = 0.05
+ q_vel = 2.65
+ self.Q = numpy.matrix([[(q_pos ** 2.0), 0.0],
+ [0.0, (q_vel ** 2.0)]])
+
+ r_volts = 0.025
+ self.R = numpy.matrix([[(r_volts ** 2.0)]])
+
+ self.KalmanGain, self.Q_steady = controls.kalman(
+ A=self.A, B=self.B, C=self.C, Q=self.Q, R=self.R)
+
+ # print 'Kal', self.KalmanGain
+ self.L = self.A * self.KalmanGain
+ print 'KalL is', self.L
+
+ # The box formed by U_min and U_max must encompass all possible values,
+ # or else Austin's code gets angry.
+ self.U_max = numpy.matrix([[12.0]])
+ self.U_min = numpy.matrix([[-12.0]])
+
+ self.InitializeState()
+
+class IntegralElevator(Elevator):
+ def __init__(self, name="IntegralElevator", mass=None):
+ super(IntegralElevator, self).__init__(name=name, mass=mass)
+
+ self.A_continuous_unaugmented = self.A_continuous
+ self.B_continuous_unaugmented = self.B_continuous
+
+ self.A_continuous = numpy.matrix(numpy.zeros((3, 3)))
+ self.A_continuous[0:2, 0:2] = self.A_continuous_unaugmented
+ self.A_continuous[0:2, 2] = self.B_continuous_unaugmented
+
+ self.B_continuous = numpy.matrix(numpy.zeros((3, 1)))
+ self.B_continuous[0:2, 0] = self.B_continuous_unaugmented
+
+ self.C_unaugmented = self.C
+ self.C = numpy.matrix(numpy.zeros((1, 3)))
+ self.C[0:1, 0:2] = self.C_unaugmented
+
+ self.A, self.B = self.ContinuousToDiscrete(self.A_continuous, self.B_continuous, self.dt)
+
+ q_pos = 0.08
+ q_vel = 0.40
+ q_voltage = 6.0
+ self.Q = numpy.matrix([[(q_pos ** 2.0), 0.0, 0.0],
+ [0.0, (q_vel ** 2.0), 0.0],
+ [0.0, 0.0, (q_voltage ** 2.0)]])
+
+ r_pos = 0.05
+ self.R = numpy.matrix([[(r_pos ** 2.0)]])
+
+ self.KalmanGain, self.Q_steady = controls.kalman(
+ A=self.A, B=self.B, C=self.C, Q=self.Q, R=self.R)
+ self.L = self.A * self.KalmanGain
+
+ self.K_unaugmented = self.K
+ self.K = numpy.matrix(numpy.zeros((1, 3)))
+ self.K[0, 0:2] = self.K_unaugmented
+ self.K[0, 2] = 1
+
+ self.InitializeState()
+
+
+class ScenarioPlotter(object):
+ def __init__(self):
+ # Various lists for graphing things.
+ self.t = []
+ self.x = []
+ self.v = []
+ self.a = []
+ self.x_hat = []
+ self.u = []
+
+ def run_test(self, elevator, goal,
+ iterations=200, controller_elevator=None,
+ observer_elevator=None):
+ """Runs the Elevator plant with an initial condition and goal.
+
+ Args:
+ Elevator: elevator object to use.
+ initial_X: starting state.
+ goal: goal state.
+ iterations: Number of timesteps to run the model for.
+ controller_Elevator: elevator object to get K from, or None if we should
+ use Elevator.
+ observer_Elevator: elevator object to use for the observer, or None if we should
+ use the actual state.
+ """
+
+ if controller_elevator is None:
+ controller_elevator = elevator
+
+ vbat = 10.0
+ if self.t:
+ initial_t = self.t[-1] + elevator.dt
+ else:
+ initial_t = 0
+ for i in xrange(iterations):
+ X_hat = elevator.X
+ if observer_elevator is not None:
+ X_hat = observer_elevator.X_hat
+ self.x_hat.append(observer_elevator.X_hat[0, 0])
+ gravity_compensation = 9.8 * elevator.mass * elevator.r / elevator.G / elevator.Kt * elevator.resistance
+
+ U = controller_elevator.K * (goal - X_hat)
+ U[0, 0] = numpy.clip(U[0, 0], -vbat , vbat )
+ self.x.append(elevator.X[0, 0])
+ if self.v:
+ last_v = self.v[-1]
+ else:
+ last_v = 0
+ self.v.append(elevator.X[1, 0])
+ self.a.append((self.v[-1] - last_v) / elevator.dt)
+
+ if observer_elevator is not None:
+ observer_elevator.Y = elevator.Y
+ observer_elevator.CorrectObserver(U)
+
+ elevator.Update(U - gravity_compensation)
+
+ if observer_elevator is not None:
+ observer_elevator.PredictObserver(U)
+
+ self.t.append(initial_t + i * elevator.dt)
+ self.u.append(U[0, 0])
+# if numpy.abs((goal - X_hat)[0:2, 0]).sum() < .025:
+# print "Time: ", self.t[-1]
+# break
+
+ print "Time: ", self.t[-1]
+
+
+ def Plot(self):
+ pylab.subplot(3, 1, 1)
+ pylab.plot(self.t, self.x, label='x')
+ pylab.plot(self.t, self.x_hat, label='x_hat')
+ pylab.legend()
+
+ pylab.subplot(3, 1, 2)
+ pylab.plot(self.t, self.u, label='u')
+
+ pylab.subplot(3, 1, 3)
+ pylab.plot(self.t, self.a, label='a')
+
+ pylab.legend()
+ pylab.show()
+
+
+def main(argv):
+ loaded_mass = 7+4.0
+ #loaded_mass = 0
+ #observer_elevator = None
+
+ # Test moving the Elevator
+ initial_X = numpy.matrix([[0.0], [0.0]])
+ up_R = numpy.matrix([[0.4572], [0.0], [0.0]])
+ down_R = numpy.matrix([[0.0], [0.0], [0.0]])
+ totemass = 3.54
+ scenario_plotter = ScenarioPlotter()
+
+ elevator_controller = IntegralElevator(mass=4*totemass + loaded_mass)
+ observer_elevator = IntegralElevator(mass=4*totemass + loaded_mass)
+
+ for i in xrange(0, 7):
+ elevator = Elevator(mass=i*totemass + loaded_mass)
+ print 'Actual poles are', numpy.linalg.eig(elevator.A - elevator.B * elevator_controller.K[0, 0:2])[0]
+
+ elevator.X = initial_X
+ scenario_plotter.run_test(elevator, goal=up_R, controller_elevator=elevator_controller,
+ observer_elevator=observer_elevator, iterations=200)
+ scenario_plotter.run_test(elevator, goal=down_R, controller_elevator=elevator_controller,
+ observer_elevator=observer_elevator, iterations=200)
+
+ scenario_plotter.Plot()
+
+ # Write the generated constants out to a file.
+ if len(argv) != 3:
+ print "Expected .h file name and .cc file name for the Elevator."
+ else:
+ elevator = Elevator("Elevator")
+ loop_writer = control_loop.ControlLoopWriter("Elevator", [elevator])
+ if argv[1][-3:] == '.cc':
+ loop_writer.Write(argv[2], argv[1])
+ else:
+ loop_writer.Write(argv[1], argv[2])
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv))
diff --git a/bot3/control_loops/python/polydrivetrain.py b/bot3/control_loops/python/polydrivetrain.py
new file mode 100644
index 0000000..a62c8c8
--- /dev/null
+++ b/bot3/control_loops/python/polydrivetrain.py
@@ -0,0 +1,504 @@
+#!/usr/bin/python
+
+import numpy
+import sys
+import polytope
+import drivetrain
+import control_loop
+import controls
+from matplotlib import pylab
+
+__author__ = 'Austin Schuh (austin.linux@gmail.com)'
+
+
+def CoerceGoal(region, K, w, R):
+ """Intersects a line with a region, and finds the closest point to R.
+
+ Finds a point that is closest to R inside the region, and on the line
+ defined by K X = w. If it is not possible to find a point on the line,
+ finds a point that is inside the region and closest to the line. This
+ function assumes that
+
+ Args:
+ region: HPolytope, the valid goal region.
+ K: numpy.matrix (2 x 1), the matrix for the equation [K1, K2] [x1; x2] = w
+ w: float, the offset in the equation above.
+ R: numpy.matrix (2 x 1), the point to be closest to.
+
+ Returns:
+ numpy.matrix (2 x 1), the point.
+ """
+ return DoCoerceGoal(region, K, w, R)[0]
+
+def DoCoerceGoal(region, K, w, R):
+ if region.IsInside(R):
+ return (R, True)
+
+ perpendicular_vector = K.T / numpy.linalg.norm(K)
+ parallel_vector = numpy.matrix([[perpendicular_vector[1, 0]],
+ [-perpendicular_vector[0, 0]]])
+
+ # We want to impose the constraint K * X = w on the polytope H * X <= k.
+ # We do this by breaking X up into parallel and perpendicular components to
+ # the half plane. This gives us the following equation.
+ #
+ # parallel * (parallel.T \dot X) + perpendicular * (perpendicular \dot X)) = X
+ #
+ # Then, substitute this into the polytope.
+ #
+ # H * (parallel * (parallel.T \dot X) + perpendicular * (perpendicular \dot X)) <= k
+ #
+ # Substitute K * X = w
+ #
+ # H * parallel * (parallel.T \dot X) + H * perpendicular * w <= k
+ #
+ # Move all the knowns to the right side.
+ #
+ # H * parallel * ([parallel1 parallel2] * X) <= k - H * perpendicular * w
+ #
+ # Let t = parallel.T \dot X, the component parallel to the surface.
+ #
+ # H * parallel * t <= k - H * perpendicular * w
+ #
+ # This is a polytope which we can solve, and use to figure out the range of X
+ # that we care about!
+
+ t_poly = polytope.HPolytope(
+ region.H * parallel_vector,
+ region.k - region.H * perpendicular_vector * w)
+
+ vertices = t_poly.Vertices()
+
+ if vertices.shape[0]:
+ # The region exists!
+ # Find the closest vertex
+ min_distance = numpy.infty
+ closest_point = None
+ for vertex in vertices:
+ point = parallel_vector * vertex + perpendicular_vector * w
+ length = numpy.linalg.norm(R - point)
+ if length < min_distance:
+ min_distance = length
+ closest_point = point
+
+ return (closest_point, True)
+ else:
+ # Find the vertex of the space that is closest to the line.
+ region_vertices = region.Vertices()
+ min_distance = numpy.infty
+ closest_point = None
+ for vertex in region_vertices:
+ point = vertex.T
+ length = numpy.abs((perpendicular_vector.T * point)[0, 0])
+ if length < min_distance:
+ min_distance = length
+ closest_point = point
+
+ return (closest_point, False)
+
+
+class VelocityDrivetrainModel(control_loop.ControlLoop):
+ def __init__(self, left_low=True, right_low=True, name="VelocityDrivetrainModel"):
+ super(VelocityDrivetrainModel, self).__init__(name)
+ self._drivetrain = drivetrain.Drivetrain(left_low=left_low,
+ right_low=right_low)
+ self.dt = 0.01
+ self.A_continuous = numpy.matrix(
+ [[self._drivetrain.A_continuous[1, 1], self._drivetrain.A_continuous[1, 3]],
+ [self._drivetrain.A_continuous[3, 1], self._drivetrain.A_continuous[3, 3]]])
+
+ self.B_continuous = numpy.matrix(
+ [[self._drivetrain.B_continuous[1, 0], self._drivetrain.B_continuous[1, 1]],
+ [self._drivetrain.B_continuous[3, 0], self._drivetrain.B_continuous[3, 1]]])
+ self.C = numpy.matrix(numpy.eye(2));
+ self.D = numpy.matrix(numpy.zeros((2, 2)));
+
+ self.A, self.B = self.ContinuousToDiscrete(self.A_continuous,
+ self.B_continuous, self.dt)
+
+ # FF * X = U (steady state)
+ self.FF = self.B.I * (numpy.eye(2) - self.A)
+
+ self.PlaceControllerPoles([0.6, 0.6])
+ self.PlaceObserverPoles([0.02, 0.02])
+
+ self.G_high = self._drivetrain.G_high
+ self.G_low = self._drivetrain.G_low
+ self.R = self._drivetrain.R
+ self.r = self._drivetrain.r
+ self.Kv = self._drivetrain.Kv
+ self.Kt = self._drivetrain.Kt
+
+ self.U_max = self._drivetrain.U_max
+ self.U_min = self._drivetrain.U_min
+
+
+class VelocityDrivetrain(object):
+ HIGH = 'high'
+ LOW = 'low'
+ SHIFTING_UP = 'up'
+ SHIFTING_DOWN = 'down'
+
+ def __init__(self):
+ self.drivetrain_low_low = VelocityDrivetrainModel(
+ left_low=True, right_low=True, name='VelocityDrivetrainLowLow')
+ self.drivetrain_low_high = VelocityDrivetrainModel(left_low=True, right_low=False, name='VelocityDrivetrainLowHigh')
+ self.drivetrain_high_low = VelocityDrivetrainModel(left_low=False, right_low=True, name = 'VelocityDrivetrainHighLow')
+ self.drivetrain_high_high = VelocityDrivetrainModel(left_low=False, right_low=False, name = 'VelocityDrivetrainHighHigh')
+
+ # X is [lvel, rvel]
+ self.X = numpy.matrix(
+ [[0.0],
+ [0.0]])
+
+ self.U_poly = polytope.HPolytope(
+ numpy.matrix([[1, 0],
+ [-1, 0],
+ [0, 1],
+ [0, -1]]),
+ numpy.matrix([[12],
+ [12],
+ [12],
+ [12]]))
+
+ self.U_max = numpy.matrix(
+ [[12.0],
+ [12.0]])
+ self.U_min = numpy.matrix(
+ [[-12.0000000000],
+ [-12.0000000000]])
+
+ self.dt = 0.01
+
+ self.R = numpy.matrix(
+ [[0.0],
+ [0.0]])
+
+ # ttrust is the comprimise between having full throttle negative inertia,
+ # and having no throttle negative inertia. A value of 0 is full throttle
+ # inertia. A value of 1 is no throttle negative inertia.
+ self.ttrust = 1.1
+
+ self.left_gear = VelocityDrivetrain.LOW
+ self.right_gear = VelocityDrivetrain.LOW
+ self.left_shifter_position = 0.0
+ self.right_shifter_position = 0.0
+ self.left_cim = drivetrain.CIM()
+ self.right_cim = drivetrain.CIM()
+
+ def IsInGear(self, gear):
+ return gear is VelocityDrivetrain.HIGH or gear is VelocityDrivetrain.LOW
+
+ def MotorRPM(self, shifter_position, velocity):
+ if shifter_position > 0.5:
+ return (velocity / self.CurrentDrivetrain().G_high /
+ self.CurrentDrivetrain().r)
+ else:
+ return (velocity / self.CurrentDrivetrain().G_low /
+ self.CurrentDrivetrain().r)
+
+ def CurrentDrivetrain(self):
+ if self.left_shifter_position > 0.5:
+ if self.right_shifter_position > 0.5:
+ return self.drivetrain_high_high
+ else:
+ return self.drivetrain_high_low
+ else:
+ if self.right_shifter_position > 0.5:
+ return self.drivetrain_low_high
+ else:
+ return self.drivetrain_low_low
+
+ def SimShifter(self, gear, shifter_position):
+ if gear is VelocityDrivetrain.HIGH or gear is VelocityDrivetrain.SHIFTING_UP:
+ shifter_position = min(shifter_position + 0.5, 1.0)
+ else:
+ shifter_position = max(shifter_position - 0.5, 0.0)
+
+ if shifter_position == 1.0:
+ gear = VelocityDrivetrain.HIGH
+ elif shifter_position == 0.0:
+ gear = VelocityDrivetrain.LOW
+
+ return gear, shifter_position
+
+ def ComputeGear(self, wheel_velocity, should_print=False, current_gear=False, gear_name=None):
+ high_omega = (wheel_velocity / self.CurrentDrivetrain().G_high /
+ self.CurrentDrivetrain().r)
+ low_omega = (wheel_velocity / self.CurrentDrivetrain().G_low /
+ self.CurrentDrivetrain().r)
+ #print gear_name, "Motor Energy Difference.", 0.5 * 0.000140032647 * (low_omega * low_omega - high_omega * high_omega), "joules"
+ high_torque = ((12.0 - high_omega / self.CurrentDrivetrain().Kv) *
+ self.CurrentDrivetrain().Kt / self.CurrentDrivetrain().R)
+ low_torque = ((12.0 - low_omega / self.CurrentDrivetrain().Kv) *
+ self.CurrentDrivetrain().Kt / self.CurrentDrivetrain().R)
+ high_power = high_torque * high_omega
+ low_power = low_torque * low_omega
+ #if should_print:
+ # print gear_name, "High omega", high_omega, "Low omega", low_omega
+ # print gear_name, "High torque", high_torque, "Low torque", low_torque
+ # print gear_name, "High power", high_power, "Low power", low_power
+
+ # Shift algorithm improvements.
+ # TODO(aschuh):
+ # It takes time to shift. Shifting down for 1 cycle doesn't make sense
+ # because you will end up slower than without shifting. Figure out how
+ # to include that info.
+ # If the driver is still in high gear, but isn't asking for the extra power
+ # from low gear, don't shift until he asks for it.
+ goal_gear_is_high = high_power > low_power
+ #goal_gear_is_high = True
+
+ if not self.IsInGear(current_gear):
+ print gear_name, 'Not in gear.'
+ return current_gear
+ else:
+ is_high = current_gear is VelocityDrivetrain.HIGH
+ if is_high != goal_gear_is_high:
+ if goal_gear_is_high:
+ print gear_name, 'Shifting up.'
+ return VelocityDrivetrain.SHIFTING_UP
+ else:
+ print gear_name, 'Shifting down.'
+ return VelocityDrivetrain.SHIFTING_DOWN
+ else:
+ return current_gear
+
+ def FilterVelocity(self, throttle):
+ # Invert the plant to figure out how the velocity filter would have to work
+ # out in order to filter out the forwards negative inertia.
+ # This math assumes that the left and right power and velocity are equal.
+
+ # The throttle filter should filter such that the motor in the highest gear
+ # should be controlling the time constant.
+ # Do this by finding the index of FF that has the lowest value, and computing
+ # the sums using that index.
+ FF_sum = self.CurrentDrivetrain().FF.sum(axis=1)
+ min_FF_sum_index = numpy.argmin(FF_sum)
+ min_FF_sum = FF_sum[min_FF_sum_index, 0]
+ min_K_sum = self.CurrentDrivetrain().K[min_FF_sum_index, :].sum()
+ # Compute the FF sum for high gear.
+ high_min_FF_sum = self.drivetrain_high_high.FF[0, :].sum()
+
+ # U = self.K[0, :].sum() * (R - x_avg) + self.FF[0, :].sum() * R
+ # throttle * 12.0 = (self.K[0, :].sum() + self.FF[0, :].sum()) * R
+ # - self.K[0, :].sum() * x_avg
+
+ # R = (throttle * 12.0 + self.K[0, :].sum() * x_avg) /
+ # (self.K[0, :].sum() + self.FF[0, :].sum())
+
+ # U = (K + FF) * R - K * X
+ # (K + FF) ^-1 * (U + K * X) = R
+
+ # Scale throttle by min_FF_sum / high_min_FF_sum. This will make low gear
+ # have the same velocity goal as high gear, and so that the robot will hold
+ # the same speed for the same throttle for all gears.
+ adjusted_ff_voltage = numpy.clip(throttle * 12.0 * min_FF_sum / high_min_FF_sum, -12.0, 12.0)
+ return ((adjusted_ff_voltage + self.ttrust * min_K_sum * (self.X[0, 0] + self.X[1, 0]) / 2.0)
+ / (self.ttrust * min_K_sum + min_FF_sum))
+
+ def Update(self, throttle, steering):
+ # Shift into the gear which sends the most power to the floor.
+ # This is the same as sending the most torque down to the floor at the
+ # wheel.
+
+ self.left_gear = self.right_gear = True
+ if False:
+ self.left_gear = self.ComputeGear(self.X[0, 0], should_print=True,
+ current_gear=self.left_gear,
+ gear_name="left")
+ self.right_gear = self.ComputeGear(self.X[1, 0], should_print=True,
+ current_gear=self.right_gear,
+ gear_name="right")
+ if self.IsInGear(self.left_gear):
+ self.left_cim.X[0, 0] = self.MotorRPM(self.left_shifter_position, self.X[0, 0])
+
+ if self.IsInGear(self.right_gear):
+ self.right_cim.X[0, 0] = self.MotorRPM(self.right_shifter_position, self.X[0, 0])
+
+ steering *= 2.3
+ if True or self.IsInGear(self.left_gear) and self.IsInGear(self.right_gear):
+ # Filter the throttle to provide a nicer response.
+ fvel = self.FilterVelocity(throttle)
+
+ # Constant radius means that angualar_velocity / linear_velocity = constant.
+ # Compute the left and right velocities.
+ steering_velocity = numpy.abs(fvel) * steering
+ left_velocity = fvel - steering_velocity
+ right_velocity = fvel + steering_velocity
+
+ # Write this constraint in the form of K * R = w
+ # angular velocity / linear velocity = constant
+ # (left - right) / (left + right) = constant
+ # left - right = constant * left + constant * right
+
+ # (fvel - steering * numpy.abs(fvel) - fvel - steering * numpy.abs(fvel)) /
+ # (fvel - steering * numpy.abs(fvel) + fvel + steering * numpy.abs(fvel)) =
+ # constant
+ # (- 2 * steering * numpy.abs(fvel)) / (2 * fvel) = constant
+ # (-steering * sign(fvel)) = constant
+ # (-steering * sign(fvel)) * (left + right) = left - right
+ # (steering * sign(fvel) + 1) * left + (steering * sign(fvel) - 1) * right = 0
+
+ equality_k = numpy.matrix(
+ [[1 + steering * numpy.sign(fvel), -(1 - steering * numpy.sign(fvel))]])
+ equality_w = 0.0
+
+ self.R[0, 0] = left_velocity
+ self.R[1, 0] = right_velocity
+
+ # Construct a constraint on R by manipulating the constraint on U
+ # Start out with H * U <= k
+ # U = FF * R + K * (R - X)
+ # H * (FF * R + K * R - K * X) <= k
+ # H * (FF + K) * R <= k + H * K * X
+ R_poly = polytope.HPolytope(
+ self.U_poly.H * (self.CurrentDrivetrain().K + self.CurrentDrivetrain().FF),
+ self.U_poly.k + self.U_poly.H * self.CurrentDrivetrain().K * self.X)
+
+ # Limit R back inside the box.
+ self.boxed_R = CoerceGoal(R_poly, equality_k, equality_w, self.R)
+
+ FF_volts = self.CurrentDrivetrain().FF * self.boxed_R
+ self.U_ideal = self.CurrentDrivetrain().K * (self.boxed_R - self.X) + FF_volts
+ else:
+ print 'Not all in gear'
+ if not self.IsInGear(self.left_gear) and not self.IsInGear(self.right_gear):
+ # TODO(austin): Use battery volts here.
+ R_left = self.MotorRPM(self.left_shifter_position, self.X[0, 0])
+ self.U_ideal[0, 0] = numpy.clip(
+ self.left_cim.K * (R_left - self.left_cim.X) + R_left / self.left_cim.Kv,
+ self.left_cim.U_min, self.left_cim.U_max)
+ self.left_cim.Update(self.U_ideal[0, 0])
+
+ R_right = self.MotorRPM(self.right_shifter_position, self.X[1, 0])
+ self.U_ideal[1, 0] = numpy.clip(
+ self.right_cim.K * (R_right - self.right_cim.X) + R_right / self.right_cim.Kv,
+ self.right_cim.U_min, self.right_cim.U_max)
+ self.right_cim.Update(self.U_ideal[1, 0])
+ else:
+ assert False
+
+ self.U = numpy.clip(self.U_ideal, self.U_min, self.U_max)
+
+ # TODO(austin): Model the robot as not accelerating when you shift...
+ # This hack only works when you shift at the same time.
+ if True or self.IsInGear(self.left_gear) and self.IsInGear(self.right_gear):
+ self.X = self.CurrentDrivetrain().A * self.X + self.CurrentDrivetrain().B * self.U
+
+ self.left_gear, self.left_shifter_position = self.SimShifter(
+ self.left_gear, self.left_shifter_position)
+ self.right_gear, self.right_shifter_position = self.SimShifter(
+ self.right_gear, self.right_shifter_position)
+
+ print "U is", self.U[0, 0], self.U[1, 0]
+ print "Left shifter", self.left_gear, self.left_shifter_position, "Right shifter", self.right_gear, self.right_shifter_position
+
+
+def main(argv):
+ vdrivetrain = VelocityDrivetrain()
+
+ if len(argv) != 7:
+ print "Expected .h file name and .cc file name"
+ else:
+ dog_loop_writer = control_loop.ControlLoopWriter(
+ "VelocityDrivetrain", [vdrivetrain.drivetrain_low_low,
+ vdrivetrain.drivetrain_low_high,
+ vdrivetrain.drivetrain_high_low,
+ vdrivetrain.drivetrain_high_high])
+
+ if argv[1][-3:] == '.cc':
+ dog_loop_writer.Write(argv[2], argv[1])
+ else:
+ dog_loop_writer.Write(argv[1], argv[2])
+
+ cim_writer = control_loop.ControlLoopWriter(
+ "CIM", [drivetrain.CIM()])
+
+ if argv[5][-3:] == '.cc':
+ cim_writer.Write(argv[6], argv[5])
+ else:
+ cim_writer.Write(argv[5], argv[6])
+ return
+
+ vl_plot = []
+ vr_plot = []
+ ul_plot = []
+ ur_plot = []
+ radius_plot = []
+ t_plot = []
+ left_gear_plot = []
+ right_gear_plot = []
+ vdrivetrain.left_shifter_position = 0.0
+ vdrivetrain.right_shifter_position = 0.0
+ vdrivetrain.left_gear = VelocityDrivetrain.LOW
+ vdrivetrain.right_gear = VelocityDrivetrain.LOW
+
+ print "K is", vdrivetrain.CurrentDrivetrain().K
+
+ if vdrivetrain.left_gear is VelocityDrivetrain.HIGH:
+ print "Left is high"
+ else:
+ print "Left is low"
+ if vdrivetrain.right_gear is VelocityDrivetrain.HIGH:
+ print "Right is high"
+ else:
+ print "Right is low"
+
+ for t in numpy.arange(0, 1.7, vdrivetrain.dt):
+ if t < 0.5:
+ vdrivetrain.Update(throttle=0.00, steering=1.0)
+ elif t < 1.2:
+ vdrivetrain.Update(throttle=0.5, steering=1.0)
+ else:
+ vdrivetrain.Update(throttle=0.00, steering=1.0)
+ t_plot.append(t)
+ vl_plot.append(vdrivetrain.X[0, 0])
+ vr_plot.append(vdrivetrain.X[1, 0])
+ ul_plot.append(vdrivetrain.U[0, 0])
+ ur_plot.append(vdrivetrain.U[1, 0])
+ left_gear_plot.append((vdrivetrain.left_gear is VelocityDrivetrain.HIGH) * 2.0 - 10.0)
+ right_gear_plot.append((vdrivetrain.right_gear is VelocityDrivetrain.HIGH) * 2.0 - 10.0)
+
+ fwd_velocity = (vdrivetrain.X[1, 0] + vdrivetrain.X[0, 0]) / 2
+ turn_velocity = (vdrivetrain.X[1, 0] - vdrivetrain.X[0, 0])
+ if abs(fwd_velocity) < 0.0000001:
+ radius_plot.append(turn_velocity)
+ else:
+ radius_plot.append(turn_velocity / fwd_velocity)
+
+ cim_velocity_plot = []
+ cim_voltage_plot = []
+ cim_time = []
+ cim = drivetrain.CIM()
+ R = numpy.matrix([[300]])
+ for t in numpy.arange(0, 0.5, cim.dt):
+ U = numpy.clip(cim.K * (R - cim.X) + R / cim.Kv, cim.U_min, cim.U_max)
+ cim.Update(U)
+ cim_velocity_plot.append(cim.X[0, 0])
+ cim_voltage_plot.append(U[0, 0] * 10)
+ cim_time.append(t)
+ pylab.plot(cim_time, cim_velocity_plot, label='cim spinup')
+ pylab.plot(cim_time, cim_voltage_plot, label='cim voltage')
+ pylab.legend()
+ pylab.show()
+
+ # TODO(austin):
+ # Shifting compensation.
+
+ # Tighten the turn.
+ # Closed loop drive.
+
+ pylab.plot(t_plot, vl_plot, label='left velocity')
+ pylab.plot(t_plot, vr_plot, label='right velocity')
+ pylab.plot(t_plot, ul_plot, label='left voltage')
+ pylab.plot(t_plot, ur_plot, label='right voltage')
+ pylab.plot(t_plot, radius_plot, label='radius')
+ pylab.plot(t_plot, left_gear_plot, label='left gear high')
+ pylab.plot(t_plot, right_gear_plot, label='right gear high')
+ pylab.legend()
+ pylab.show()
+ return 0
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv))
diff --git a/bot3/control_loops/python/polydrivetrain_test.py b/bot3/control_loops/python/polydrivetrain_test.py
new file mode 100644
index 0000000..434cdca
--- /dev/null
+++ b/bot3/control_loops/python/polydrivetrain_test.py
@@ -0,0 +1,82 @@
+#!/usr/bin/python
+
+import polydrivetrain
+import numpy
+from numpy.testing import *
+import polytope
+import unittest
+
+__author__ = 'Austin Schuh (austin.linux@gmail.com)'
+
+
+class TestVelocityDrivetrain(unittest.TestCase):
+ def MakeBox(self, x1_min, x1_max, x2_min, x2_max):
+ H = numpy.matrix([[1, 0],
+ [-1, 0],
+ [0, 1],
+ [0, -1]])
+ K = numpy.matrix([[x1_max],
+ [-x1_min],
+ [x2_max],
+ [-x2_min]])
+ return polytope.HPolytope(H, K)
+
+ def test_coerce_inside(self):
+ """Tests coercion when the point is inside the box."""
+ box = self.MakeBox(1, 2, 1, 2)
+
+ # x1 = x2
+ K = numpy.matrix([[1, -1]])
+ w = 0
+
+ assert_array_equal(polydrivetrain.CoerceGoal(box, K, w,
+ numpy.matrix([[1.5], [1.5]])),
+ numpy.matrix([[1.5], [1.5]]))
+
+ def test_coerce_outside_intersect(self):
+ """Tests coercion when the line intersects the box."""
+ box = self.MakeBox(1, 2, 1, 2)
+
+ # x1 = x2
+ K = numpy.matrix([[1, -1]])
+ w = 0
+
+ assert_array_equal(polydrivetrain.CoerceGoal(box, K, w, numpy.matrix([[5], [5]])),
+ numpy.matrix([[2.0], [2.0]]))
+
+ def test_coerce_outside_no_intersect(self):
+ """Tests coercion when the line does not intersect the box."""
+ box = self.MakeBox(3, 4, 1, 2)
+
+ # x1 = x2
+ K = numpy.matrix([[1, -1]])
+ w = 0
+
+ assert_array_equal(polydrivetrain.CoerceGoal(box, K, w, numpy.matrix([[5], [5]])),
+ numpy.matrix([[3.0], [2.0]]))
+
+ def test_coerce_middle_of_edge(self):
+ """Tests coercion when the line intersects the middle of an edge."""
+ box = self.MakeBox(0, 4, 1, 2)
+
+ # x1 = x2
+ K = numpy.matrix([[-1, 1]])
+ w = 0
+
+ assert_array_equal(polydrivetrain.CoerceGoal(box, K, w, numpy.matrix([[5], [5]])),
+ numpy.matrix([[2.0], [2.0]]))
+
+ def test_coerce_perpendicular_line(self):
+ """Tests coercion when the line does not intersect and is in quadrant 2."""
+ box = self.MakeBox(1, 2, 1, 2)
+
+ # x1 = -x2
+ K = numpy.matrix([[1, 1]])
+ w = 0
+
+ assert_array_equal(polydrivetrain.CoerceGoal(box, K, w, numpy.matrix([[5], [5]])),
+ numpy.matrix([[1.0], [1.0]]))
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/bot3/control_loops/update_drivetrain.sh b/bot3/control_loops/update_drivetrain.sh
new file mode 100644
index 0000000..369cbc5
--- /dev/null
+++ b/bot3/control_loops/update_drivetrain.sh
@@ -0,0 +1,10 @@
+#!/bin/bash
+#
+# Updates the drivetrain controllers.
+
+cd $(dirname $0)
+
+./python/drivetrain.py drivetrain/drivetrain_dog_motor_plant.h \
+ drivetrain/drivetrain_dog_motor_plant.cc \
+ drivetrain/drivetrain_clutch_motor_plant.h \
+ drivetrain/drivetrain_clutch_motor_plant.cc
diff --git a/bot3/control_loops/update_polydrivetrain.sh b/bot3/control_loops/update_polydrivetrain.sh
new file mode 100644
index 0000000..37987d5
--- /dev/null
+++ b/bot3/control_loops/update_polydrivetrain.sh
@@ -0,0 +1,12 @@
+#!/bin/bash
+#
+# Updates the polydrivetrain controllers and CIM models.
+
+cd $(dirname $0)
+
+./python/polydrivetrain.py drivetrain/polydrivetrain_dog_motor_plant.h \
+ drivetrain/polydrivetrain_dog_motor_plant.cc \
+ drivetrain/polydrivetrain_clutch_motor_plant.h \
+ drivetrain/polydrivetrain_clutch_motor_plant.cc \
+ drivetrain/polydrivetrain_cim_plant.h \
+ drivetrain/polydrivetrain_cim_plant.cc
diff --git a/bot3/joystick_reader.cc b/bot3/joystick_reader.cc
new file mode 100644
index 0000000..fd7f36b
--- /dev/null
+++ b/bot3/joystick_reader.cc
@@ -0,0 +1,148 @@
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <math.h>
+
+#include "aos/linux_code/init.h"
+#include "aos/prime/input/joystick_input.h"
+#include "aos/common/input/driver_station_data.h"
+#include "aos/common/logging/logging.h"
+#include "aos/common/util/log_interval.h"
+#include "aos/common/time.h"
+#include "aos/common/actions/actions.h"
+
+#include "bot3/control_loops/drivetrain/drivetrain.q.h"
+#include "frc971/queues/gyro.q.h"
+#include "bot3/autonomous/auto.q.h"
+
+using ::bot3::control_loops::drivetrain_queue;
+using ::frc971::sensors::gyro_reading;
+
+using ::aos::input::driver_station::ButtonLocation;
+using ::aos::input::driver_station::POVLocation;
+using ::aos::input::driver_station::JoystickAxis;
+using ::aos::input::driver_station::ControlBit;
+
+namespace bot3 {
+namespace input {
+namespace joysticks {
+
+const JoystickAxis kSteeringWheel(1, 1), kDriveThrottle(2, 2);
+const ButtonLocation kShiftHigh(2, 1), kShiftLow(2, 3);
+const ButtonLocation kQuickTurn(1, 5);
+
+// TODO(comran): Figure out the actions we need.
+
+class Reader : public ::aos::input::JoystickInput {
+ public:
+ Reader() : was_running_(false) {}
+
+ virtual void RunIteration(const ::aos::input::driver_station::Data &data) {
+ bool last_auto_running = auto_running_;
+ auto_running_ = data.GetControlBit(ControlBit::kAutonomous) &&
+ data.GetControlBit(ControlBit::kEnabled);
+ if (auto_running_ != last_auto_running) {
+ if (auto_running_) {
+ StartAuto();
+ } else {
+ StopAuto();
+ }
+ }
+
+ if (!data.GetControlBit(ControlBit::kAutonomous)) {
+ HandleDrivetrain(data);
+ HandleTeleop(data);
+ }
+ }
+
+ void HandleDrivetrain(const ::aos::input::driver_station::Data &data) {
+ const double wheel = -data.GetAxis(kSteeringWheel);
+ const double throttle = -data.GetAxis(kDriveThrottle);
+
+ if (!drivetrain_queue.goal.MakeWithBuilder()
+ .steering(wheel)
+ .throttle(throttle)
+ .quickturn(data.IsPressed(kQuickTurn))
+ .control_loop_driving(false)
+ .Send()) {
+ LOG(WARNING, "sending stick values failed\n");
+ }
+ }
+
+ void HandleTeleop(const ::aos::input::driver_station::Data &data) {
+ if (!data.GetControlBit(ControlBit::kEnabled)) {
+ action_queue_.CancelAllActions();
+ LOG(DEBUG, "Canceling\n");
+ }
+
+ // TODO(comran): Run stuff when buttons are pushed.
+
+ // TODO(comran): If it cannot get a status from the superstructure...
+ /*
+ if (!superstructure_queue.status.get()) {
+ LOG(ERROR, "Got no superstructure status packet.\n");
+ }
+ */
+
+ // TODO(comran): If everything looks good, start teleop.
+ /*if (superstructure_queue.status.get() && superstructure_queue.status->zeroed) {
+ if (waiting_for_zero_) {
+ LOG(INFO, "Zeroed! Starting teleop mode.\n");
+ waiting_for_zero_ = false;
+
+ // Set the initial goals to where we are now.
+ superstructure_goal_ = 0.0;
+ }
+ } else {
+ waiting_for_zero_ = true;
+ }*/
+
+ if (!waiting_for_zero_) {
+ if (!action_queue_.Running()) {
+ // TODO(comran): Send some goals.
+ }
+ }
+
+ if (action_queue_.Running()) {
+ // If we are running an action, update our goals to the current goals.
+ // TODO(comran): Do this for each superstructure queue.
+ }
+ action_queue_.Tick();
+ was_running_ = action_queue_.Running();
+ }
+
+ private:
+ void StartAuto() {
+ LOG(INFO, "Starting auto mode\n");
+ ::bot3::autonomous::autonomous.MakeWithBuilder().run_auto(true).Send();
+ }
+
+ void StopAuto() {
+ LOG(INFO, "Stopping auto mode\n");
+ ::bot3::autonomous::autonomous.MakeWithBuilder().run_auto(false).Send();
+ }
+
+ bool was_running_;
+
+ // If we're waiting for the subsystems to zero.
+ bool waiting_for_zero_ = true;
+
+ bool auto_running_ = false;
+
+ ::aos::common::actions::ActionQueue action_queue_;
+
+ ::aos::util::SimpleLogInterval no_drivetrain_status_ =
+ ::aos::util::SimpleLogInterval(::aos::time::Time::InSeconds(0.2), WARNING,
+ "no drivetrain status");
+};
+
+} // namespace joysticks
+} // namespace input
+} // namespace bot3
+
+int main() {
+ ::aos::Init();
+ ::bot3::input::joysticks::Reader reader;
+ reader.Run();
+ ::aos::Cleanup();
+}
diff --git a/bot3/prime/build.sh b/bot3/prime/build.sh
new file mode 100755
index 0000000..9586590
--- /dev/null
+++ b/bot3/prime/build.sh
@@ -0,0 +1,5 @@
+#!/bin/bash
+
+cd $(dirname $0)
+
+exec ../../aos/build/build.py $0 prime bot3 prime.gyp "$@"
diff --git a/bot3/prime/prime.gyp b/bot3/prime/prime.gyp
new file mode 100644
index 0000000..f0a0e27
--- /dev/null
+++ b/bot3/prime/prime.gyp
@@ -0,0 +1,40 @@
+{
+ 'targets': [
+ {
+ 'target_name': 'All',
+ 'type': 'none',
+ 'dependencies': [
+ '<(AOS)/build/aos_all.gyp:Prime',
+
+ '<(DEPTH)/frc971/control_loops/control_loops.gyp:state_feedback_loop_test',
+ '<(DEPTH)/frc971/control_loops/control_loops.gyp:position_sensor_sim_test',
+ '../control_loops/drivetrain/drivetrain.gyp:drivetrain_bot3',
+ '../control_loops/drivetrain/drivetrain.gyp:drivetrain_lib_test_bot3',
+ '../control_loops/drivetrain/drivetrain.gyp:replay_drivetrain_bot3',
+ '../control_loops/intake/intake.gyp:intake',
+ '../control_loops/intake/intake.gyp:intake_lib_test',
+ #'../autonomous/autonomous.gyp:auto_bot3',
+ '../bot3.gyp:joystick_reader_bot3',
+ '<(DEPTH)/frc971/zeroing/zeroing.gyp:zeroing_test',
+ #'../http_status/http_status.gyp:http_status',
+ '<(DEPTH)/frc971/control_loops/voltage_cap/voltage_cap.gyp:voltage_cap_test',
+ '../actors/actors.gyp:binaries',
+ ],
+ 'copies': [
+ {
+ 'destination': '<(rsync_dir)',
+ 'files': [
+ 'start_list.txt',
+ ],
+ },
+ ],
+ 'conditions': [
+ ['ARCHITECTURE=="arm_frc"', {
+ 'dependencies': [
+ '../wpilib/wpilib.gyp:wpilib_interface_bot3',
+ ],
+ }],
+ ],
+ },
+ ],
+}
diff --git a/bot3/prime/start_list.txt b/bot3/prime/start_list.txt
new file mode 100644
index 0000000..390595a
--- /dev/null
+++ b/bot3/prime/start_list.txt
@@ -0,0 +1,7 @@
+joystick_reader_bot3
+wpilib_interface_bot3
+binary_log_writer
+drivetrain_bot3
+auto_bot3
+drivetrain_action_bot3
+http_status_bot3
diff --git a/bot3/wpilib/wpilib.gyp b/bot3/wpilib/wpilib.gyp
new file mode 100644
index 0000000..311f6cd
--- /dev/null
+++ b/bot3/wpilib/wpilib.gyp
@@ -0,0 +1,36 @@
+{
+ 'targets': [
+ {
+ 'target_name': 'wpilib_interface_bot3',
+ 'type': 'executable',
+ 'sources': [
+ 'wpilib_interface.cc'
+ ],
+ 'dependencies': [
+ '<(AOS)/linux_code/linux_code.gyp:init',
+ '<(AOS)/common/common.gyp:stl_mutex',
+ '<(AOS)/build/aos.gyp:logging',
+ '<(EXTERNALS):WPILib',
+ '<(DEPTH)/bot3/control_loops/drivetrain/drivetrain.gyp:drivetrain_queue',
+ '<(AOS)/common/controls/controls.gyp:control_loop',
+ '<(AOS)/common/util/util.gyp:log_interval',
+ '<(AOS)/common/common.gyp:time',
+ '<(AOS)/common/logging/logging.gyp:queue_logging',
+ '<(AOS)/common/messages/messages.gyp:robot_state',
+ '<(AOS)/common/util/util.gyp:phased_loop',
+ '<(AOS)/common/messages/messages.gyp:robot_state',
+ '<(DEPTH)/frc971/wpilib/wpilib.gyp:hall_effect',
+ '<(DEPTH)/frc971/wpilib/wpilib.gyp:joystick_sender',
+ '<(DEPTH)/frc971/wpilib/wpilib.gyp:loop_output_handler',
+ '<(DEPTH)/frc971/wpilib/wpilib.gyp:buffered_pcm',
+ '<(DEPTH)/frc971/wpilib/wpilib.gyp:gyro_sender',
+ '<(DEPTH)/frc971/wpilib/wpilib.gyp:dma_edge_counting',
+ '<(DEPTH)/frc971/wpilib/wpilib.gyp:interrupt_edge_counting',
+ '<(DEPTH)/frc971/wpilib/wpilib.gyp:encoder_and_potentiometer',
+ '<(DEPTH)/frc971/control_loops/control_loops.gyp:queues',
+ '<(DEPTH)/frc971/wpilib/wpilib.gyp:logging_queue',
+ '<(DEPTH)/bot3/control_loops/drivetrain/drivetrain.gyp:drivetrain_lib',
+ ],
+ },
+ ],
+}
diff --git a/bot3/wpilib/wpilib_interface.cc b/bot3/wpilib/wpilib_interface.cc
new file mode 100644
index 0000000..49be884
--- /dev/null
+++ b/bot3/wpilib/wpilib_interface.cc
@@ -0,0 +1,363 @@
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <inttypes.h>
+
+#include <thread>
+#include <mutex>
+#include <functional>
+
+#include "aos/common/logging/logging.h"
+#include "aos/common/logging/queue_logging.h"
+#include "aos/common/time.h"
+#include "aos/common/util/log_interval.h"
+#include "aos/common/util/phased_loop.h"
+#include "aos/common/util/wrapping_counter.h"
+#include "aos/common/stl_mutex.h"
+#include "aos/linux_code/init.h"
+#include "aos/common/messages/robot_state.q.h"
+
+#include "frc971/control_loops/control_loops.q.h"
+#include "bot3/control_loops/drivetrain/drivetrain.q.h"
+
+#include "frc971/wpilib/hall_effect.h"
+#include "frc971/wpilib/joystick_sender.h"
+#include "frc971/wpilib/loop_output_handler.h"
+#include "frc971/wpilib/buffered_solenoid.h"
+#include "frc971/wpilib/buffered_pcm.h"
+#include "frc971/wpilib/gyro_sender.h"
+#include "frc971/wpilib/dma_edge_counting.h"
+#include "frc971/wpilib/interrupt_edge_counting.h"
+#include "frc971/wpilib/encoder_and_potentiometer.h"
+#include "frc971/wpilib/logging.q.h"
+#include "bot3/control_loops/drivetrain/drivetrain.h"
+
+#include "Encoder.h"
+#include "Talon.h"
+#include "DriverStation.h"
+#include "AnalogInput.h"
+#include "Compressor.h"
+#include "Relay.h"
+#include "RobotBase.h"
+#include "dma.h"
+#include "ControllerPower.h"
+
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif
+
+using ::aos::util::SimpleLogInterval;
+using ::bot3::control_loops::drivetrain_queue;
+using ::frc971::wpilib::DMAEncoderAndPotentiometer;
+using ::frc971::PotAndIndexPosition;
+using ::frc971::wpilib::InterruptEncoderAndPotentiometer;
+using ::frc971::wpilib::DMASynchronizer;
+using ::frc971::wpilib::BufferedPcm;
+using ::frc971::wpilib::LoopOutputHandler;
+using ::frc971::wpilib::JoystickSender;
+using ::frc971::wpilib::GyroSender;
+
+namespace bot3 {
+namespace wpilib {
+
+double drivetrain_translate(int32_t in) {
+ return static_cast<double>(in) /
+ (256.0 /*cpr*/ * 4.0 /*4x*/) *
+ ::bot3::control_loops::kDrivetrainEncoderRatio *
+ (4 /*wheel diameter*/ * 2.54 / 100.0 * M_PI);
+}
+
+// TODO(comran): Check/update the values below for the bot3.
+static const double kMaximumEncoderPulsesPerSecond =
+ 19500.0 /* free speed RPM */ * 12.0 / 56.0 /* belt reduction */ /
+ 60.0 /* seconds / minute */ * 256.0 /* CPR */ *
+ 4.0 /* index pulse = 1/4 cycle */;
+
+class SensorReader {
+ public:
+ SensorReader() {
+ // Set it to filter out anything shorter than 1/4 of the minimum pulse width
+ // we should ever see.
+ filter_.SetPeriodNanoSeconds(
+ static_cast<int>(1 / 4.0 / kMaximumEncoderPulsesPerSecond * 1e9 + 0.5));
+ }
+
+ void set_left_encoder(::std::unique_ptr<Encoder> left_encoder) {
+ left_encoder_ = ::std::move(left_encoder);
+ }
+
+ void set_right_encoder(::std::unique_ptr<Encoder> right_encoder) {
+ right_encoder_ = ::std::move(right_encoder);
+ }
+
+ // All of the DMA-related set_* calls must be made before this, and it doesn't
+ // hurt to do all of them.
+ // TODO(comran): Do we still need dma?
+ void set_dma(::std::unique_ptr<DMA> dma) {
+ dma_synchronizer_.reset(new DMASynchronizer(::std::move(dma)));
+ }
+
+ void operator()() {
+ LOG(INFO, "In sensor reader thread\n");
+ ::aos::SetCurrentThreadName("SensorReader");
+
+ my_pid_ = getpid();
+ ds_ = DriverStation::GetInstance();
+
+ dma_synchronizer_->Start();
+ LOG(INFO, "Things are now started\n");
+
+ ::aos::SetCurrentThreadRealtimePriority(kPriority);
+ while (run_) {
+ ::aos::time::PhasedLoopXMS(5, 4000);
+ RunIteration();
+ }
+ }
+
+ void RunIteration() {
+ {
+ auto new_state = ::aos::robot_state.MakeMessage();
+
+ new_state->reader_pid = my_pid_;
+ new_state->outputs_enabled = ds_->IsSysActive();
+ new_state->browned_out = ds_->IsSysBrownedOut();
+
+ new_state->is_3v3_active = ControllerPower::GetEnabled3V3();
+ new_state->is_5v_active = ControllerPower::GetEnabled5V();
+ new_state->voltage_3v3 = ControllerPower::GetVoltage3V3();
+ new_state->voltage_5v = ControllerPower::GetVoltage5V();
+
+ new_state->voltage_roborio_in = ControllerPower::GetInputVoltage();
+ new_state->voltage_battery = ds_->GetBatteryVoltage();
+
+ LOG_STRUCT(DEBUG, "robot_state", *new_state);
+
+ new_state.Send();
+ }
+
+ {
+ auto drivetrain_message = drivetrain_queue.position.MakeMessage();
+ drivetrain_message->right_encoder =
+ -drivetrain_translate(right_encoder_->GetRaw());
+ drivetrain_message->left_encoder =
+ drivetrain_translate(left_encoder_->GetRaw());
+
+ drivetrain_message.Send();
+ }
+
+ dma_synchronizer_->RunIteration();
+ }
+
+ void Quit() { run_ = false; }
+
+ private:
+ static const int kPriority = 30;
+ static const int kInterruptPriority = 55;
+
+ int32_t my_pid_;
+ DriverStation *ds_;
+
+ void CopyPotAndIndexPosition(
+ const DMAEncoderAndPotentiometer &encoder, PotAndIndexPosition *position,
+ ::std::function<double(int32_t)> encoder_translate,
+ ::std::function<double(double)> potentiometer_translate, bool reverse,
+ double potentiometer_offset) {
+ const double multiplier = reverse ? -1.0 : 1.0;
+ position->encoder =
+ multiplier * encoder_translate(encoder.polled_encoder_value());
+ position->pot = multiplier * potentiometer_translate(
+ encoder.polled_potentiometer_voltage()) +
+ potentiometer_offset;
+ position->latched_encoder =
+ multiplier * encoder_translate(encoder.last_encoder_value());
+ position->latched_pot =
+ multiplier *
+ potentiometer_translate(encoder.last_potentiometer_voltage()) +
+ potentiometer_offset;
+ position->index_pulses = encoder.index_posedge_count();
+ }
+
+ void CopyPotAndIndexPosition(
+ const InterruptEncoderAndPotentiometer &encoder,
+ PotAndIndexPosition *position,
+ ::std::function<double(int32_t)> encoder_translate,
+ ::std::function<double(double)> potentiometer_translate, bool reverse,
+ double potentiometer_offset) {
+ const double multiplier = reverse ? -1.0 : 1.0;
+ position->encoder =
+ multiplier * encoder_translate(encoder.encoder()->GetRaw());
+ position->pot = multiplier * potentiometer_translate(
+ encoder.potentiometer()->GetVoltage()) +
+ potentiometer_offset;
+ position->latched_encoder =
+ multiplier * encoder_translate(encoder.last_encoder_value());
+ position->latched_pot =
+ multiplier *
+ potentiometer_translate(encoder.last_potentiometer_voltage()) +
+ potentiometer_offset;
+ position->index_pulses = encoder.index_posedge_count();
+ }
+
+ ::std::unique_ptr<DMASynchronizer> dma_synchronizer_;
+
+ ::std::unique_ptr<Encoder> left_encoder_;
+ ::std::unique_ptr<Encoder> right_encoder_;
+
+ ::std::atomic<bool> run_{true};
+ DigitalGlitchFilter filter_;
+};
+
+class SolenoidWriter {
+ public:
+ SolenoidWriter(const ::std::unique_ptr<::frc971::wpilib::BufferedPcm> &pcm)
+ : pcm_(pcm) {}
+
+ void set_pressure_switch(::std::unique_ptr<DigitalSource> pressure_switch) {
+ pressure_switch_ = ::std::move(pressure_switch);
+ }
+
+ void set_compressor_relay(::std::unique_ptr<Relay> compressor_relay) {
+ compressor_relay_ = ::std::move(compressor_relay);
+ }
+
+ void operator()() {
+ ::aos::SetCurrentThreadName("Solenoids");
+ ::aos::SetCurrentThreadRealtimePriority(30);
+
+ while (run_) {
+ ::aos::time::PhasedLoopXMS(20, 1000);
+
+ ::aos::joystick_state.FetchLatest();
+
+ {
+ ::frc971::wpilib::PneumaticsToLog to_log;
+ {
+ const bool compressor_on = !pressure_switch_->Get();
+ to_log.compressor_on = compressor_on;
+ if (compressor_on) {
+ compressor_relay_->Set(Relay::kForward);
+ } else {
+ compressor_relay_->Set(Relay::kOff);
+ }
+ }
+
+ pcm_->Flush();
+ to_log.read_solenoids = pcm_->GetAll();
+ LOG_STRUCT(DEBUG, "pneumatics info", to_log);
+ }
+ }
+ }
+
+ void Quit() { run_ = false; }
+
+ private:
+ const ::std::unique_ptr<BufferedPcm> &pcm_;
+ ::std::unique_ptr<DigitalSource> pressure_switch_;
+ ::std::unique_ptr<Relay> compressor_relay_;
+
+ ::std::atomic<bool> run_{true};
+};
+
+class DrivetrainWriter : public LoopOutputHandler {
+ public:
+ void set_left_drivetrain_talon(::std::unique_ptr<Talon> t) {
+ left_drivetrain_talon_ = ::std::move(t);
+ }
+
+ void set_right_drivetrain_talon(::std::unique_ptr<Talon> t) {
+ right_drivetrain_talon_ = ::std::move(t);
+ }
+
+ private:
+ virtual void Read() override {
+ ::bot3::control_loops::drivetrain_queue.output.FetchAnother();
+ }
+
+ virtual void Write() override {
+ auto &queue = ::bot3::control_loops::drivetrain_queue.output;
+ LOG_STRUCT(DEBUG, "will output", *queue);
+ left_drivetrain_talon_->Set(queue->left_voltage / 12.0);
+ right_drivetrain_talon_->Set(-queue->right_voltage / 12.0);
+ }
+
+ virtual void Stop() override {
+ LOG(WARNING, "drivetrain output too old\n");
+ left_drivetrain_talon_->Disable();
+ right_drivetrain_talon_->Disable();
+ }
+
+ ::std::unique_ptr<Talon> left_drivetrain_talon_;
+ ::std::unique_ptr<Talon> right_drivetrain_talon_;
+};
+
+// TODO(brian): Replace this with ::std::make_unique once all our toolchains
+// have support.
+template <class T, class... U>
+std::unique_ptr<T> make_unique(U &&... u) {
+ return std::unique_ptr<T>(new T(std::forward<U>(u)...));
+}
+
+class WPILibRobot : public RobotBase {
+ public:
+ ::std::unique_ptr<Encoder> encoder(int index) {
+ return make_unique<Encoder>(10 + index * 2, 11 + index * 2, false,
+ Encoder::k4X);
+ }
+ virtual void StartCompetition() {
+ ::aos::InitNRT();
+ ::aos::SetCurrentThreadName("StartCompetition");
+
+ JoystickSender joystick_sender;
+ ::std::thread joystick_thread(::std::ref(joystick_sender));
+
+ SensorReader reader;
+ LOG(INFO, "Creating the reader\n");
+
+ // TODO(comran): Find talon/encoder numbers.
+ reader.set_left_encoder(encoder(2));
+ reader.set_right_encoder(encoder(3));
+ reader.set_dma(make_unique<DMA>());
+ ::std::thread reader_thread(::std::ref(reader));
+ GyroSender gyro_sender;
+ ::std::thread gyro_thread(::std::ref(gyro_sender));
+
+ DrivetrainWriter drivetrain_writer;
+ drivetrain_writer.set_left_drivetrain_talon(
+ ::std::unique_ptr<Talon>(new Talon(8)));
+ drivetrain_writer.set_right_drivetrain_talon(
+ ::std::unique_ptr<Talon>(new Talon(0)));
+ ::std::thread drivetrain_writer_thread(::std::ref(drivetrain_writer));
+
+ ::std::unique_ptr<::frc971::wpilib::BufferedPcm> pcm(
+ new ::frc971::wpilib::BufferedPcm());
+ SolenoidWriter solenoid_writer(pcm);
+ solenoid_writer.set_pressure_switch(make_unique<DigitalInput>(9));
+ solenoid_writer.set_compressor_relay(make_unique<Relay>(0));
+ ::std::thread solenoid_thread(::std::ref(solenoid_writer));
+
+ // Wait forever. Not much else to do...
+ PCHECK(select(0, nullptr, nullptr, nullptr, nullptr));
+
+ LOG(ERROR, "Exiting WPILibRobot\n");
+
+ joystick_sender.Quit();
+ joystick_thread.join();
+ reader.Quit();
+ reader_thread.join();
+ gyro_sender.Quit();
+ gyro_thread.join();
+
+ drivetrain_writer.Quit();
+ drivetrain_writer_thread.join();
+ solenoid_writer.Quit();
+ solenoid_thread.join();
+
+ ::aos::Cleanup();
+ }
+};
+
+} // namespace wpilib
+} // namespace bot3
+
+
+START_ROBOT_CLASS(::bot3::wpilib::WPILibRobot);
diff --git a/doc/building-the-code.txt b/doc/building-the-code.txt
new file mode 100644
index 0000000..c2676d1
--- /dev/null
+++ b/doc/building-the-code.txt
@@ -0,0 +1,43 @@
+This file contains instructions on how to set up a computer to build the code.
+
+[OS]
+Most of these instructions assume 64 bit Debian Wheezy.
+
+[Install Packages]
+First, you have to download and follow the directions in
+ <http://robotics.mvla.net/files/frc971/packages/frc971.list>.
+Then, run `apt-get install python3`.
+The build script will tell you what other packages to install when you run it.
+ It's pretty smart about not checking for things it doesn't need, so you might
+ want to build 'deploy all' to see everything it wants.
+ You will have to accept the
+ "WARNING: The following packages cannot be authenticated!" warning for
+ various packages downloaded from our package repository.
+ This works for amd64 Wheezy, no guarantees or support for anything else.
+
+[Running Locally]
+If you want to be able to run the realtime code on your development machine
+ without just disabling the realtime part (set the AOS_NO_REALTIME environment
+ variable), follow the directions in //aos/config/aos.conf.
+If you want to run the clang or gcc_4.8 amd64 code, add /opt/clang-3.5/lib64/ to
+ LD_LIBRARY_PATH. Using the system gcc-compiled versions should just work.
+
+[Compiling and Downloading]
+Run //frc971/*/build.sh.
+ Give it clean, tests, or deploy as a first argument to do something other
+ than just build.
+ Each action (build (the default), clean, tests, or deploy) has a different
+ default set of versions of the code it builds. You can change those by
+ passing another argument. Some popular ones are 'all' (build everything),
+ 'clang-amd64-none' (the most basic one for local testing), and
+ 'gcc_frc-arm-nodebug' (the one that gets downloaded). See its --help for
+ more details.
+
+[Communicating with the cRIO]
+Use netconsole (in the amd64 outputs directories) to communicate directly with
+ the cRIO.
+Use "reboot" from within netconsole.sh will reboot the cRIO. ^C will
+ stop the netconsole program. "version" will tell you the the
+ VxWorks and WIND versions and "help" will give you a list of VxWorks
+ commands that you can use.
+Make sure your computer is on the right subnet first.
diff --git a/doc/clang-3.5.ctl b/doc/clang-3.5.ctl
new file mode 100644
index 0000000..b20c52a
--- /dev/null
+++ b/doc/clang-3.5.ctl
@@ -0,0 +1,13 @@
+Source: llvm-toolchain-snapshot
+Section: misc
+Priority: optional
+Homepage: http://www.llvm.org/
+Standards-Version: 3.9.2
+Package: clang-3.5
+Version: 1:3.5~svn201561
+Maintainer: FRC Team 971 <spartanrobotics.org>
+Depends: libbsd0, libc6, libedit2, libncurses5, libpython2.7, libtinfo5, zlib1g
+Architecture: amd64
+Description: Clang 3.5 with included GCC 4.8 so it actually works.
+ This is just 1 massive package with a newer clang that doesn't conflict with
+ other stuff like the official clang-3.5 package does.
diff --git a/doc/codereview-directions.txt b/doc/codereview-directions.txt
new file mode 100644
index 0000000..4610c95
--- /dev/null
+++ b/doc/codereview-directions.txt
@@ -0,0 +1,56 @@
+This file has some general information about how 971 does code reviews.
+
+We use Rietveld (<https://code.google.com/p/rietveld>) running in an app engine
+ app of ours. It is at <http://971code.appspot.com/>.
+
+[General Procedure]
+We try to code review all code before it gets checked in to svn. Code reviews
+ are also sometimes useful for getting feedback about documentation etc.
+First, somebody starts a code review and sends out emails to people asking them
+ to look. Then, other people look at it and make suggestions (in the form of
+ comments). The person who started the review (the owner of it) looks at the
+ comments and responds to them and/or changes their code and uploads new
+ versions for everybody to look at. Once all of the reviewers approve the code,
+ the owner checks it in and closes the code review (issue).
+
+[Reviewing]
+This is the section for people who have received emails about reviewing code
+ should look.
+The email that you receive will have a link to the issue (like
+ <http://971code.appspot.com/82001/>). Click on it to go to the issue page.
+ The most useful form of diff are the "Side-by-side diffs". Click the "View"
+ link in that column next to each file to look at it. After the first set of
+ comments, the "Delta from patch set" links are also helpful to see what got
+ changed so that you don't have to look at everything again. While looking at
+ a diff, double-click on any line of code to leave a comment there. You can
+ also reply to existing comments. The web interface sometimes hides comments
+ right after you create/edit them. Refresh the page to fix that (don't just do
+ it again, or you'll end up with 2 identical comments). Once you are done
+ looking at all of the files and making comments, click the
+ "Publish+Mail Comments" link (at the top of each diff or on the left of the
+ main issue page) to send out your comments. You can also put general notes in
+ the "Message" box. If you think that it looks good, then put "LGTM" at the top
+ of the message. The owner of the code review will keep looking at your
+ comments, making changes, and sending out more messages until it's finished.
+
+[Starting a Review]
+To begin, log in to <http://971code.appspot.com/> then click the "Create Issue"
+ link. Download the "upload.py" script and use that, not the web form, for
+ uploading a diff. (The web form has too many problems and is unusable with
+ git.) The script will find svn, git, or hg diffs in your current directory. If
+ you want the diff to cover back to an earlier change number, use the --rev
+ arg.
+
+ You don't need to give it a -s (--server) arg since the right default server
+ address is in the script. You can give it a -e (--email) arg with your gmail
+ address or let it ask you. (If you use 2-factor login for gmail, which I
+ recommend, you'll need to create an application-specific password instead of
+ using your regular password + OTP code.)
+
+ After it uploads, you can review the diffs on the web page. You can change
+ code and upload another diff. When ready, use the web UI to send the code
+ review email. Don't delete the old diffs; they are helpful for reviewers to
+ figure out what changed.
+
+For more information about using Rietveld, see
+ <https://code.google.com/p/rietveld/wiki/CodeReviewHelp>.
diff --git a/doc/environment_variables b/doc/environment_variables
new file mode 100644
index 0000000..98d51bc
--- /dev/null
+++ b/doc/environment_variables
@@ -0,0 +1,4 @@
+A list of environment variables useful for running the code on not-robots:
+ AOS_NO_REALTIME
+ AOS_SHM_NAME
+ AOS_TEAM_NUMBER
diff --git a/doc/git-setup.txt b/doc/git-setup.txt
new file mode 100644
index 0000000..6fc52b5
--- /dev/null
+++ b/doc/git-setup.txt
@@ -0,0 +1,58 @@
+Some of us are using git for managing quickly changing code. This file has
+notes for setting that up.
+
+[Cloning]
+`git clone \
+ ssh://USERNAME@robotics.mvla.net/www/https/git/frc971/SOMEBODY/2014.git`
+ where USERNAME is your login on the server and SOMEBODY is whoever's git
+ repo you want to clone
+If you don't have a login on the server, then cloning
+ https://robotics.mvla.net/git/frc971/somebody/2014.git instead should work
+ (with your SVN username and password). However, that form of URL is read-
+ only. In order for this to work, you have to either set the environment
+ variable GIT_SSL_NO_VERIFY to 1 or set the git option http.sslverify to false.
+ If you get an error like the one below, install a newer version of git.
+ Unable to find 8fcd87533b76ca5b4b83fb6031ddf0de5e03eb57 under https://robotics.mvla.net/git/frc971/brian/2013.git
+ Cannot obtain needed object 8fcd87533b76ca5b4b83fb6031ddf0de5e03eb57
+ error: Fetch failed.
+ I get this error sometimes with version 1.7.2.5 but not with version 1.7.10.4.
+
+[Adding Other People's Repositories]
+`git remote add SOMEBODY \
+ ssh://USERNAME@robotics.mvla.net/www/https/git/frc971/SOMEBODY/2014.git`
+ where USERNAME is your login on the server and SOMEBODY is another person's
+ git repository
+The https:// URL discussed above in [Cloning] will work too.
+
+[Working with Other People's Repositories]
+`git fetch SOMEBODY` will pull their changes, and then you can rebase on top of
+ them, merge them in, etc.
+`git push --mirror` will push all of your local branches so that everybody else
+ can see them. (This is the default if the configuration option remote.<remote>.mirror is set.)
+ However, you can not do that with an https:// URL.
+
+[Synchronizing with SVN]
+Somebody should occasionally copy all of the files from git (bbb_cape,
+ frc971, and aos) into svn.
+
+[Server Setup]
+You need a place to store your files. You will need an adminstrator to
+ create a folder for you in /www/https/git/frc971 (on the server) with the
+ correct permissions and group.
+
+[Repository Setup]
+To create a git repository on the server,
+ `git init --bare 2014.git` in your folder (ie
+ "/www/https/git/frc971/USERNAME/"), `cd 2014.git`,
+ `git config --local gc.pruneExpire never`, `rm -r objects/`, and then
+ `ln -s /www/https/git/frc971/objects/2014 objects`. That will set you up
+ with a repository in the standard location and make it so all of the objects
+ in it won't be duplicated with the identical ones in everybody else's
+ repositories.
+In order for https:// access to work, you have to make sure to rename
+ .git/hooks/post-update.sample to .git/hooks/post-update (and then run
+ `git update-server-info` if you're not going to push immediately).
+
+To learn more about git, see git(1) (`man git` or
+ <http://manpages.debian.net/cgi-bin/man.cgi?query=git>) (especially the NOTES
+ section).
diff --git a/frc971/analysis/analysis.py b/frc971/analysis/analysis.py
new file mode 100755
index 0000000..e477bdd
--- /dev/null
+++ b/frc971/analysis/analysis.py
@@ -0,0 +1,377 @@
+#!/usr/bin/python3
+import matplotlib
+from matplotlib import pylab
+from matplotlib.font_manager import FontProperties
+
+class Dataset(object):
+ def __init__(self):
+ self.time = []
+ self.data = []
+
+ def Add(self, time, data):
+ self.time.append(time)
+ self.data.append(data)
+
+
+class Plotter(object):
+ def __init__(self):
+ self.signal = dict()
+
+ def Add(self, binary, struct_instance_name, *data_search_path):
+ """
+ Specifies a specific piece of data to plot
+
+ Args:
+ binary: str, The name of the executable that generated the log.
+ struct_instance_name: str, The name of the struct instance whose data
+ contents should be plotted.
+ data_search_path: [str], The path into the struct of the exact piece of
+ data to plot.
+
+ Returns:
+ None
+ """
+ self.signal[(binary, struct_instance_name, data_search_path)] = Dataset()
+
+ def HandleLine(self, line):
+ """
+ Parses a line from a log file and adds the data to the plot data.
+
+ Args:
+ line: str, The line from the log file to parse
+
+ Returns:
+ None
+ """
+ pline = ParseLine(line)
+ for key in self.signal:
+ value = self.signal[key]
+ binary = key[0]
+ struct_instance_name = key[1]
+ data_search_path = key[2]
+ boolean_multiplier = None
+
+ # If the plot definition line ends with a "-b X" where X is a number then
+ # that number gets drawn when the value is True. Zero gets drawn when the
+ # value is False.
+ if len(data_search_path) >= 2 and data_search_path[-2] == '-b':
+ boolean_multiplier = float(data_search_path[-1])
+ data_search_path = data_search_path[:-2]
+
+ # Make sure that we're looking at the right binary structure instance.
+ if binary == pline.name:
+ if pline.msg.startswith(struct_instance_name + ': '):
+ # Parse the structure and traverse it as specified in
+ # `data_search_path`. This lets the user access very deeply nested
+ # structures.
+ _, _, data = pline.ParseStruct()
+ for path in data_search_path:
+ data = data[path]
+
+ if boolean_multiplier is not None:
+ if data == 'T':
+ value.Add(pline.time, boolean_multiplier)
+ else:
+ value.Add(pline.time, 0)
+ else:
+ value.Add(pline.time, data)
+
+ def Plot(self, no_binary_in_legend):
+ """
+ Plots all the data after it's parsed.
+
+ This should only be called after `HandleFile` has been called so that there
+ is actual data to plot.
+ """
+ for key in self.signal:
+ value = self.signal[key]
+
+ # Create a legend label using the binary name (optional), the structure
+ # name and the data search path.
+ label = key[1] + '.' + '.'.join(key[2])
+ if not no_binary_in_legend:
+ label = key[0] + ' ' + label
+
+ pylab.plot(value.time, value.data, label=label)
+
+ # Set legend font size to small and move it to the top center.
+ fontP = FontProperties()
+ fontP.set_size('small')
+ pylab.legend(bbox_to_anchor=(0.5, 1.05), prop=fontP)
+
+ pylab.show()
+
+ def PlotFile(self, f, no_binary_in_legend=False):
+ """
+ Parses and plots all the data.
+
+ Args:
+ f: str, The filename of the log whose data to parse and plot.
+
+ Returns:
+ None
+ """
+ self.HandleFile(f)
+ self.Plot(no_binary_in_legend)
+
+ def HandleFile(self, f):
+ """
+ Parses the specified log file.
+
+ Args:
+ f: str, The filename of the log whose data to parse.
+
+ Returns:
+ None
+ """
+ with open(f, 'r') as fd:
+ for line in fd:
+ self.HandleLine(line)
+
+
+class LogEntry:
+ """This class provides a way to parse log entries."""
+
+ def __init__(self, line):
+ """Creates a LogEntry from a line."""
+ name_index = line.find('(')
+ self.name = line[0:name_index]
+
+ pid_index = line.find(')', name_index + 1)
+ self.pid = int(line[name_index + 1:pid_index])
+
+ msg_index_index = line.find(')', pid_index + 1)
+ self.msg_index = int(line[pid_index + 2:msg_index_index])
+
+ level_index = line.find(' ', msg_index_index + 3)
+ self.level = line[msg_index_index + 3:level_index]
+
+ time_index_start = line.find(' at ', level_index) + 4
+ time_index_end = line.find('s:', level_index)
+ self.time = float(line[time_index_start:time_index_end])
+
+ filename_end = line.find(':', time_index_end + 3)
+ self.filename = line[time_index_end + 3:filename_end]
+
+ linenumber_end = line.find(':', filename_end + 2)
+ self.linenumber = int(line[filename_end + 2:linenumber_end])
+
+ self.msg = line[linenumber_end+2:]
+
+ def __str__(self):
+ """Formats the data cleanly."""
+ return '%s(%d)(%d): %s at %fs: %s: %d: %s' % (self.name, self.pid, self.msg_index, self.level, self.time, self.filename, self.linenumber, self.msg)
+
+ def __JsonizeTokenArray(self, sub_array, tokens, token_index):
+ """Parses an array from the provided tokens.
+
+ Args:
+ sub_array: list, The list to stick the elements in.
+ tokens: list of strings, The list with all the tokens in it.
+ token_index: int, Where to start in the token list.
+
+ Returns:
+ int, The last token used.
+ """
+ # Make sure the data starts with a '['
+ if tokens[token_index] != '[':
+ print(tokens)
+ print('Expected [ at beginning, found', tokens[token_index + 1])
+ return None
+
+ # Eat the '['
+ token_index += 1
+
+ # Loop through the tokens.
+ while token_index < len(tokens):
+ if tokens[token_index + 1] == ',':
+ # Next item is a comma, so we should just add the element.
+ sub_array.append(tokens[token_index])
+ token_index += 2
+ elif tokens[token_index + 1] == ']':
+ # Next item is a ']', so we should just add the element and finish.
+ sub_array.append(tokens[token_index])
+ token_index += 1
+ return token_index
+ else:
+ # Otherwise, it must be a sub-message.
+ sub_json = dict()
+ token_index = self.JsonizeTokens(sub_json, tokens, token_index + 1)
+ sub_array.append(sub_json)
+ if tokens[token_index] == ',':
+ # Handle there either being another data element.
+ token_index += 1
+ elif tokens[token_index] == ']':
+ # Handle the end of the array.
+ return token_index
+ else:
+ print('Unexpected ', tokens[token_index])
+ return None
+
+ print('Unexpected end')
+ return None
+
+ def JsonizeTokens(self, json, tokens, token_index):
+ """Creates a json-like dictionary from the provided tokens.
+
+ Args:
+ json: dict, The dict to stick the elements in.
+ tokens: list of strings, The list with all the tokens in it.
+ token_index: int, Where to start in the token list.
+
+ Returns:
+ int, The last token used.
+ """
+ # Check that the message starts with a {
+ if tokens[token_index] != '{':
+ print(tokens)
+ print('Expected { at beginning, found', tokens[token_index])
+ return None
+
+ # Eat the {
+ token_index += 1
+
+ # States and state variable for parsing elements.
+ STATE_INIT = 'init'
+ STATE_HAS_NAME = 'name'
+ STATE_HAS_COLON = 'colon'
+ STATE_EXPECTING_SUBMSG = 'submsg'
+ STATE_EXPECTING_COMMA = 'comma'
+ parser_state = STATE_INIT
+
+ while token_index < len(tokens):
+ if tokens[token_index] == '}':
+ # Finish if there is a }
+ return token_index + 1
+ elif tokens[token_index] == '{':
+ if parser_state != STATE_EXPECTING_SUBMSG:
+ print(tokens)
+ print(parser_state)
+ print('Bad input, was not expecting {')
+ return None
+ # Found a submessage, parse it.
+ sub_json = dict()
+ token_index = self.JsonizeTokens(sub_json, tokens, token_index)
+ json[token_name] = sub_json
+ parser_state = STATE_EXPECTING_COMMA
+ else:
+ if parser_state == STATE_INIT:
+ # This token is the name.
+ token_name = tokens[token_index]
+ parser_state = STATE_HAS_NAME
+ elif parser_state == STATE_HAS_NAME:
+ if tokens[token_index] != ':':
+ print(tokens)
+ print(parser_state)
+ print('Bad input, found', tokens[token_index], 'expected :')
+ return None
+ # After a name, comes a :
+ parser_state = STATE_HAS_COLON
+ elif parser_state == STATE_HAS_COLON:
+ # After the colon, figure out what is next.
+ if tokens[token_index] == '[':
+ # Found a sub-array!
+ sub_array = []
+ token_index = self.__JsonizeTokenArray(sub_array, tokens, token_index)
+ json[token_name] = sub_array
+ parser_state = STATE_EXPECTING_COMMA
+ elif tokens[token_index + 1] == '{':
+ # Found a sub-message, trigger parsing it.
+ parser_state = STATE_EXPECTING_SUBMSG
+ else:
+ # This is just an element, move on.
+ json[token_name] = tokens[token_index]
+ parser_state = STATE_EXPECTING_COMMA
+ elif parser_state == STATE_EXPECTING_COMMA:
+ # Complain if there isn't a comma here.
+ if tokens[token_index] != ',':
+ print(tokens)
+ print(parser_state)
+ print('Bad input, found', tokens[token_index], 'expected ,')
+ return None
+ parser_state = STATE_INIT
+ else:
+ print('Bad parser state')
+ return None
+ token_index += 1
+
+ print('Unexpected end')
+ return None
+
+ def ParseStruct(self):
+ """Parses the message as a structure.
+
+ Returns:
+ struct_name, struct_type, json dict.
+ """
+ struct_name_index = self.msg.find(':')
+ struct_name = self.msg[0:struct_name_index]
+
+ struct_body = self.msg[struct_name_index+2:]
+ tokens = []
+ this_token = ''
+ # For the various deliminators, append what we have found so far to the
+ # list and the token.
+ for char in struct_body:
+ if char == '{':
+ if this_token:
+ tokens.append(this_token)
+ this_token = ''
+ tokens.append('{')
+ elif char == '}':
+ if this_token:
+ tokens.append(this_token)
+ this_token = ''
+ tokens.append('}')
+ elif char == '[':
+ if this_token:
+ tokens.append(this_token)
+ this_token = ''
+ tokens.append('[')
+ elif char == ']':
+ if this_token:
+ tokens.append(this_token)
+ this_token = ''
+ tokens.append(']')
+ elif char == ':':
+ if this_token:
+ tokens.append(this_token)
+ this_token = ''
+ tokens.append(':')
+ elif char == ',':
+ if this_token:
+ tokens.append(this_token)
+ this_token = ''
+ tokens.append(',')
+ elif char == ' ':
+ if this_token:
+ tokens.append(this_token)
+ this_token = ''
+ else:
+ this_token += char
+ if this_token:
+ tokens.append(this_token)
+
+ struct_type = tokens[0]
+ json = dict()
+ # Now that we have tokens, parse them.
+ self.JsonizeTokens(json, tokens, 1)
+
+ return (struct_name, struct_type, json)
+
+
+def ParseLine(line):
+ return LogEntry(line)
+
+if __name__ == '__main__':
+ print('motor_writer(2240)(07421): DEBUG at 0000000819.99620s: ../../frc971/output/motor_writer.cc: 105: sending: .aos.controls.OutputCheck{pwm_value:221, pulse_length:2.233333}')
+ line = ParseLine('motor_writer(2240)(07421): DEBUG at 0000000819.99620s: ../../frc971/output/motor_writer.cc: 105: sending: .aos.controls.OutputCheck{pwm_value:221, pulse_length:2.233333}')
+ if '.aos.controls.OutputCheck' in line.msg:
+ print(line)
+ print(line.ParseStruct())
+
+ line = ParseLine('claw(2263)(19404): DEBUG at 0000000820.00000s: ../../aos/common/controls/control_loop-tmpl.h: 104: position: .frc971.control_loops.ClawGroup.Position{top:.frc971.control_loops.HalfClawPosition{position:1.672153, front:.frc971.HallEffectStruct{current:f, posedge_count:0, negedge_count:52}, calibration:.frc971.HallEffectStruct{current:f, posedge_count:6, negedge_count:13}, back:.frc971.HallEffectStruct{current:f, posedge_count:0, negedge_count:62}, posedge_value:0.642681, negedge_value:0.922207}, bottom:.frc971.control_loops.HalfClawPosition{position:1.353539, front:.frc971.HallEffectStruct{current:f, posedge_count:2, negedge_count:150}, calibration:.frc971.HallEffectStruct{current:f, posedge_count:8, negedge_count:18}, back:.frc971.HallEffectStruct{current:f, posedge_count:0, negedge_count:6}, posedge_value:0.434514, negedge_value:0.759491}}')
+ print(line.ParseStruct())
+
+ line = ParseLine('joystick_proxy(2255)(39560): DEBUG at 0000000820.00730s: ../../aos/prime/input/joystick_input.cc: 61: sending: .aos.RobotState{joysticks:[.aos.Joystick{buttons:0, axis:[0.000000, 1.000000, 1.000000, 0.000000]}, .aos.Joystick{buttons:0, axis:[-0.401575, 1.000000, -1.007874, 0.000000]}, .aos.Joystick{buttons:0, axis:[0.007874, 0.000000, 1.000000, -1.007874]}, .aos.Joystick{buttons:0, axis:[0.000000, 0.000000, 0.000000, 0.000000]}], test_mode:f, fms_attached:f, enabled:T, autonomous:f, team_id:971, fake:f}')
+ print(line.ParseStruct())
diff --git a/frc971/analysis/plot_action.py b/frc971/analysis/plot_action.py
new file mode 100755
index 0000000..2f50ffd
--- /dev/null
+++ b/frc971/analysis/plot_action.py
@@ -0,0 +1,88 @@
+#!/usr/bin/python3
+
+import sys
+import numpy
+import analysis
+import argparse
+
+def ReadPlotDefinitions(filename):
+ """
+ Read a file with plotting definitions.
+
+ A plotting definition is a single line that defines what data to search for
+ in order to plot it. The following in a file would duplicate the default
+ behaviour:
+
+ fridge goal height
+ fridge goal angle
+ fridge goal velocity
+ fridge goal angular_velocity
+ fridge output left_arm
+ fridge output right_arm
+ fridge output left_elevator
+ fridge output right_elevator
+
+ Lines are ignored if they start with a hash mark (i.e. '#').
+
+ Lines that end with a "-b X" where X is a number then it designates that line
+ as plotting a boolean value. X is the value plotted when the boolean is true.
+ When the boolean is false then the values is plotted as zero. For example,
+ the following boolean value is drawn to toggle between 2.0 and 0 when the
+ boolean is True and False, respectively:
+
+ fridge status zeroed -b 2.0
+
+ Args:
+ filename: The name of the file to read the definitions from.
+
+ Returns:
+ [[str]]: The definitions in the specified file.
+ """
+ defs = []
+ with open(filename) as fd:
+ for line in fd:
+ raw_defs = line.split()
+
+ # Only add to the list of definitions if the line's not empty and it
+ # doesn't start with a hash.
+ if raw_defs and not raw_defs[0].startswith('#'):
+ defs.append(raw_defs)
+
+ return defs
+
+def main():
+ # Parse all command line arguments.
+ arg_parser = argparse.ArgumentParser(description='Log Plotter')
+ arg_parser.add_argument('log_file', metavar='LOG_FILE', type=str, \
+ help='The file from which to read logs and plot.')
+ arg_parser.add_argument('--plot-defs', '-p', action='store', type=str, \
+ help='Read the items to plot from this file.')
+ arg_parser.add_argument('--no-binary', '-n', action='store_true', \
+ help='Don\'t print the binary name in the legend.')
+
+ args = arg_parser.parse_args(sys.argv[1:])
+
+ p = analysis.Plotter()
+
+ # If the user defines the list of data to plot in a file, read it from there.
+ if args.plot_defs:
+ defs = ReadPlotDefinitions(args.plot_defs)
+ for definition in defs:
+ p.Add(definition[0], definition[1], *definition[2:])
+
+ # Otherwise use a pre-defined set of data to plot.
+ else:
+ p.Add('fridge', 'goal', 'height')
+ p.Add('fridge', 'goal', 'angle')
+ p.Add('fridge', 'goal', 'velocity')
+ p.Add('fridge', 'goal', 'angular_velocity')
+
+ p.Add('fridge', 'output', 'left_arm')
+ p.Add('fridge', 'output', 'right_arm')
+ p.Add('fridge', 'output', 'left_elevator')
+ p.Add('fridge', 'output', 'right_elevator')
+
+ p.PlotFile(args.log_file, args.no_binary)
+
+if __name__ == '__main__':
+ main()
diff --git a/frc971/autonomous/auto.q b/frc971/autonomous/auto.q
new file mode 100644
index 0000000..5791034
--- /dev/null
+++ b/frc971/autonomous/auto.q
@@ -0,0 +1,8 @@
+package frc971.autonomous;
+
+message AutoControl {
+ // True if auto mode should be running, false otherwise.
+ bool run_auto;
+};
+
+queue AutoControl autonomous;
diff --git a/frc971/autonomous/autonomous.gyp b/frc971/autonomous/autonomous.gyp
new file mode 100644
index 0000000..e36a532
--- /dev/null
+++ b/frc971/autonomous/autonomous.gyp
@@ -0,0 +1,13 @@
+{
+ 'targets': [
+ {
+ 'target_name': 'auto_queue',
+ 'type': 'static_library',
+ 'sources': ['auto.q'],
+ 'variables': {
+ 'header_path': 'frc971/autonomous',
+ },
+ 'includes': ['../../aos/build/queues.gypi'],
+ },
+ ],
+}
diff --git a/frc971/constants.h b/frc971/constants.h
new file mode 100644
index 0000000..890c63c
--- /dev/null
+++ b/frc971/constants.h
@@ -0,0 +1,22 @@
+#ifndef FRC971_CONSTANTS_H_
+#define FRC971_CONSTANTS_H_
+
+namespace frc971 {
+namespace constants {
+
+struct ZeroingConstants {
+ // The number of samples in the moving average filter.
+ int average_filter_size;
+ // The difference in scaled units between two index pulses.
+ double index_difference;
+ // The absolute position in scaled units of one of the index pulses.
+ double measured_index_position;
+ // Value between 0 and 1 which determines a fraction of the index_diff
+ // you want to use.
+ double allowable_encoder_error;
+};
+
+} // namespace constants
+} // namespace frc971
+
+#endif // FRC971_CONSTANTS_H_
diff --git a/frc971/control_loops/coerce_goal.cc b/frc971/control_loops/coerce_goal.cc
new file mode 100644
index 0000000..6482cc7
--- /dev/null
+++ b/frc971/control_loops/coerce_goal.cc
@@ -0,0 +1,63 @@
+#include "frc971/control_loops/coerce_goal.h"
+
+#include "Eigen/Dense"
+
+#include "aos/common/controls/polytope.h"
+
+namespace frc971 {
+namespace control_loops {
+
+Eigen::Matrix<double, 2, 1> DoCoerceGoal(const aos::controls::HPolytope<2> ®ion,
+ const Eigen::Matrix<double, 1, 2> &K,
+ double w,
+ const Eigen::Matrix<double, 2, 1> &R,
+ bool *is_inside) {
+ if (region.IsInside(R)) {
+ if (is_inside) *is_inside = true;
+ return R;
+ }
+ Eigen::Matrix<double, 2, 1> parallel_vector;
+ Eigen::Matrix<double, 2, 1> perpendicular_vector;
+ perpendicular_vector = K.transpose().normalized();
+ parallel_vector << perpendicular_vector(1, 0), -perpendicular_vector(0, 0);
+
+ aos::controls::HPolytope<1> t_poly(
+ region.H() * parallel_vector,
+ region.k() - region.H() * perpendicular_vector * w);
+
+ Eigen::Matrix<double, 1, Eigen::Dynamic> vertices = t_poly.Vertices();
+ if (vertices.innerSize() > 0) {
+ double min_distance_sqr = 0;
+ Eigen::Matrix<double, 2, 1> closest_point;
+ for (int i = 0; i < vertices.innerSize(); i++) {
+ Eigen::Matrix<double, 2, 1> point;
+ point = parallel_vector * vertices(0, i) + perpendicular_vector * w;
+ const double length = (R - point).squaredNorm();
+ if (i == 0 || length < min_distance_sqr) {
+ closest_point = point;
+ min_distance_sqr = length;
+ }
+ }
+ if (is_inside) *is_inside = true;
+ return closest_point;
+ } else {
+ Eigen::Matrix<double, 2, Eigen::Dynamic> region_vertices =
+ region.Vertices();
+ double min_distance = INFINITY;
+ int closest_i = 0;
+ for (int i = 0; i < region_vertices.outerSize(); i++) {
+ const double length = ::std::abs(
+ (perpendicular_vector.transpose() * (region_vertices.col(i)))(0, 0));
+ if (i == 0 || length < min_distance) {
+ closest_i = i;
+ min_distance = length;
+ }
+ }
+ if (is_inside) *is_inside = false;
+ return (Eigen::Matrix<double, 2, 1>() << region_vertices(0, closest_i),
+ region_vertices(1, closest_i)).finished();
+ }
+}
+
+} // namespace control_loops
+} // namespace frc971
diff --git a/frc971/control_loops/coerce_goal.h b/frc971/control_loops/coerce_goal.h
new file mode 100644
index 0000000..3933050
--- /dev/null
+++ b/frc971/control_loops/coerce_goal.h
@@ -0,0 +1,32 @@
+#ifndef FRC971_CONTROL_LOOPS_COERCE_GOAL_H_
+#define FRC971_CONTROL_LOOPS_COERCE_GOAL_H_
+
+#include "Eigen/Dense"
+
+#include "aos/common/controls/polytope.h"
+
+namespace frc971 {
+namespace control_loops {
+
+Eigen::Matrix<double, 2, 1> DoCoerceGoal(const aos::controls::HPolytope<2> ®ion,
+ const Eigen::Matrix<double, 1, 2> &K,
+ double w,
+ const Eigen::Matrix<double, 2, 1> &R,
+ bool *is_inside);
+
+// Intersects a line with a region, and finds the closest point to R.
+// Finds a point that is closest to R inside the region, and on the line
+// defined by K X = w. If it is not possible to find a point on the line,
+// finds a point that is inside the region and closest to the line.
+static inline Eigen::Matrix<double, 2, 1>
+ CoerceGoal(const aos::controls::HPolytope<2> ®ion,
+ const Eigen::Matrix<double, 1, 2> &K,
+ double w,
+ const Eigen::Matrix<double, 2, 1> &R) {
+ return DoCoerceGoal(region, K, w, R, nullptr);
+}
+
+} // namespace control_loops
+} // namespace frc971
+
+#endif // FRC971_CONTROL_LOOPS_COERCE_GOAL_H_
diff --git a/frc971/control_loops/control_loops.gyp b/frc971/control_loops/control_loops.gyp
new file mode 100644
index 0000000..ff62bac
--- /dev/null
+++ b/frc971/control_loops/control_loops.gyp
@@ -0,0 +1,112 @@
+{
+ 'targets': [
+ {
+ 'target_name': 'team_number_test_environment',
+ 'type': 'static_library',
+ 'sources': [
+ 'team_number_test_environment.cc'
+ ],
+ 'dependencies': [
+ '<(AOS)/common/network/network.gyp:team_number',
+ '<(EXTERNALS):gtest',
+ ],
+ 'export_dependent_settings': [
+ '<(EXTERNALS):gtest',
+ ],
+ },
+ {
+ 'target_name': 'state_feedback_loop_test',
+ 'type': 'executable',
+ 'sources': [
+ 'state_feedback_loop_test.cc',
+ ],
+ 'dependencies': [
+ 'state_feedback_loop',
+ '<(EXTERNALS):gtest',
+ ],
+ },
+ {
+ 'target_name': 'hall_effect_tracker',
+ 'type': 'static_library',
+ 'sources': [
+ #'hall_effect_tracker.h',
+ ],
+ 'dependencies': [
+ 'queues',
+ ],
+ 'export_dependent_settings': [
+ 'queues',
+ ],
+ },
+ {
+ 'target_name': 'queues',
+ 'type': 'static_library',
+ 'sources': [
+ 'control_loops.q',
+ ],
+ 'variables': {
+ 'header_path': 'frc971/control_loops',
+ },
+ 'includes': ['../../aos/build/queues.gypi'],
+ },
+ {
+ 'target_name': 'position_sensor_sim_test',
+ 'type': 'executable',
+ 'sources': [
+ 'position_sensor_sim_test.cc',
+ ],
+ 'dependencies': [
+ 'queues',
+ 'position_sensor_sim',
+ '<(EXTERNALS):gtest',
+ '<(AOS)/build/aos.gyp:logging',
+ ],
+ },
+ {
+ 'target_name': 'position_sensor_sim',
+ 'type': 'static_library',
+ 'sources': [
+ 'position_sensor_sim.cc',
+ ],
+ 'dependencies': [
+ 'queues',
+ 'gaussian_noise',
+ ],
+ },
+ {
+ 'target_name': 'gaussian_noise',
+ 'type': 'static_library',
+ 'sources': [
+ 'gaussian_noise.cc',
+ ],
+ },
+ {
+ 'target_name': 'coerce_goal',
+ 'type': 'static_library',
+ 'sources': [
+ 'coerce_goal.cc',
+ ],
+ 'dependencies': [
+ '<(EXTERNALS):eigen',
+ '<(AOS)/common/controls/controls.gyp:polytope',
+ ],
+ 'export_dependent_settings': [
+ '<(EXTERNALS):eigen',
+ '<(AOS)/common/controls/controls.gyp:polytope',
+ ],
+ },
+ {
+ 'target_name': 'state_feedback_loop',
+ 'type': 'static_library',
+ 'sources': [
+ #'state_feedback_loop.h'
+ ],
+ 'dependencies': [
+ '<(EXTERNALS):eigen',
+ ],
+ 'export_dependent_settings': [
+ '<(EXTERNALS):eigen',
+ ],
+ },
+ ],
+}
diff --git a/frc971/control_loops/control_loops.q b/frc971/control_loops/control_loops.q
new file mode 100644
index 0000000..37a6420
--- /dev/null
+++ b/frc971/control_loops/control_loops.q
@@ -0,0 +1,62 @@
+package frc971;
+
+// Represents all of the data for a single potentiometer and indexed encoder
+// pair.
+// The units on all of the positions are the same.
+// All encoder values are relative to where the encoder was at some arbitrary
+// point in time. All potentiometer values are relative to some arbitrary 0
+// position which varies with each robot.
+struct PotAndIndexPosition {
+ // Current position read from the encoder.
+ double encoder;
+ // Current position read from the potentiometer.
+ double pot;
+
+ // Position from the encoder latched at the last index pulse.
+ double latched_encoder;
+ // Position from the potentiometer latched at the last index pulse.
+ double latched_pot;
+
+ // How many index pulses we've seen since startup. Starts at 0.
+ uint32_t index_pulses;
+};
+
+// The internal state of a zeroing estimator.
+struct EstimatorState {
+ // If true, there has been a fatal error for the estimator.
+ bool error;
+ // If the joint has seen an index pulse and is zeroed.
+ bool zeroed;
+ // The estimated position of the joint.
+ double position;
+};
+
+// A left/right pair of PotAndIndexPositions.
+struct PotAndIndexPair {
+ PotAndIndexPosition left;
+ PotAndIndexPosition right;
+};
+
+// Records edges captured on a single hall effect sensor.
+struct HallEffectStruct {
+ bool current;
+ int32_t posedge_count;
+ int32_t negedge_count;
+ double posedge_value;
+ double negedge_value;
+};
+
+// Records the positions for a mechanism with edge-capturing sensors on it.
+struct HallEffectPositions {
+ double current;
+ double posedge;
+ double negedge;
+};
+
+// Records edges captured on a single hall effect sensor.
+struct PosedgeOnlyCountedHallEffectStruct {
+ bool current;
+ int32_t posedge_count;
+ int32_t negedge_count;
+ double posedge_value;
+};
diff --git a/frc971/control_loops/gaussian_noise.cc b/frc971/control_loops/gaussian_noise.cc
new file mode 100644
index 0000000..c1c8701
--- /dev/null
+++ b/frc971/control_loops/gaussian_noise.cc
@@ -0,0 +1,18 @@
+#include "frc971/control_loops/gaussian_noise.h"
+
+namespace frc971 {
+namespace control_loops {
+
+GaussianNoise::GaussianNoise(unsigned int seed, double stddev)
+ : stddev_(stddev),
+ generator_(seed),
+ distribution_(0.0, 1.0) {
+ // Everything is initialized now.
+}
+
+double GaussianNoise::AddNoiseToSample(double sample) {
+ return sample + (distribution_(generator_) * stddev_);
+}
+
+} // namespace control_loops
+} // namespace frc971
diff --git a/frc971/control_loops/gaussian_noise.h b/frc971/control_loops/gaussian_noise.h
new file mode 100644
index 0000000..8adf840
--- /dev/null
+++ b/frc971/control_loops/gaussian_noise.h
@@ -0,0 +1,36 @@
+#ifndef FRC971_CONTROL_LOOPS_GAUSSIAN_NOISE_H_
+#define FRC971_CONTROL_LOOPS_GAUSSIAN_NOISE_H_
+
+#include <unistd.h>
+
+#include <memory>
+#include <random>
+
+namespace frc971 {
+namespace control_loops {
+
+class GaussianNoise {
+ public:
+ // seed: The seed for the random number generator.
+ // stddev: The standard deviation of the distribution.
+ GaussianNoise(unsigned int seed, double stddev);
+
+ // Returns a version of the sample with gaussian noise added in.
+ double AddNoiseToSample(double sample);
+
+ // Sets the standard deviation of the gaussian noise.
+ inline void set_standard_deviation(double stddev) {
+ stddev_ = stddev;
+ }
+
+ private:
+ double stddev_;
+
+ std::default_random_engine generator_;
+ std::normal_distribution<double> distribution_;
+};
+
+} // namespace control_loops
+} // namespace frc971
+
+#endif // FRC971_CONTROL_LOOPS_GAUSSIAN_NOISE_H_
diff --git a/frc971/control_loops/hall_effect_tracker.h b/frc971/control_loops/hall_effect_tracker.h
new file mode 100644
index 0000000..d1b6efe
--- /dev/null
+++ b/frc971/control_loops/hall_effect_tracker.h
@@ -0,0 +1,75 @@
+#ifndef FRC971_CONTROL_LOOPS_HALL_EFFECT_H_
+#define FRC971_CONTROL_LOOPS_HALL_EFFECT_H_
+
+#include <stdint.h>
+
+#include "frc971/control_loops/control_loops.q.h"
+
+namespace frc971 {
+
+// TODO(aschuh): Can we filter for 2 cycles instead of just 1?
+class HallEffectTracker {
+ public:
+ int32_t get_posedges() const { return posedges_.count(); }
+ int32_t get_negedges() const { return negedges_.count(); }
+
+ bool either_count_changed() const {
+ return posedges_.count_changed() || negedges_.count_changed();
+ }
+ bool posedge_count_changed() const { return posedges_.count_changed(); }
+ bool negedge_count_changed() const { return negedges_.count_changed(); }
+
+ bool value() const { return value_; }
+ bool last_value() const { return last_value_; }
+ bool is_posedge() const { return value() && !last_value(); }
+ bool is_negedge() const { return !value() && last_value(); }
+
+ double posedge_value() const { return posedge_value_; }
+ double negedge_value() const { return negedge_value_; }
+
+ void Update(const HallEffectStruct &position) {
+ last_value_ = value_;
+ value_ = position.current;
+ posedge_value_ = position.posedge_value;
+ negedge_value_ = position.negedge_value;
+ posedges_.update(position.posedge_count);
+ negedges_.update(position.negedge_count);
+ }
+
+ void Reset(const HallEffectStruct &position) {
+ posedges_.Reset(position.posedge_count);
+ negedges_.Reset(position.negedge_count);
+ value_ = position.current;
+ last_value_ = position.current;
+ posedge_value_ = position.posedge_value;
+ negedge_value_ = position.negedge_value;
+ }
+
+ private:
+ class {
+ public:
+ void update(int32_t count) {
+ previous_count_ = count_;
+ count_ = count;
+ }
+
+ void Reset(int32_t count) { count_ = count; }
+
+ bool count_changed() const { return previous_count_ != count_; }
+
+ int32_t count() const { return count_; }
+
+ private:
+ int32_t count_ = 0;
+ int32_t previous_count_ = 0;
+ } posedges_, negedges_;
+
+ bool value_ = false;
+ bool last_value_ = false;
+
+ double posedge_value_ = 0, negedge_value_ = 0;
+};
+
+} // namespace frc971
+
+#endif // FRC971_CONTROL_LOOPS_HALL_EFFECT_H_
diff --git a/frc971/control_loops/position_sensor_sim.cc b/frc971/control_loops/position_sensor_sim.cc
new file mode 100644
index 0000000..caa383e
--- /dev/null
+++ b/frc971/control_loops/position_sensor_sim.cc
@@ -0,0 +1,97 @@
+#include "frc971/control_loops/position_sensor_sim.h"
+
+#include <cmath>
+
+namespace frc971 {
+namespace control_loops {
+
+/* Index pulse/segment Explanation:
+ *
+ * The index segments are labelled starting at zero and go up. Each index
+ * segment is the space between the two bordering index pulses. The length of
+ * each index segment is determined by the `index_diff` variable in the
+ * constructor below.
+ *
+ * The index pulses are encountered when the mechanism moves from one index
+ * segment to another.
+ *
+ * index segment
+ * |
+ * V
+ *
+ * +--- 0---+--- 1---+--- 2---+--- 3---+--- 4---+--- 5---+--- 6---+
+ *
+ * | | | | | | | |
+ * 0 1 2 3 4 5 6 7
+ *
+ * A
+ * |
+ * index pulse
+ */
+
+PositionSensorSimulator::PositionSensorSimulator(double index_diff,
+ unsigned int noise_seed)
+ : index_diff_(index_diff), pot_noise_(noise_seed, 0.0) {
+ Initialize(0.0, 0.0);
+}
+
+void PositionSensorSimulator::Initialize(double start_position,
+ double pot_noise_stddev,
+ double known_index_pos /* = 0*/) {
+ // We're going to make the index pulse we know "segment zero".
+ cur_index_segment_ = floor((start_position - known_index_pos) / index_diff_);
+ known_index_pos_ = known_index_pos;
+ cur_index_ = 0;
+ index_count_ = 0;
+ cur_pos_ = start_position;
+ start_position_ = start_position;
+ pot_noise_.set_standard_deviation(pot_noise_stddev);
+}
+
+void PositionSensorSimulator::MoveTo(double new_pos) {
+ // Compute which index segment we're in. In other words, compute between
+ // which two index pulses we are.
+ const int new_index_segment =
+ floor((new_pos - known_index_pos_) / index_diff_);
+
+ if (new_index_segment < cur_index_segment_) {
+ // We've crossed an index pulse in the negative direction. That means the
+ // index pulse we just crossed is the higher end of the current index
+ // segment. For example, if the mechanism moved from index segment four to
+ // index segment three, then we just crossed index pulse 4.
+ cur_index_ = new_index_segment + 1;
+ index_count_++;
+ } else if (new_index_segment > cur_index_segment_) {
+ // We've crossed an index pulse in the positive direction. That means the
+ // index pulse we just crossed is the lower end of the index segment. For
+ // example, if the mechanism moved from index segment seven to index
+ // segment eight, then we just crossed index pulse eight.
+ cur_index_ = new_index_segment;
+ index_count_++;
+ }
+
+ cur_index_segment_ = new_index_segment;
+ cur_pos_ = new_pos;
+}
+
+void PositionSensorSimulator::GetSensorValues(PotAndIndexPosition *values) {
+ values->pot = pot_noise_.AddNoiseToSample(cur_pos_);
+ values->encoder = cur_pos_ - start_position_;
+
+ if (index_count_ == 0) {
+ values->latched_pot = 0.0;
+ values->latched_encoder = 0.0;
+ } else {
+ // Determine the position of the index pulse relative to absolute zero.
+ double index_pulse_position = cur_index_ * index_diff_ + known_index_pos_;
+
+ // Populate the latched pot/encoder samples.
+ values->latched_pot = pot_noise_.AddNoiseToSample(index_pulse_position);
+ values->latched_encoder = index_pulse_position - start_position_;
+ }
+
+ values->index_pulses = index_count_;
+}
+
+} // namespace control_loops
+} // namespace frc971
diff --git a/frc971/control_loops/position_sensor_sim.h b/frc971/control_loops/position_sensor_sim.h
new file mode 100644
index 0000000..6c1884d
--- /dev/null
+++ b/frc971/control_loops/position_sensor_sim.h
@@ -0,0 +1,76 @@
+#ifndef FRC971_CONTROL_LOOPS_POSITION_SENSOR_SIM_H_
+#define FRC971_CONTROL_LOOPS_POSITION_SENSOR_SIM_H_
+
+#include "frc971/control_loops/control_loops.q.h"
+#include "frc971/control_loops/gaussian_noise.h"
+
+namespace frc971 {
+namespace control_loops {
+
+// NOTE: All potentiometer and encoder values in this class are assumed to be in
+// translated SI units.
+
+class PositionSensorSimulator {
+ public:
+ // index_diff: The interval between index pulses. This is measured in SI
+ // units. For example, if an index pulse hits every 5cm on the
+ // elevator, set this to 0.05.
+ // noise_seed: The seed to feed into the random number generator for the
+ // potentiometer values.
+ // TODO(danielp): Allow for starting with a non-zero encoder value.
+ PositionSensorSimulator(double index_diff, unsigned int noise_seed = 0);
+
+ // Set new parameters for the sensors. This is useful for unit tests to change
+ // the simulated sensors' behavior on the fly.
+ // start_position: The position relative to absolute zero where the simulated
+ // structure starts. For example, to simulate the elevator
+ // starting at 40cm above absolute zero, set this to 0.4.
+ // pot_noise_stddev: The pot noise is sampled from a gaussian distribution.
+ // This specifies the standard deviation of that
+ // distribution.
+ // known_index_pos: The absolute position of an index pulse.
+ void Initialize(double start_position,
+ double pot_noise_stddev,
+ double known_index_pos = 0.0);
+
+ // Simulate the structure moving to a new position. The new value is measured
+ // relative to absolute zero. This will update the simulated sensors with new
+ // readings.
+ // new_position: The new position relative to absolute zero.
+ void MoveTo(double new_position);
+
+ // Get the current values of the simulated sensors.
+ // values: The target structure will be populated with simulated sensor
+ // readings. The readings will be in SI units. For example the units
+ // can be given in radians, meters, etc.
+ void GetSensorValues(PotAndIndexPosition* values);
+
+ private:
+ // The absolute segment between two index pulses the simulation is on. For
+ // example, when the current position is betwen index pulse zero and one,
+ // the current index segment is considered to be zero. Index segment one is
+ // between index pulses 1 and 2, etc.
+ int cur_index_segment_;
+ // Index pulse to use for calculating latched sensor values, relative to
+ // absolute zero. In other words this always holds the index pulse that was
+ // encountered most recently.
+ int cur_index_;
+ // How many index pulses we've seen.
+ int index_count_;
+ // Distance between index pulses on the mechanism.
+ double index_diff_;
+ // Absolute position of a known index pulse.
+ double known_index_pos_;
+ // Current position of the mechanism relative to absolute zero.
+ double cur_pos_;
+ // Starting position of the mechanism relative to absolute zero. See the
+ // `starting_position` parameter in the constructor for more info.
+ double start_position_;
+ // Gaussian noise to add to pot readings.
+ GaussianNoise pot_noise_;
+};
+
+} // namespace control_loops
+} // namespace frc971
+
+#endif /* FRC971_CONTROL_LOOPS_POSITION_SENSOR_SIM_H_ */
diff --git a/frc971/control_loops/position_sensor_sim_test.cc b/frc971/control_loops/position_sensor_sim_test.cc
new file mode 100644
index 0000000..0c9d3a9
--- /dev/null
+++ b/frc971/control_loops/position_sensor_sim_test.cc
@@ -0,0 +1,154 @@
+#include <unistd.h>
+
+#include <memory>
+
+#include <random>
+
+#include "gtest/gtest.h"
+#include "frc971/control_loops/control_loops.q.h"
+#include "frc971/control_loops/position_sensor_sim.h"
+#include "aos/common/die.h"
+
+namespace frc971 {
+namespace control_loops {
+
+class PositionSensorSimTest : public ::testing::Test {
+ protected:
+ PositionSensorSimTest() {}
+};
+
+TEST_F(PositionSensorSimTest, NoIndices) {
+ // We'll simulate a potentiometer with no noise so that we can accurately
+ // verify where the mechanism currently is. Overall though, the purpose of
+ // this test is to verify that no false index pulses are generated while the
+ // mechanism stays between two index pulses.
+ const double index_diff = 0.5;
+ PotAndIndexPosition position;
+ PositionSensorSimulator sim(index_diff);
+ sim.Initialize(3.6 * index_diff, 0);
+
+ // Make sure that we don't accidentally hit an index pulse.
+ for (int i = 0; i < 30; i++) {
+ sim.MoveTo(3.6 * index_diff);
+ sim.GetSensorValues(&position);
+ ASSERT_DOUBLE_EQ(3.6 * index_diff, position.pot);
+ ASSERT_EQ(0u, position.index_pulses);
+ }
+
+ for (int i = 0; i < 30; i++) {
+ sim.MoveTo(3.0 * index_diff);
+ sim.GetSensorValues(&position);
+ ASSERT_DOUBLE_EQ(3.0 * index_diff, position.pot);
+ ASSERT_EQ(0u, position.index_pulses);
+ }
+
+ for (int i = 0; i < 30; i++) {
+ sim.MoveTo(3.99 * index_diff);
+ sim.GetSensorValues(&position);
+ ASSERT_DOUBLE_EQ(3.99 * index_diff, position.pot);
+ ASSERT_EQ(0u, position.index_pulses);
+ }
+
+ for (int i = 0; i < 30; i++) {
+ sim.MoveTo(3.0 * index_diff);
+ sim.GetSensorValues(&position);
+ ASSERT_DOUBLE_EQ(3.0 * index_diff, position.pot);
+ ASSERT_EQ(0u, position.index_pulses);
+ }
+}
+
+TEST_F(PositionSensorSimTest, CountIndices) {
+ // The purpose of this test is to verify that the simulator latches the
+ // correct index pulse when transitioning from one segment to another. We
+ // again simulate zero noise on the potentiometer to accurately verify the
+ // mechanism's position during the index pulses.
+ const double index_diff = 0.8;
+ PotAndIndexPosition position;
+ PositionSensorSimulator sim(index_diff);
+ sim.Initialize(4.6 * index_diff, 0);
+
+ // Make sure that we get an index pulse on every transition.
+ sim.GetSensorValues(&position);
+ ASSERT_EQ(0u, position.index_pulses);
+
+ sim.MoveTo(3.6 * index_diff);
+ sim.GetSensorValues(&position);
+ ASSERT_DOUBLE_EQ(4.0 * index_diff, position.latched_pot);
+ ASSERT_EQ(1u, position.index_pulses);
+
+ sim.MoveTo(4.5 * index_diff);
+ sim.GetSensorValues(&position);
+ ASSERT_DOUBLE_EQ(4.0 * index_diff, position.latched_pot);
+ ASSERT_EQ(2u, position.index_pulses);
+
+ sim.MoveTo(5.9 * index_diff);
+ sim.GetSensorValues(&position);
+ ASSERT_DOUBLE_EQ(5.0 * index_diff, position.latched_pot);
+ ASSERT_EQ(3u, position.index_pulses);
+
+ sim.MoveTo(6.1 * index_diff);
+ sim.GetSensorValues(&position);
+ ASSERT_DOUBLE_EQ(6.0 * index_diff, position.latched_pot);
+ ASSERT_EQ(4u, position.index_pulses);
+
+ sim.MoveTo(8.7 * index_diff);
+ sim.GetSensorValues(&position);
+ ASSERT_DOUBLE_EQ(8.0 * index_diff, position.latched_pot);
+ ASSERT_EQ(5u, position.index_pulses);
+
+ sim.MoveTo(7.3 * index_diff);
+ sim.GetSensorValues(&position);
+ ASSERT_DOUBLE_EQ(8.0 * index_diff, position.latched_pot);
+ ASSERT_EQ(6u, position.index_pulses);
+}
+
+// Tests that the simulator handles non-zero specified index pulse locations
+// correctly.
+TEST_F(PositionSensorSimTest, NonZeroIndexLocation) {
+ const double index_diff = 0.5;
+ PositionSensorSimulator sim(index_diff);
+ sim.Initialize(index_diff * 0.25, 0.0, index_diff * 0.5);
+ PotAndIndexPosition position;
+
+ sim.MoveTo(0.75 * index_diff);
+ sim.GetSensorValues(&position);
+ EXPECT_EQ(1u, position.index_pulses);
+ EXPECT_DOUBLE_EQ(index_diff * 0.5, position.latched_pot);
+ EXPECT_DOUBLE_EQ(index_diff * 0.25, position.latched_encoder);
+
+ sim.MoveTo(index_diff);
+ sim.GetSensorValues(&position);
+ EXPECT_EQ(1u, position.index_pulses);
+ EXPECT_DOUBLE_EQ(index_diff * 0.5, position.latched_pot);
+ EXPECT_DOUBLE_EQ(index_diff * 0.25, position.latched_encoder);
+
+ sim.MoveTo(1.75 * index_diff);
+ sim.GetSensorValues(&position);
+ EXPECT_EQ(2u, position.index_pulses);
+ EXPECT_DOUBLE_EQ(index_diff * 1.5, position.latched_pot);
+ EXPECT_DOUBLE_EQ(index_diff * 1.25, position.latched_encoder);
+
+ // Try it with our known index pulse not being our first one.
+ sim.Initialize(index_diff * 0.25, 0.0, index_diff * 1.5);
+
+ sim.MoveTo(0.75 * index_diff);
+ sim.GetSensorValues(&position);
+ EXPECT_EQ(1u, position.index_pulses);
+ EXPECT_DOUBLE_EQ(index_diff * 0.5, position.latched_pot);
+ EXPECT_DOUBLE_EQ(index_diff * 0.25, position.latched_encoder);
+
+ sim.MoveTo(index_diff);
+ sim.GetSensorValues(&position);
+ EXPECT_EQ(1u, position.index_pulses);
+ EXPECT_DOUBLE_EQ(index_diff * 0.5, position.latched_pot);
+ EXPECT_DOUBLE_EQ(index_diff * 0.25, position.latched_encoder);
+
+ sim.MoveTo(1.75 * index_diff);
+ sim.GetSensorValues(&position);
+ EXPECT_EQ(2u, position.index_pulses);
+ EXPECT_DOUBLE_EQ(index_diff * 1.5, position.latched_pot);
+ EXPECT_DOUBLE_EQ(index_diff * 1.25, position.latched_encoder);
+}
+
+} // namespace control_loops
+} // namespace frc971
diff --git a/frc971/control_loops/python/control_loop.py b/frc971/control_loops/python/control_loop.py
new file mode 100644
index 0000000..881880f
--- /dev/null
+++ b/frc971/control_loops/python/control_loop.py
@@ -0,0 +1,338 @@
+import controls
+import numpy
+
+class Constant(object):
+ def __init__ (self, name, formatt, value):
+ self.name = name
+ self.formatt = formatt
+ self.value = value
+ self.formatToType = {}
+ self.formatToType['%f'] = "double";
+ self.formatToType['%d'] = "int";
+ def __str__ (self):
+ return str("\nstatic constexpr %s %s = "+ self.formatt +";\n") % \
+ (self.formatToType[self.formatt], self.name, self.value)
+
+
+class ControlLoopWriter(object):
+ def __init__(self, gain_schedule_name, loops, namespaces=None, write_constants=False):
+ """Constructs a control loop writer.
+
+ Args:
+ gain_schedule_name: string, Name of the overall controller.
+ loops: array[ControlLoop], a list of control loops to gain schedule
+ in order.
+ namespaces: array[string], a list of names of namespaces to nest in
+ order. If None, the default will be used.
+ """
+ self._gain_schedule_name = gain_schedule_name
+ self._loops = loops
+ if namespaces:
+ self._namespaces = namespaces
+ else:
+ self._namespaces = ['frc971', 'control_loops']
+
+ self._namespace_start = '\n'.join(
+ ['namespace %s {' % name for name in self._namespaces])
+
+ self._namespace_end = '\n'.join(
+ ['} // namespace %s' % name for name in reversed(self._namespaces)])
+
+ self._constant_list = []
+
+ def AddConstant(self, constant):
+ """Adds a constant to write.
+
+ Args:
+ constant: Constant, the constant to add to the header.
+ """
+ self._constant_list.append(constant)
+
+ def _TopDirectory(self):
+ return self._namespaces[0]
+
+ def _HeaderGuard(self, header_file):
+ return (self._TopDirectory().upper() + '_CONTROL_LOOPS_' +
+ header_file.upper().replace('.', '_').replace('/', '_') +
+ '_')
+
+ def Write(self, header_file, cc_file):
+ """Writes the loops to the specified files."""
+ self.WriteHeader(header_file)
+ self.WriteCC(header_file, cc_file)
+
+ def _GenericType(self, typename):
+ """Returns a loop template using typename for the type."""
+ num_states = self._loops[0].A.shape[0]
+ num_inputs = self._loops[0].B.shape[1]
+ num_outputs = self._loops[0].C.shape[0]
+ return '%s<%d, %d, %d>' % (
+ typename, num_states, num_inputs, num_outputs)
+
+ def _ControllerType(self):
+ """Returns a template name for StateFeedbackController."""
+ return self._GenericType('StateFeedbackController')
+
+ def _LoopType(self):
+ """Returns a template name for StateFeedbackLoop."""
+ return self._GenericType('StateFeedbackLoop')
+
+ def _PlantType(self):
+ """Returns a template name for StateFeedbackPlant."""
+ return self._GenericType('StateFeedbackPlant')
+
+ def _CoeffType(self):
+ """Returns a template name for StateFeedbackPlantCoefficients."""
+ return self._GenericType('StateFeedbackPlantCoefficients')
+
+ def WriteHeader(self, header_file, double_appendage=False, MoI_ratio=0.0):
+ """Writes the header file to the file named header_file.
+ Set double_appendage to true in order to include a ratio of
+ moments of inertia constant. Currently, only used for 2014 claw."""
+ with open(header_file, 'w') as fd:
+ header_guard = self._HeaderGuard(header_file)
+ fd.write('#ifndef %s\n'
+ '#define %s\n\n' % (header_guard, header_guard))
+ fd.write('#include \"frc971/control_loops/state_feedback_loop.h\"\n')
+ fd.write('\n')
+
+ fd.write(self._namespace_start)
+
+ for const in self._constant_list:
+ fd.write(str(const))
+
+ fd.write('\n\n')
+ for loop in self._loops:
+ fd.write(loop.DumpPlantHeader())
+ fd.write('\n')
+ fd.write(loop.DumpControllerHeader())
+ fd.write('\n')
+
+ fd.write('%s Make%sPlant();\n\n' %
+ (self._PlantType(), self._gain_schedule_name))
+
+ fd.write('%s Make%sLoop();\n\n' %
+ (self._LoopType(), self._gain_schedule_name))
+
+ fd.write(self._namespace_end)
+ fd.write('\n\n')
+ fd.write("#endif // %s\n" % header_guard)
+
+ def WriteCC(self, header_file_name, cc_file):
+ """Writes the cc file to the file named cc_file."""
+ with open(cc_file, 'w') as fd:
+ fd.write('#include \"%s/control_loops/%s\"\n' %
+ (self._TopDirectory(), header_file_name))
+ fd.write('\n')
+ fd.write('#include <vector>\n')
+ fd.write('\n')
+ fd.write('#include \"frc971/control_loops/state_feedback_loop.h\"\n')
+ fd.write('\n')
+ fd.write(self._namespace_start)
+ fd.write('\n\n')
+ for loop in self._loops:
+ fd.write(loop.DumpPlant())
+ fd.write('\n')
+
+ for loop in self._loops:
+ fd.write(loop.DumpController())
+ fd.write('\n')
+
+ fd.write('%s Make%sPlant() {\n' %
+ (self._PlantType(), self._gain_schedule_name))
+ fd.write(' ::std::vector< ::std::unique_ptr<%s>> plants(%d);\n' % (
+ self._CoeffType(), len(self._loops)))
+ for index, loop in enumerate(self._loops):
+ fd.write(' plants[%d] = ::std::unique_ptr<%s>(new %s(%s));\n' %
+ (index, self._CoeffType(), self._CoeffType(),
+ loop.PlantFunction()))
+ fd.write(' return %s(&plants);\n' % self._PlantType())
+ fd.write('}\n\n')
+
+ fd.write('%s Make%sLoop() {\n' %
+ (self._LoopType(), self._gain_schedule_name))
+ fd.write(' ::std::vector< ::std::unique_ptr<%s>> controllers(%d);\n' % (
+ self._ControllerType(), len(self._loops)))
+ for index, loop in enumerate(self._loops):
+ fd.write(' controllers[%d] = ::std::unique_ptr<%s>(new %s(%s));\n' %
+ (index, self._ControllerType(), self._ControllerType(),
+ loop.ControllerFunction()))
+ fd.write(' return %s(&controllers);\n' % self._LoopType())
+ fd.write('}\n\n')
+
+ fd.write(self._namespace_end)
+ fd.write('\n')
+
+
+class ControlLoop(object):
+ def __init__(self, name):
+ """Constructs a control loop object.
+
+ Args:
+ name: string, The name of the loop to use when writing the C++ files.
+ """
+ self._name = name
+
+ def ContinuousToDiscrete(self, A_continuous, B_continuous, dt):
+ """Calculates the discrete time values for A and B.
+
+ Args:
+ A_continuous: numpy.matrix, The continuous time A matrix
+ B_continuous: numpy.matrix, The continuous time B matrix
+ dt: float, The time step of the control loop
+
+ Returns:
+ (A, B), numpy.matrix, the control matricies.
+ """
+ return controls.c2d(A_continuous, B_continuous, dt)
+
+ def InitializeState(self):
+ """Sets X, Y, and X_hat to zero defaults."""
+ self.X = numpy.zeros((self.A.shape[0], 1))
+ self.Y = self.C * self.X
+ self.X_hat = numpy.zeros((self.A.shape[0], 1))
+
+ def PlaceControllerPoles(self, poles):
+ """Places the controller poles.
+
+ Args:
+ poles: array, An array of poles. Must be complex conjegates if they have
+ any imaginary portions.
+ """
+ self.K = controls.dplace(self.A, self.B, poles)
+
+ def PlaceObserverPoles(self, poles):
+ """Places the observer poles.
+
+ Args:
+ poles: array, An array of poles. Must be complex conjegates if they have
+ any imaginary portions.
+ """
+ self.L = controls.dplace(self.A.T, self.C.T, poles).T
+
+ def Update(self, U):
+ """Simulates one time step with the provided U."""
+ #U = numpy.clip(U, self.U_min, self.U_max)
+ self.X = self.A * self.X + self.B * U
+ self.Y = self.C * self.X + self.D * U
+
+ def PredictObserver(self, U):
+ """Runs the predict step of the observer update."""
+ self.X_hat = (self.A * self.X_hat + self.B * U)
+
+ def CorrectObserver(self, U):
+ """Runs the correct step of the observer update."""
+ self.X_hat += numpy.linalg.inv(self.A) * self.L * (
+ self.Y - self.C * self.X_hat - self.D * U)
+
+ def UpdateObserver(self, U):
+ """Updates the observer given the provided U."""
+ self.X_hat = (self.A * self.X_hat + self.B * U +
+ self.L * (self.Y - self.C * self.X_hat - self.D * U))
+
+ def _DumpMatrix(self, matrix_name, matrix):
+ """Dumps the provided matrix into a variable called matrix_name.
+
+ Args:
+ matrix_name: string, The variable name to save the matrix to.
+ matrix: The matrix to dump.
+
+ Returns:
+ string, The C++ commands required to populate a variable named matrix_name
+ with the contents of matrix.
+ """
+ ans = [' Eigen::Matrix<double, %d, %d> %s;\n' % (
+ matrix.shape[0], matrix.shape[1], matrix_name)]
+ first = True
+ for x in xrange(matrix.shape[0]):
+ for y in xrange(matrix.shape[1]):
+ element = matrix[x, y]
+ if first:
+ ans.append(' %s << ' % matrix_name)
+ first = False
+ else:
+ ans.append(', ')
+ ans.append(str(element))
+
+ ans.append(';\n')
+ return ''.join(ans)
+
+ def DumpPlantHeader(self):
+ """Writes out a c++ header declaration which will create a Plant object.
+
+ Returns:
+ string, The header declaration for the function.
+ """
+ num_states = self.A.shape[0]
+ num_inputs = self.B.shape[1]
+ num_outputs = self.C.shape[0]
+ return 'StateFeedbackPlantCoefficients<%d, %d, %d> Make%sPlantCoefficients();\n' % (
+ num_states, num_inputs, num_outputs, self._name)
+
+ def DumpPlant(self):
+ """Writes out a c++ function which will create a PlantCoefficients object.
+
+ Returns:
+ string, The function which will create the object.
+ """
+ num_states = self.A.shape[0]
+ num_inputs = self.B.shape[1]
+ num_outputs = self.C.shape[0]
+ ans = ['StateFeedbackPlantCoefficients<%d, %d, %d>'
+ ' Make%sPlantCoefficients() {\n' % (
+ num_states, num_inputs, num_outputs, self._name)]
+
+ ans.append(self._DumpMatrix('A', self.A))
+ ans.append(self._DumpMatrix('B', self.B))
+ ans.append(self._DumpMatrix('C', self.C))
+ ans.append(self._DumpMatrix('D', self.D))
+ ans.append(self._DumpMatrix('U_max', self.U_max))
+ ans.append(self._DumpMatrix('U_min', self.U_min))
+
+ ans.append(' return StateFeedbackPlantCoefficients<%d, %d, %d>'
+ '(A, B, C, D, U_max, U_min);\n' % (num_states, num_inputs,
+ num_outputs))
+ ans.append('}\n')
+ return ''.join(ans)
+
+ def PlantFunction(self):
+ """Returns the name of the plant coefficient function."""
+ return 'Make%sPlantCoefficients()' % self._name
+
+ def ControllerFunction(self):
+ """Returns the name of the controller function."""
+ return 'Make%sController()' % self._name
+
+ def DumpControllerHeader(self):
+ """Writes out a c++ header declaration which will create a Controller object.
+
+ Returns:
+ string, The header declaration for the function.
+ """
+ num_states = self.A.shape[0]
+ num_inputs = self.B.shape[1]
+ num_outputs = self.C.shape[0]
+ return 'StateFeedbackController<%d, %d, %d> %s;\n' % (
+ num_states, num_inputs, num_outputs, self.ControllerFunction())
+
+ def DumpController(self):
+ """Returns a c++ function which will create a Controller object.
+
+ Returns:
+ string, The function which will create the object.
+ """
+ num_states = self.A.shape[0]
+ num_inputs = self.B.shape[1]
+ num_outputs = self.C.shape[0]
+ ans = ['StateFeedbackController<%d, %d, %d> %s {\n' % (
+ num_states, num_inputs, num_outputs, self.ControllerFunction())]
+
+ ans.append(self._DumpMatrix('L', self.L))
+ ans.append(self._DumpMatrix('K', self.K))
+ ans.append(self._DumpMatrix('A_inv', numpy.linalg.inv(self.A)))
+
+ ans.append(' return StateFeedbackController<%d, %d, %d>'
+ '(L, K, A_inv, Make%sPlantCoefficients());\n' % (
+ num_states, num_inputs, num_outputs, self._name))
+ ans.append('}\n')
+ return ''.join(ans)
diff --git a/frc971/control_loops/python/controls.py b/frc971/control_loops/python/controls.py
new file mode 100644
index 0000000..b66bd56
--- /dev/null
+++ b/frc971/control_loops/python/controls.py
@@ -0,0 +1,153 @@
+#!/usr/bin/python
+
+"""
+Control loop pole placement library.
+
+This library will grow to support many different pole placement methods.
+Currently it only supports direct pole placement.
+"""
+
+__author__ = 'Austin Schuh (austin.linux@gmail.com)'
+
+import numpy
+import slycot
+import scipy.signal.cont2discrete
+
+class Error (Exception):
+ """Base class for all control loop exceptions."""
+
+
+class PolePlacementError(Error):
+ """Exception raised when pole placement fails."""
+
+
+# TODO(aschuh): dplace should take a control system object.
+# There should also exist a function to manipulate laplace expressions, and
+# something to plot bode plots and all that.
+def dplace(A, B, poles, alpha=1e-6):
+ """Set the poles of (A - BF) to poles.
+
+ Args:
+ A: numpy.matrix(n x n), The A matrix.
+ B: numpy.matrix(n x m), The B matrix.
+ poles: array(imaginary numbers), The poles to use. Complex conjugates poles
+ must be in pairs.
+
+ Raises:
+ ValueError: Arguments were the wrong shape or there were too many poles.
+ PolePlacementError: Pole placement failed.
+
+ Returns:
+ numpy.matrix(m x n), K
+ """
+ # See http://www.icm.tu-bs.de/NICONET/doc/SB01BD.html for a description of the
+ # fortran code that this is cleaning up the interface to.
+ n = A.shape[0]
+ if A.shape[1] != n:
+ raise ValueError("A must be square")
+ if B.shape[0] != n:
+ raise ValueError("B must have the same number of states as A.")
+ m = B.shape[1]
+
+ num_poles = len(poles)
+ if num_poles > n:
+ raise ValueError("Trying to place more poles than states.")
+
+ out = slycot.sb01bd(n=n,
+ m=m,
+ np=num_poles,
+ alpha=alpha,
+ A=A,
+ B=B,
+ w=numpy.array(poles),
+ dico='D')
+
+ A_z = numpy.matrix(out[0])
+ num_too_small_eigenvalues = out[2]
+ num_assigned_eigenvalues = out[3]
+ num_uncontrollable_eigenvalues = out[4]
+ K = numpy.matrix(-out[5])
+ Z = numpy.matrix(out[6])
+
+ if num_too_small_eigenvalues != 0:
+ raise PolePlacementError("Number of eigenvalues that are too small "
+ "and are therefore unmodified is %d." %
+ num_too_small_eigenvalues)
+ if num_assigned_eigenvalues != num_poles:
+ raise PolePlacementError("Did not place all the eigenvalues that were "
+ "requested. Only placed %d eigenvalues." %
+ num_assigned_eigenvalues)
+ if num_uncontrollable_eigenvalues != 0:
+ raise PolePlacementError("Found %d uncontrollable eigenvlaues." %
+ num_uncontrollable_eigenvalues)
+
+ return K
+
+
+def c2d(A, B, dt):
+ """Converts from continuous time state space representation to discrete time.
+ Returns (A, B). C and D are unchanged."""
+
+ ans_a, ans_b, _, _, _ = scipy.signal.cont2discrete((A, B, None, None), dt)
+ return numpy.matrix(ans_a), numpy.matrix(ans_b)
+
+def ctrb(A, B):
+ """Returns the controlability matrix.
+
+ This matrix must have full rank for all the states to be controlable.
+ """
+ n = A.shape[0]
+ output = B
+ intermediate = B
+ for i in xrange(0, n):
+ intermediate = A * intermediate
+ output = numpy.concatenate((output, intermediate), axis=1)
+
+ return output
+
+def dlqr(A, B, Q, R):
+ """Solves for the optimal lqr controller.
+
+ x(n+1) = A * x(n) + B * u(n)
+ J = sum(0, inf, x.T * Q * x + u.T * R * u)
+ """
+
+ # P = (A.T * P * A) - (A.T * P * B * numpy.linalg.inv(R + B.T * P *B) * (A.T * P.T * B).T + Q
+
+ P, rcond, w, S, T = slycot.sb02od(
+ n=A.shape[0], m=B.shape[1], A=A, B=B, Q=Q, R=R, dico='D')
+
+ F = numpy.linalg.inv(R + B.T * P *B) * B.T * P * A
+ return F
+
+def kalman(A, B, C, Q, R):
+ """Solves for the steady state kalman gain and covariance matricies.
+
+ Args:
+ A, B, C: SS matricies.
+ Q: The model uncertantity
+ R: The measurement uncertainty
+
+ Returns:
+ KalmanGain, Covariance.
+ """
+ P = Q
+
+ I = numpy.matrix(numpy.eye(P.shape[0]))
+ At = A.T
+ Ct = C.T
+ i = 0
+
+ while True:
+ last_P = P
+ P_prior = A * P * At + Q
+ S = C * P_prior * Ct + R
+ K = P_prior * Ct * numpy.linalg.inv(S)
+ P = (I - K * C) * P_prior
+
+ diff = P - last_P
+ i += 1
+ if numpy.linalg.norm(diff) / numpy.linalg.norm(P) < 1e-9:
+ break
+
+ return K, P
diff --git a/frc971/control_loops/python/libcdd.py b/frc971/control_loops/python/libcdd.py
new file mode 100644
index 0000000..6b81a4f
--- /dev/null
+++ b/frc971/control_loops/python/libcdd.py
@@ -0,0 +1,130 @@
+#!/usr/bin/python
+
+"""Wrapper around libcdd, a polytope manipulation library."""
+
+__author__ = 'Austin Schuh (austin.linux@gmail.com)'
+
+import ctypes
+import sys
+
+# Wrapper around PyFile_AsFile so that we can print out the error messages.
+# Set the arg type and return types of the function call.
+class FILE(ctypes.Structure):
+ pass
+
+ctypes.pythonapi.PyFile_AsFile.argtypes = [ctypes.py_object]
+ctypes.pythonapi.PyFile_AsFile.restype = ctypes.POINTER(FILE)
+
+# Load and init libcdd. libcdd is a C library that implements algorithm to
+# manipulate half space and vertex representations of polytopes.
+# Unfortunately, the library was compiled with C++ even though it has a lot of C
+# code in it, so all the symbol names are mangled. Ug.
+libcdd = ctypes.cdll.LoadLibrary('libcdd.so')
+libcdd._Z23dd_set_global_constantsv()
+
+# The variable type mytype that libcdd defines (double[1])
+# See http://docs.python.org/2/library/ctypes.html#arrays for the documentation
+# explaining why ctypes.c_double * 1 => double[1]
+# libcdd defines mytype to various things so it can essentially template its
+# functions. What a weird library.
+mytype = ctypes.c_double * 1
+
+
+# Forward declaration for the polyhedra data structure.
+class dd_polyhedradata(ctypes.Structure):
+ pass
+
+
+# Definition of dd_matrixdata
+class dd_matrixdata(ctypes.Structure):
+ _fields_ = [
+ ("rowsize", ctypes.c_long),
+ ("linset", ctypes.POINTER(ctypes.c_ulong)),
+ ("colsize", ctypes.c_long),
+ ("representation", ctypes.c_int),
+ ("numbtype", ctypes.c_int),
+ ("matrix", ctypes.POINTER(ctypes.POINTER(mytype))),
+ ("objective", ctypes.c_int),
+ ("rowvec", ctypes.POINTER(mytype)),
+ ]
+
+# Define the input and output types for a bunch of libcdd functions.
+libcdd._Z15dd_CreateMatrixll.restype = ctypes.POINTER(dd_matrixdata)
+libcdd._Z9ddd_get_dPd.argtypes = [mytype]
+libcdd._Z9ddd_get_dPd.restype = ctypes.c_double
+
+libcdd._Z17dd_CopyGeneratorsP16dd_polyhedradata.argtypes = [
+ ctypes.POINTER(dd_polyhedradata)
+]
+libcdd._Z17dd_CopyGeneratorsP16dd_polyhedradata.restype = ctypes.POINTER(dd_matrixdata)
+
+libcdd._Z16dd_DDMatrix2PolyP13dd_matrixdataP12dd_ErrorType.argtypes = [
+ ctypes.POINTER(dd_matrixdata),
+ ctypes.POINTER(ctypes.c_int)
+]
+libcdd._Z16dd_DDMatrix2PolyP13dd_matrixdataP12dd_ErrorType.restype = (
+ ctypes.POINTER(dd_polyhedradata))
+
+libcdd._Z13dd_FreeMatrixP13dd_matrixdata.argtypes = [
+ ctypes.POINTER(dd_matrixdata)
+]
+
+libcdd._Z16dd_FreePolyhedraP16dd_polyhedradata.argtypes = [
+ ctypes.POINTER(dd_polyhedradata)
+]
+
+libcdd._Z9ddd_set_dPdd.argtypes = [
+ mytype,
+ ctypes.c_double
+]
+
+
+# Various enums.
+DD_INEQUALITY = 1
+DD_REAL = 1
+DD_NO_ERRORS = 17
+
+
+def dd_CreateMatrix(rows, cols):
+ return libcdd._Z15dd_CreateMatrixll(
+ ctypes.c_long(rows),
+ ctypes.c_long(cols))
+
+
+def dd_set_d(mytype_address, double_value):
+ libcdd._Z9ddd_set_dPdd(mytype_address,
+ ctypes.c_double(double_value))
+
+
+def dd_CopyGenerators(polyhedraptr):
+ return libcdd._Z17dd_CopyGeneratorsP16dd_polyhedradata(polyhedraptr)
+
+
+def dd_get_d(mytype_address):
+ return libcdd._Z9ddd_get_dPd(mytype_address)
+
+
+def dd_FreeMatrix(matrixptr):
+ libcdd._Z13dd_FreeMatrixP13dd_matrixdata(matrixptr)
+
+
+def dd_FreePolyhedra(polyhedraptr):
+ libcdd._Z16dd_FreePolyhedraP16dd_polyhedradata(polyhedraptr)
+
+
+def dd_DDMatrix2Poly(matrixptr):
+ error = ctypes.c_int()
+ polyhedraptr = libcdd._Z16dd_DDMatrix2PolyP13dd_matrixdataP12dd_ErrorType(
+ matrixptr,
+ ctypes.byref(error))
+
+ # Return None on error.
+ # The error values are enums, so they aren't exposed.
+ if error.value != DD_NO_ERRORS:
+ # Dump out the errors to stderr
+ libcdd._Z21dd_WriteErrorMessagesP8_IO_FILE12dd_ErrorType(
+ ctypes.pythonapi.PyFile_AsFile(ctypes.py_object(sys.stdout)),
+ error)
+ dd_FreePolyhedra(polyhedraptr)
+ return None
+ return polyhedraptr
diff --git a/frc971/control_loops/python/polytope.py b/frc971/control_loops/python/polytope.py
new file mode 100644
index 0000000..e08a160
--- /dev/null
+++ b/frc971/control_loops/python/polytope.py
@@ -0,0 +1,224 @@
+#!/usr/bin/python
+
+"""
+Polyhedral set library.
+
+This library implements convex regions of the form H x <= k, where H, x, and k
+are matricies. It also provides convenient methods to find all the verticies.
+"""
+
+__author__ = 'Austin Schuh (austin.linux@gmail.com)'
+
+
+import libcdd
+import numpy
+import string
+import sys
+
+
+def _PiecewiseConcat(*args):
+ """Concatenates strings inside lists, elementwise.
+
+ Given ['a', 's'] and ['d', 'f'], returns ['ad', 'sf']
+ """
+ return map(''.join, zip(*args))
+
+
+def _SplitAndPad(s):
+ """Splits a string on newlines, and pads the lines to be the same width."""
+ split_string = s.split('\n')
+ width = max(len(stringpiece) for stringpiece in split_string) + 1
+
+ padded_strings = [string.ljust(stringpiece, width, ' ')
+ for stringpiece in split_string]
+ return padded_strings
+
+
+def _PadHeight(padded_array, min_height):
+ """Adds lines of spaces to the top and bottom of an array symmetrically."""
+ height = len(padded_array)
+ if height < min_height:
+ pad_array = [' ' * len(padded_array[0])]
+ height_error = min_height - height
+ return (pad_array * ((height_error) / 2) +
+ padded_array +
+ pad_array * ((height_error + 1) / 2))
+ return padded_array
+
+
+class HPolytope(object):
+ """This object represents a H-polytope.
+
+ Polytopes are convex regions in n-dimensional space.
+ For H-polytopes, this is represented as the intersection of a set of half
+ planes. The mathematic equation that represents this is H x <= k.
+ """
+
+ def __init__(self, H, k):
+ """Constructs a H-polytope from the H and k matricies.
+
+ Args:
+ H: numpy.Matrix (n by k), where n is the number of constraints, and k is
+ the number of dimensions in the space. Does not copy the matrix.
+ k: numpy.Matrix (n by 1). Does not copy the matrix.
+ """
+ self._H = H
+ self._k = k
+
+ @property
+ def k(self):
+ """Returns the k in H x <= k."""
+ return self._k
+
+ @property
+ def H(self):
+ """Returns the H in H x <= k."""
+ return self._H
+
+ @property
+ def ndim(self):
+ """Returns the dimension of the set."""
+ return self._H.shape[1]
+
+ @property
+ def num_constraints(self):
+ """Returns the number of constraints defining the set."""
+ return self._k.shape[0]
+
+ def IsInside(self, point):
+ """Returns true if the point is inside the polytope, edges included."""
+ return (self._H * point <= self._k).all()
+
+ def Vertices(self):
+ """Returns a matrix with the vertices of the set in its rows."""
+ # TODO(aschuh): It would be better to write some small C/C++ function that
+ # does all of this and takes in a numpy array.
+ # The copy is expensive in Python and cheaper in C.
+
+ # Create an empty matrix with the correct size.
+ matrixptr = libcdd.dd_CreateMatrix(self.num_constraints, self.ndim + 1)
+ matrix = matrixptr.contents
+
+ try:
+ # Copy the data into the matrix.
+ for i in xrange(self.num_constraints):
+ libcdd.dd_set_d(matrix.matrix[i][0], self._k[i, 0])
+ for j in xrange(self.ndim):
+ libcdd.dd_set_d(matrix.matrix[i][j + 1], -self._H[i, j])
+
+ # Set enums to the correct values.
+ matrix.representation = libcdd.DD_INEQUALITY
+ matrix.numbtype = libcdd.DD_REAL
+
+ # TODO(aschuh): Set linearity if it is useful.
+ # This would be useful if we had any constraints saying B - A x = 0
+
+ # Build a Polyhedra
+ polyhedraptr = libcdd.dd_DDMatrix2Poly(matrixptr)
+
+ if not polyhedraptr:
+ return None
+
+ try:
+ # Return None on error.
+ # The error values are enums, so they aren't exposed.
+
+ # Magic happens here. Computes the vertices
+ vertex_matrixptr = libcdd.dd_CopyGenerators(
+ polyhedraptr)
+ vertex_matrix = vertex_matrixptr.contents
+
+ try:
+ # Count the number of vertices and rays in the result.
+ num_vertices = 0
+ num_rays = 0
+ for i in xrange(vertex_matrix.rowsize):
+ if libcdd.dd_get_d(vertex_matrix.matrix[i][0]) == 0:
+ num_rays += 1
+ else:
+ num_vertices += 1
+
+ # Build zeroed matricies for the new vertices and rays.
+ vertices = numpy.matrix(numpy.zeros((num_vertices,
+ vertex_matrix.colsize - 1)))
+ rays = numpy.matrix(numpy.zeros((num_rays,
+ vertex_matrix.colsize - 1)))
+
+ ray_index = 0
+ vertex_index = 0
+
+ # Copy the data out of the matrix.
+ for index in xrange(vertex_matrix.rowsize):
+ if libcdd.dd_get_d(vertex_matrix.matrix[index][0]) == 0.0:
+ for j in xrange(vertex_matrix.colsize - 1):
+ rays[ray_index, j] = libcdd.dd_get_d(
+ vertex_matrix.matrix[index][j + 1])
+ ray_index += 1
+ else:
+ for j in xrange(vertex_matrix.colsize - 1):
+ vertices[vertex_index, j] = libcdd.dd_get_d(
+ vertex_matrix.matrix[index][j + 1])
+ vertex_index += 1
+ finally:
+ # Free everything.
+ libcdd.dd_FreeMatrix(vertex_matrixptr)
+
+ finally:
+ libcdd.dd_FreePolyhedra(polyhedraptr)
+
+ finally:
+ libcdd.dd_FreeMatrix(matrixptr)
+
+ # Rays are unsupported right now. This may change in the future.
+ assert(rays.shape[0] == 0)
+
+ return vertices
+
+
+ def __str__(self):
+ """Returns a formatted version of the polytope.
+
+ The dump will look something like the following, which prints out the matrix
+ comparison.
+
+ [[ 1 0] [[12]
+ [-1 0] [[x0] <= [12]
+ [ 0 1] [x1]] [12]
+ [ 0 -1]] [12]]
+ """
+ height = max(self.ndim, self.num_constraints)
+
+ # Split the print up into 4 parts and concatenate them all.
+ H_strings = _PadHeight(_SplitAndPad(str(self.H)), height)
+ v_strings = _PadHeight(_SplitAndPad(str(self.k)), height)
+ x_strings = _PadHeight(self._MakeXStrings(), height)
+ cmp_strings = self._MakeCmpStrings(height)
+
+ return '\n'.join(_PiecewiseConcat(H_strings, x_strings,
+ cmp_strings, v_strings))
+
+ def _MakeXStrings(self):
+ """Builds an array of strings with constraint names in it for printing."""
+ x_strings = []
+ if self.ndim == 1:
+ x_strings = ["[[x0]] "]
+ else:
+ for index in xrange(self.ndim):
+ if index == 0:
+ x = "[[x%d] " % index
+ elif index == self.ndim - 1:
+ x = " [x%d]] " % index
+ else:
+ x = " [x%d] " % index
+ x_strings.append(x)
+ return x_strings
+
+ def _MakeCmpStrings(self, height):
+ """Builds an array of strings with the comparison in it for printing."""
+ cmp_strings = []
+ for index in xrange(height):
+ if index == (height - 1) / 2:
+ cmp_strings.append("<= ")
+ else:
+ cmp_strings.append(" ")
+ return cmp_strings
diff --git a/frc971/control_loops/python/polytope_test.py b/frc971/control_loops/python/polytope_test.py
new file mode 100755
index 0000000..9a35ebe
--- /dev/null
+++ b/frc971/control_loops/python/polytope_test.py
@@ -0,0 +1,192 @@
+#!/usr/bin/python
+
+import numpy
+from numpy.testing import *
+import polytope
+import unittest
+
+__author__ = 'Austin Schuh (austin.linux@gmail.com)'
+
+def MakePoint(*args):
+ """Makes a point from a set of arguments."""
+ return numpy.matrix([[arg] for arg in args])
+
+class TestHPolytope(unittest.TestCase):
+ def setUp(self):
+ """Builds a simple box polytope."""
+ self.H = numpy.matrix([[1, 0],
+ [-1, 0],
+ [0, 1],
+ [0, -1]])
+ self.k = numpy.matrix([[12],
+ [12],
+ [12],
+ [12]])
+ self.p = polytope.HPolytope(self.H, self.k)
+
+ def test_Hk(self):
+ """Tests that H and k are saved correctly."""
+ assert_array_equal(self.p.H, self.H)
+ assert_array_equal(self.p.k, self.k)
+
+ def test_IsInside(self):
+ """Tests IsInside for various points."""
+ inside_points = [
+ MakePoint(0, 0),
+ MakePoint(6, 6),
+ MakePoint(12, 6),
+ MakePoint(-6, 10)]
+ outside_points = [
+ MakePoint(14, 0),
+ MakePoint(-14, 0),
+ MakePoint(0, 14),
+ MakePoint(0, -14),
+ MakePoint(14, -14)]
+
+ for inside_point in inside_points:
+ self.assertTrue(self.p.IsInside(inside_point),
+ msg='Point is' + str(inside_point))
+
+ for outside_point in outside_points:
+ self.assertFalse(self.p.IsInside(outside_point),
+ msg='Point is' + str(outside_point))
+
+ def AreVertices(self, p, vertices):
+ """Checks that all the vertices are on corners of the set."""
+ for i in xrange(vertices.shape[0]):
+ # Check that all the vertices have the correct number of active
+ # constraints.
+ lmda = p.H * vertices[i,:].T - p.k
+ num_active_constraints = 0
+ for j in xrange(lmda.shape[0]):
+ # Verify that the constraints are either active, or not violated.
+ if numpy.abs(lmda[j, 0]) <= 1e-9:
+ num_active_constraints += 1
+ else:
+ self.assertLessEqual(lmda[j, 0], 0.0)
+
+ self.assertEqual(p.ndim, num_active_constraints)
+
+ def HasSamePoints(self, expected, actual):
+ """Verifies that the points in expected are in actual."""
+ found_points = set()
+ self.assertEqual(expected.shape, actual.shape)
+ for index in xrange(expected.shape[0]):
+ expected_point = expected[index, :]
+ for actual_index in xrange(actual.shape[0]):
+ actual_point = actual[actual_index, :]
+ if numpy.abs(expected_point - actual_point).max() <= 1e-4:
+ found_points.add(actual_index)
+ break
+
+ self.assertEqual(len(found_points), actual.shape[0],
+ msg="Expected:\n" + str(expected) + "\nActual:\n" + str(actual))
+
+ def test_Skewed_Nonsym_Vertices(self):
+ """Tests the vertices of a severely skewed space."""
+ self.H = numpy.matrix([[10, -1],
+ [-1, -1],
+ [-1, 10],
+ [10, 10]])
+ self.k = numpy.matrix([[2],
+ [2],
+ [2],
+ [2]])
+ self.p = polytope.HPolytope(self.H, self.k)
+ vertices = self.p.Vertices()
+ self.AreVertices(self.p, vertices)
+
+ self.HasSamePoints(
+ numpy.matrix([[0., 0.2],
+ [0.2, 0.],
+ [-2., 0.],
+ [0., -2.]]),
+ vertices)
+
+ def test_Vertices_Nonsym(self):
+ """Tests the vertices of a nonsymetric space."""
+ self.k = numpy.matrix([[6],
+ [12],
+ [2],
+ [10]])
+ self.p = polytope.HPolytope(self.H, self.k)
+ vertices = self.p.Vertices()
+ self.AreVertices(self.p, vertices)
+
+ self.HasSamePoints(
+ numpy.matrix([[6., 2.],
+ [6., -10.],
+ [-12., -10.],
+ [-12., 2.]]),
+ vertices)
+
+ def test_Vertices(self):
+ """Tests the vertices of a nonsymetric space."""
+ self.HasSamePoints(self.p.Vertices(),
+ numpy.matrix([[12., 12.],
+ [12., -12.],
+ [-12., -12.],
+ [-12., 12.]]))
+
+ def test_concat(self):
+ """Tests that the concat function works for simple inputs."""
+ self.assertEqual(["asd", "qwe"],
+ polytope._PiecewiseConcat(["a", "q"],
+ ["s", "w"],
+ ["d", "e"]))
+
+ def test_str(self):
+ """Verifies that the str method works for the provided p."""
+ self.assertEqual('[[ 1 0] [[12] \n'
+ ' [-1 0] [[x0] <= [12] \n'
+ ' [ 0 1] [x1]] [12] \n'
+ ' [ 0 -1]] [12]] ',
+ str(self.p))
+
+ def MakePWithDims(self, num_constraints, num_dims):
+ """Makes a zeroed out polytope with the correct size."""
+ self.p = polytope.HPolytope(
+ numpy.matrix(numpy.zeros((num_constraints, num_dims))),
+ numpy.matrix(numpy.zeros((num_constraints, 1))))
+
+ def test_few_constraints_odd_constraint_even_dims_str(self):
+ """Tests printing out the set with odd constraints and even dimensions."""
+ self.MakePWithDims(num_constraints=5, num_dims=2)
+ self.assertEqual('[[ 0. 0.] [[ 0.] \n'
+ ' [ 0. 0.] [[x0] [ 0.] \n'
+ ' [ 0. 0.] [x1]] <= [ 0.] \n'
+ ' [ 0. 0.] [ 0.] \n'
+ ' [ 0. 0.]] [ 0.]] ',
+ str(self.p))
+
+ def test_few_constraints_odd_constraint_small_dims_str(self):
+ """Tests printing out the set with odd constraints and odd dimensions."""
+ self.MakePWithDims(num_constraints=5, num_dims=1)
+ self.assertEqual('[[ 0.] [[ 0.] \n'
+ ' [ 0.] [ 0.] \n'
+ ' [ 0.] [[x0]] <= [ 0.] \n'
+ ' [ 0.] [ 0.] \n'
+ ' [ 0.]] [ 0.]] ',
+ str(self.p))
+
+ def test_few_constraints_odd_constraint_odd_dims_str(self):
+ """Tests printing out the set with odd constraints and odd dimensions."""
+ self.MakePWithDims(num_constraints=5, num_dims=3)
+ self.assertEqual('[[ 0. 0. 0.] [[ 0.] \n'
+ ' [ 0. 0. 0.] [[x0] [ 0.] \n'
+ ' [ 0. 0. 0.] [x1] <= [ 0.] \n'
+ ' [ 0. 0. 0.] [x2]] [ 0.] \n'
+ ' [ 0. 0. 0.]] [ 0.]] ',
+ str(self.p))
+
+ def test_many_constraints_even_constraint_odd_dims_str(self):
+ """Tests printing out the set with even constraints and odd dimensions."""
+ self.MakePWithDims(num_constraints=2, num_dims=3)
+ self.assertEqual('[[ 0. 0. 0.] [[x0] [[ 0.] \n'
+ ' [ 0. 0. 0.]] [x1] <= [ 0.]] \n'
+ ' [x2]] ',
+ str(self.p))
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/frc971/control_loops/state_feedback_loop.h b/frc971/control_loops/state_feedback_loop.h
new file mode 100644
index 0000000..6ebd32e
--- /dev/null
+++ b/frc971/control_loops/state_feedback_loop.h
@@ -0,0 +1,428 @@
+#ifndef FRC971_CONTROL_LOOPS_STATE_FEEDBACK_LOOP_H_
+#define FRC971_CONTROL_LOOPS_STATE_FEEDBACK_LOOP_H_
+
+#include <assert.h>
+
+#include <vector>
+#include <memory>
+#include <iostream>
+
+#include "Eigen/Dense"
+
+#include "aos/common/logging/logging.h"
+#include "aos/common/macros.h"
+
+// For everything in this file, "inputs" and "outputs" are defined from the
+// perspective of the plant. This means U is an input and Y is an output
+// (because you give the plant U (powers) and it gives you back a Y (sensor
+// values). This is the opposite of what they mean from the perspective of the
+// controller (U is an output because that's what goes to the motors and Y is an
+// input because that's what comes back from the sensors).
+
+template <int number_of_states, int number_of_inputs, int number_of_outputs>
+class StateFeedbackPlantCoefficients final {
+ public:
+ EIGEN_MAKE_ALIGNED_OPERATOR_NEW;
+
+ StateFeedbackPlantCoefficients(const StateFeedbackPlantCoefficients &other)
+ : A_(other.A()),
+ B_(other.B()),
+ C_(other.C()),
+ D_(other.D()),
+ U_min_(other.U_min()),
+ U_max_(other.U_max()) {
+ }
+
+ StateFeedbackPlantCoefficients(
+ const Eigen::Matrix<double, number_of_states, number_of_states> &A,
+ const Eigen::Matrix<double, number_of_states, number_of_inputs> &B,
+ const Eigen::Matrix<double, number_of_outputs, number_of_states> &C,
+ const Eigen::Matrix<double, number_of_outputs, number_of_inputs> &D,
+ const Eigen::Matrix<double, number_of_inputs, 1> &U_max,
+ const Eigen::Matrix<double, number_of_inputs, 1> &U_min)
+ : A_(A),
+ B_(B),
+ C_(C),
+ D_(D),
+ U_min_(U_min),
+ U_max_(U_max) {
+ }
+
+ const Eigen::Matrix<double, number_of_states, number_of_states> &A() const {
+ return A_;
+ }
+ double A(int i, int j) const { return A()(i, j); }
+ const Eigen::Matrix<double, number_of_states, number_of_inputs> &B() const {
+ return B_;
+ }
+ double B(int i, int j) const { return B()(i, j); }
+ const Eigen::Matrix<double, number_of_outputs, number_of_states> &C() const {
+ return C_;
+ }
+ double C(int i, int j) const { return C()(i, j); }
+ const Eigen::Matrix<double, number_of_outputs, number_of_inputs> &D() const {
+ return D_;
+ }
+ double D(int i, int j) const { return D()(i, j); }
+ const Eigen::Matrix<double, number_of_inputs, 1> &U_min() const {
+ return U_min_;
+ }
+ double U_min(int i, int j) const { return U_min()(i, j); }
+ const Eigen::Matrix<double, number_of_inputs, 1> &U_max() const {
+ return U_max_;
+ }
+ double U_max(int i, int j) const { return U_max()(i, j); }
+
+ private:
+ const Eigen::Matrix<double, number_of_states, number_of_states> A_;
+ const Eigen::Matrix<double, number_of_states, number_of_inputs> B_;
+ const Eigen::Matrix<double, number_of_outputs, number_of_states> C_;
+ const Eigen::Matrix<double, number_of_outputs, number_of_inputs> D_;
+ const Eigen::Matrix<double, number_of_inputs, 1> U_min_;
+ const Eigen::Matrix<double, number_of_inputs, 1> U_max_;
+
+ StateFeedbackPlantCoefficients &operator=(
+ StateFeedbackPlantCoefficients other) = delete;
+};
+
+template <int number_of_states, int number_of_inputs, int number_of_outputs>
+class StateFeedbackPlant {
+ public:
+ EIGEN_MAKE_ALIGNED_OPERATOR_NEW;
+
+ StateFeedbackPlant(
+ ::std::vector< ::std::unique_ptr<StateFeedbackPlantCoefficients<
+ number_of_states, number_of_inputs, number_of_outputs>>> *
+ coefficients)
+ : coefficients_(::std::move(*coefficients)), plant_index_(0) {
+ Reset();
+ }
+
+ StateFeedbackPlant(StateFeedbackPlant &&other)
+ : plant_index_(other.plant_index_) {
+ ::std::swap(coefficients_, other.coefficients_);
+ X_.swap(other.X_);
+ Y_.swap(other.Y_);
+ U_.swap(other.U_);
+ }
+
+ virtual ~StateFeedbackPlant() {}
+
+ const Eigen::Matrix<double, number_of_states, number_of_states> &A() const {
+ return coefficients().A();
+ }
+ double A(int i, int j) const { return A()(i, j); }
+ const Eigen::Matrix<double, number_of_states, number_of_inputs> &B() const {
+ return coefficients().B();
+ }
+ double B(int i, int j) const { return B()(i, j); }
+ const Eigen::Matrix<double, number_of_outputs, number_of_states> &C() const {
+ return coefficients().C();
+ }
+ double C(int i, int j) const { return C()(i, j); }
+ const Eigen::Matrix<double, number_of_outputs, number_of_inputs> &D() const {
+ return coefficients().D();
+ }
+ double D(int i, int j) const { return D()(i, j); }
+ const Eigen::Matrix<double, number_of_inputs, 1> &U_min() const {
+ return coefficients().U_min();
+ }
+ double U_min(int i, int j) const { return U_min()(i, j); }
+ const Eigen::Matrix<double, number_of_inputs, 1> &U_max() const {
+ return coefficients().U_max();
+ }
+ double U_max(int i, int j) const { return U_max()(i, j); }
+
+ const Eigen::Matrix<double, number_of_states, 1> &X() const { return X_; }
+ double X(int i, int j) const { return X()(i, j); }
+ const Eigen::Matrix<double, number_of_outputs, 1> &Y() const { return Y_; }
+ double Y(int i, int j) const { return Y()(i, j); }
+ const Eigen::Matrix<double, number_of_inputs, 1> &U() const { return U_; }
+ double U(int i, int j) const { return U()(i, j); }
+
+ Eigen::Matrix<double, number_of_states, 1> &mutable_X() { return X_; }
+ double &mutable_X(int i, int j) { return mutable_X()(i, j); }
+ Eigen::Matrix<double, number_of_outputs, 1> &mutable_Y() { return Y_; }
+ double &mutable_Y(int i, int j) { return mutable_Y()(i, j); }
+ Eigen::Matrix<double, number_of_inputs, 1> &mutable_U() { return U_; }
+ double &mutable_U(int i, int j) { return mutable_U()(i, j); }
+
+ const StateFeedbackPlantCoefficients<
+ number_of_states, number_of_inputs, number_of_outputs>
+ &coefficients() const {
+ return *coefficients_[plant_index_];
+ }
+
+ int plant_index() const { return plant_index_; }
+ void set_plant_index(int plant_index) {
+ if (plant_index < 0) {
+ plant_index_ = 0;
+ } else if (plant_index >= static_cast<int>(coefficients_.size())) {
+ plant_index_ = static_cast<int>(coefficients_.size()) - 1;
+ } else {
+ plant_index_ = plant_index;
+ }
+ }
+
+ void Reset() {
+ X_.setZero();
+ Y_.setZero();
+ U_.setZero();
+ }
+
+ // Assert that U is within the hardware range.
+ virtual void CheckU() {
+ for (int i = 0; i < kNumInputs; ++i) {
+ assert(U(i, 0) <= U_max(i, 0) + 0.00001);
+ assert(U(i, 0) >= U_min(i, 0) - 0.00001);
+ }
+ }
+
+ // Computes the new X and Y given the control input.
+ void Update() {
+ // Powers outside of the range are more likely controller bugs than things
+ // that the plant should deal with.
+ CheckU();
+ X_ = A() * X() + B() * U();
+ Y_ = C() * X() + D() * U();
+ }
+
+ protected:
+ // these are accessible from non-templated subclasses
+ static const int kNumStates = number_of_states;
+ static const int kNumOutputs = number_of_outputs;
+ static const int kNumInputs = number_of_inputs;
+
+ private:
+ Eigen::Matrix<double, number_of_states, 1> X_;
+ Eigen::Matrix<double, number_of_outputs, 1> Y_;
+ Eigen::Matrix<double, number_of_inputs, 1> U_;
+
+ ::std::vector< ::std::unique_ptr<StateFeedbackPlantCoefficients<
+ number_of_states, number_of_inputs, number_of_outputs>>> coefficients_;
+
+ int plant_index_;
+
+ DISALLOW_COPY_AND_ASSIGN(StateFeedbackPlant);
+};
+
+// A Controller is a structure which holds a plant and the K and L matrices.
+// This is designed such that multiple controllers can share one set of state to
+// support gain scheduling easily.
+template <int number_of_states, int number_of_inputs, int number_of_outputs>
+struct StateFeedbackController final {
+ EIGEN_MAKE_ALIGNED_OPERATOR_NEW;
+
+ const Eigen::Matrix<double, number_of_states, number_of_outputs> L;
+ const Eigen::Matrix<double, number_of_inputs, number_of_states> K;
+ const Eigen::Matrix<double, number_of_states, number_of_states> A_inv;
+ StateFeedbackPlantCoefficients<number_of_states, number_of_inputs,
+ number_of_outputs> plant;
+
+ StateFeedbackController(
+ const Eigen::Matrix<double, number_of_states, number_of_outputs> &L,
+ const Eigen::Matrix<double, number_of_inputs, number_of_states> &K,
+ const Eigen::Matrix<double, number_of_states, number_of_states> &A_inv,
+ const StateFeedbackPlantCoefficients<number_of_states, number_of_inputs,
+ number_of_outputs> &plant)
+ : L(L),
+ K(K),
+ A_inv(A_inv),
+ plant(plant) {
+ }
+};
+
+template <int number_of_states, int number_of_inputs, int number_of_outputs>
+class StateFeedbackLoop {
+ public:
+ EIGEN_MAKE_ALIGNED_OPERATOR_NEW;
+
+ StateFeedbackLoop(const StateFeedbackController<
+ number_of_states, number_of_inputs, number_of_outputs> &controller)
+ : controller_index_(0) {
+ controllers_.emplace_back(new StateFeedbackController<
+ number_of_states, number_of_inputs, number_of_outputs>(controller));
+ Reset();
+ }
+
+ StateFeedbackLoop(
+ const Eigen::Matrix<double, number_of_states, number_of_outputs> &L,
+ const Eigen::Matrix<double, number_of_inputs, number_of_states> &K,
+ const Eigen::Matrix<double, number_of_states, number_of_states> &A_inv,
+ const StateFeedbackPlantCoefficients<number_of_states, number_of_inputs,
+ number_of_outputs> &plant)
+ : controller_index_(0) {
+ controllers_.emplace_back(
+ new StateFeedbackController<number_of_states, number_of_inputs,
+ number_of_outputs>(L, K, A_inv, plant));
+
+ Reset();
+ }
+
+ StateFeedbackLoop(::std::vector< ::std::unique_ptr<StateFeedbackController<
+ number_of_states, number_of_inputs, number_of_outputs>>> *controllers)
+ : controllers_(::std::move(*controllers)), controller_index_(0) {
+ Reset();
+ }
+
+ StateFeedbackLoop(StateFeedbackLoop &&other) {
+ X_hat_.swap(other.X_hat_);
+ R_.swap(other.R_);
+ U_.swap(other.U_);
+ U_uncapped_.swap(other.U_uncapped_);
+ ::std::swap(controllers_, other.controllers_);
+ controller_index_ = other.controller_index_;
+ }
+
+ virtual ~StateFeedbackLoop() {}
+
+ const Eigen::Matrix<double, number_of_states, number_of_states> &A() const {
+ return controller().plant.A();
+ }
+ double A(int i, int j) const { return A()(i, j); }
+ const Eigen::Matrix<double, number_of_states, number_of_inputs> &B() const {
+ return controller().plant.B();
+ }
+ const Eigen::Matrix<double, number_of_states, number_of_states> &A_inv() const {
+ return controller().A_inv;
+ }
+ double A_inv(int i, int j) const { return A_inv()(i, j); }
+ double B(int i, int j) const { return B()(i, j); }
+ const Eigen::Matrix<double, number_of_outputs, number_of_states> &C() const {
+ return controller().plant.C();
+ }
+ double C(int i, int j) const { return C()(i, j); }
+ const Eigen::Matrix<double, number_of_outputs, number_of_inputs> &D() const {
+ return controller().plant.D();
+ }
+ double D(int i, int j) const { return D()(i, j); }
+ const Eigen::Matrix<double, number_of_inputs, 1> &U_min() const {
+ return controller().plant.U_min();
+ }
+ double U_min(int i, int j) const { return U_min()(i, j); }
+ const Eigen::Matrix<double, number_of_inputs, 1> &U_max() const {
+ return controller().plant.U_max();
+ }
+ double U_max(int i, int j) const { return U_max()(i, j); }
+
+ const Eigen::Matrix<double, number_of_inputs, number_of_states> &K() const {
+ return controller().K;
+ }
+ double K(int i, int j) const { return K()(i, j); }
+ const Eigen::Matrix<double, number_of_states, number_of_outputs> &L() const {
+ return controller().L;
+ }
+ double L(int i, int j) const { return L()(i, j); }
+
+ const Eigen::Matrix<double, number_of_states, 1> &X_hat() const {
+ return X_hat_;
+ }
+ double X_hat(int i, int j) const { return X_hat()(i, j); }
+ const Eigen::Matrix<double, number_of_states, 1> &R() const { return R_; }
+ double R(int i, int j) const { return R()(i, j); }
+ const Eigen::Matrix<double, number_of_inputs, 1> &U() const { return U_; }
+ double U(int i, int j) const { return U()(i, j); }
+ const Eigen::Matrix<double, number_of_inputs, 1> &U_uncapped() const {
+ return U_uncapped_;
+ }
+ double U_uncapped(int i, int j) const { return U_uncapped()(i, j); }
+
+ Eigen::Matrix<double, number_of_states, 1> &mutable_X_hat() { return X_hat_; }
+ double &mutable_X_hat(int i, int j) { return mutable_X_hat()(i, j); }
+ Eigen::Matrix<double, number_of_states, 1> &mutable_R() { return R_; }
+ double &mutable_R(int i, int j) { return mutable_R()(i, j); }
+ Eigen::Matrix<double, number_of_inputs, 1> &mutable_U() { return U_; }
+ double &mutable_U(int i, int j) { return mutable_U()(i, j); }
+ Eigen::Matrix<double, number_of_inputs, 1> &mutable_U_uncapped() {
+ return U_uncapped_;
+ }
+ double &mutable_U_uncapped(int i, int j) {
+ return mutable_U_uncapped()(i, j);
+ }
+
+ const StateFeedbackController<number_of_states, number_of_inputs,
+ number_of_outputs> &controller() const {
+ return *controllers_[controller_index_];
+ }
+
+ const StateFeedbackController<number_of_states, number_of_inputs,
+ number_of_outputs> &controller(
+ int index) const {
+ return *controllers_[index];
+ }
+
+ void Reset() {
+ X_hat_.setZero();
+ R_.setZero();
+ U_.setZero();
+ U_uncapped_.setZero();
+ }
+
+ // If U is outside the hardware range, limit it before the plant tries to use
+ // it.
+ virtual void CapU() {
+ for (int i = 0; i < kNumInputs; ++i) {
+ if (U(i, 0) > U_max(i, 0)) {
+ U_(i, 0) = U_max(i, 0);
+ } else if (U(i, 0) < U_min(i, 0)) {
+ U_(i, 0) = U_min(i, 0);
+ }
+ }
+ }
+
+ // Corrects X_hat given the observation in Y.
+ void Correct(const Eigen::Matrix<double, number_of_outputs, 1> &Y) {
+ X_hat_ += A_inv() * L() * (Y - C() * X_hat_ - D() * U());
+ }
+
+ // stop_motors is whether or not to output all 0s.
+ void Update(bool stop_motors) {
+ if (stop_motors) {
+ U_.setZero();
+ U_uncapped_.setZero();
+ } else {
+ U_ = U_uncapped_ = K() * (R() - X_hat());
+ CapU();
+ }
+
+ UpdateObserver();
+ }
+
+ void UpdateObserver() {
+ X_hat_ = A() * X_hat() + B() * U();
+ }
+
+ // Sets the current controller to be index, clamped to be within range.
+ void set_controller_index(int index) {
+ if (index < 0) {
+ controller_index_ = 0;
+ } else if (index >= static_cast<int>(controllers_.size())) {
+ controller_index_ = static_cast<int>(controllers_.size()) - 1;
+ } else {
+ controller_index_ = index;
+ }
+ }
+
+ int controller_index() const { return controller_index_; }
+
+ protected:
+ ::std::vector< ::std::unique_ptr<StateFeedbackController<
+ number_of_states, number_of_inputs, number_of_outputs>>> controllers_;
+
+ // These are accessible from non-templated subclasses.
+ static const int kNumStates = number_of_states;
+ static const int kNumOutputs = number_of_outputs;
+ static const int kNumInputs = number_of_inputs;
+
+ private:
+ Eigen::Matrix<double, number_of_states, 1> X_hat_;
+ Eigen::Matrix<double, number_of_states, 1> R_;
+ Eigen::Matrix<double, number_of_inputs, 1> U_;
+ Eigen::Matrix<double, number_of_inputs, 1> U_uncapped_;
+
+ int controller_index_;
+
+ DISALLOW_COPY_AND_ASSIGN(StateFeedbackLoop);
+};
+
+#endif // FRC971_CONTROL_LOOPS_STATE_FEEDBACK_LOOP_H_
diff --git a/frc971/control_loops/state_feedback_loop_test.cc b/frc971/control_loops/state_feedback_loop_test.cc
new file mode 100644
index 0000000..f355b6d
--- /dev/null
+++ b/frc971/control_loops/state_feedback_loop_test.cc
@@ -0,0 +1,49 @@
+#include "frc971/control_loops/state_feedback_loop.h"
+
+#include "gtest/gtest.h"
+
+namespace testing {
+
+// Tests that everything compiles and nothing crashes even if
+// number_of_inputs!=number_of_outputs.
+// There used to be lots of bugs in this area.
+TEST(StateFeedbackLoopTest, UnequalSizes) {
+ // In general, most (all?) errors will make these statements either not
+ // compile or have assertion failures at runtime.
+ const StateFeedbackPlantCoefficients<2, 4, 7> coefficients(
+ Eigen::Matrix<double, 2, 2>::Identity(),
+ Eigen::Matrix<double, 2, 4>::Identity(),
+ Eigen::Matrix<double, 7, 2>::Identity(),
+ Eigen::Matrix<double, 7, 4>::Identity(),
+ Eigen::Matrix<double, 4, 1>::Constant(1),
+ Eigen::Matrix<double, 4, 1>::Constant(-1));
+
+ {
+ ::std::vector< ::std::unique_ptr<StateFeedbackPlantCoefficients<2, 4, 7>>> v;
+ v.emplace_back(new StateFeedbackPlantCoefficients<2, 4, 7>(coefficients));
+ StateFeedbackPlant<2, 4, 7> plant(&v);
+ plant.Update();
+ plant.Reset();
+ plant.CheckU();
+ }
+ {
+ StateFeedbackLoop<2, 4, 7> test_loop(StateFeedbackController<2, 4, 7>(
+ Eigen::Matrix<double, 2, 7>::Identity(),
+ Eigen::Matrix<double, 4, 2>::Identity(),
+ Eigen::Matrix<double, 2, 2>::Identity(), coefficients));
+ test_loop.Correct(Eigen::Matrix<double, 7, 1>::Identity());
+ test_loop.Update(false);
+ test_loop.CapU();
+ }
+ {
+ StateFeedbackLoop<2, 4, 7> test_loop(
+ Eigen::Matrix<double, 2, 7>::Identity(),
+ Eigen::Matrix<double, 4, 2>::Identity(),
+ Eigen::Matrix<double, 2, 2>::Identity(), coefficients);
+ test_loop.Correct(Eigen::Matrix<double, 7, 1>::Identity());
+ test_loop.Update(false);
+ test_loop.CapU();
+ }
+}
+
+} // namespace testing
diff --git a/frc971/control_loops/team_number_test_environment.cc b/frc971/control_loops/team_number_test_environment.cc
new file mode 100644
index 0000000..624a119
--- /dev/null
+++ b/frc971/control_loops/team_number_test_environment.cc
@@ -0,0 +1,15 @@
+#include "frc971/control_loops/team_number_test_environment.h"
+
+#include "aos/common/network/team_number.h"
+
+namespace frc971 {
+namespace control_loops {
+namespace testing {
+
+void TeamNumberEnvironment::SetUp() {
+ ::aos::network::OverrideTeamNumber(kTeamNumber);
+}
+
+} // namespace testing
+} // namespace control_loops
+} // namespace frc971
diff --git a/frc971/control_loops/team_number_test_environment.h b/frc971/control_loops/team_number_test_environment.h
new file mode 100644
index 0000000..f0c1a16
--- /dev/null
+++ b/frc971/control_loops/team_number_test_environment.h
@@ -0,0 +1,30 @@
+#ifndef FRC971_CONTROL_LOOPS_TEAM_NUMBER_TEST_ENVIRONMENT_H_
+#define FRC971_CONTROL_LOOPS_TEAM_NUMBER_TEST_ENVIRONMENT_H_
+
+#include "gtest/gtest.h"
+
+namespace frc971 {
+namespace control_loops {
+namespace testing {
+
+// The team number we use for tests.
+static const int kTeamNumber = 1;
+
+// Overrides the team number to kTeamNumber before any test consructors run.
+// This is important for tests which retrieve constants values during
+// construction.
+class TeamNumberEnvironment : public ::testing::Environment {
+ public:
+ void SetUp() override;
+};
+
+// The static variable in a header is intentional. Kind of a hack, undefined
+// order, but that works OK here.
+static ::testing::Environment* const team_number_env =
+ ::testing::AddGlobalTestEnvironment(new TeamNumberEnvironment());
+
+} // namespace testing
+} // namespace control_loops
+} // namespace frc971
+
+#endif // FRC971_CONTROL_LOOPS_TEAM_NUMBER_TEST_ENVIRONMENT_H_
diff --git a/frc971/control_loops/voltage_cap/voltage_cap.cc b/frc971/control_loops/voltage_cap/voltage_cap.cc
new file mode 100644
index 0000000..3de5828
--- /dev/null
+++ b/frc971/control_loops/voltage_cap/voltage_cap.cc
@@ -0,0 +1,95 @@
+#include "voltage_cap.h"
+
+#include <limits>
+
+namespace frc971 {
+namespace control_loops {
+
+void VoltageCap(double max_voltage, double voltage_one, double voltage_two,
+ double *out_voltage_one, double *out_voltage_two) {
+ *out_voltage_one = *out_voltage_two =
+ ::std::numeric_limits<double>::quiet_NaN();
+ if (voltage_one <= max_voltage && voltage_two <= max_voltage &&
+ voltage_one >= -max_voltage && voltage_two >= -max_voltage) {
+ *out_voltage_one = voltage_one;
+ *out_voltage_two = voltage_two;
+ } else if (voltage_one - voltage_two > 2 * max_voltage) {
+ //(voltage_one is larger)
+ // if the difference between the two voltages is more than 24 we know a
+ // 45degree slope will not intersect the 'box'
+ *out_voltage_one = max_voltage;
+ *out_voltage_two = -max_voltage;
+ // top left corner of 'box'
+ } else if (voltage_one - voltage_two < -2 * max_voltage) {
+ //(voltage_two is larger)
+ // if the difference between the two voltages is less than -24 we know a
+ // 45degree slope will not intersect the 'box'
+ *out_voltage_one = -max_voltage;
+ *out_voltage_two = max_voltage;
+ // bottom right corner of 'box'
+ } else {
+ if (voltage_one >= 0) {
+ if (voltage_two >= 0) {
+ // Quadrant 1
+ if (voltage_one > voltage_two) {
+ // Above box
+ double difference = voltage_one - voltage_two;
+ *out_voltage_one = max_voltage;
+ *out_voltage_two = max_voltage - difference;
+ } else {
+ // Right of box
+ double difference = voltage_two - voltage_one;
+ *out_voltage_one = max_voltage - difference;
+ *out_voltage_two = max_voltage;
+ }
+ } else { // voltage_two < 0
+ // Quadrant 2
+ double difference = voltage_one - voltage_two;
+ if (-voltage_two > voltage_one) {
+ // Left of box
+ *out_voltage_one = -max_voltage + difference;
+ *out_voltage_two = -max_voltage;
+ } else {
+ // Above box
+ *out_voltage_one = max_voltage;
+ *out_voltage_two = max_voltage - difference;
+ }
+ }
+ } else { // voltage_one < 0
+ if (voltage_two <= 0) {
+ // Quadrant 3
+ if (voltage_one < voltage_two) {
+ // Left of Box
+ double difference = voltage_two - voltage_one;
+ *out_voltage_one = -max_voltage;
+ *out_voltage_two = -max_voltage + difference;
+ } else {
+ // Under box
+ double difference = voltage_one - voltage_two;
+ *out_voltage_one = -max_voltage + difference;
+ *out_voltage_two = -max_voltage;
+ }
+ } else { // voltage_two > 0
+ // Quadrant 4
+ double difference = voltage_two - voltage_one;
+ if (-voltage_one > voltage_two) {
+ // Right of box
+ *out_voltage_one = -max_voltage;
+ *out_voltage_two = -max_voltage + difference;
+ } else {
+ // Under box
+ *out_voltage_one = max_voltage - difference;
+ *out_voltage_two = max_voltage;
+ }
+ }
+ }
+ }
+}
+
+void VoltageCap(double voltage_one, double voltage_two, double *out_voltage_one,
+ double *out_voltage_two) {
+ VoltageCap(12.0, voltage_one, voltage_two, out_voltage_one, out_voltage_two);
+}
+
+} // namespace control_loops
+} // namespace frc971
diff --git a/frc971/control_loops/voltage_cap/voltage_cap.gyp b/frc971/control_loops/voltage_cap/voltage_cap.gyp
new file mode 100644
index 0000000..08bd27a
--- /dev/null
+++ b/frc971/control_loops/voltage_cap/voltage_cap.gyp
@@ -0,0 +1,23 @@
+{
+ 'targets': [
+ {
+ 'target_name': 'voltage_cap',
+ 'type': 'static_library',
+ 'sources': [
+ 'voltage_cap.cc',
+ ],
+ },
+ {
+ 'target_name': 'voltage_cap_test',
+ 'type': 'executable',
+ 'sources': [
+ 'voltage_cap_test.cc',
+ ],
+ 'dependencies': [
+ 'voltage_cap',
+ '<(EXTERNALS):gtest',
+ '<(AOS)/common/common.gyp:queue_testutils',
+ ],
+ },
+ ],
+}
diff --git a/frc971/control_loops/voltage_cap/voltage_cap.h b/frc971/control_loops/voltage_cap/voltage_cap.h
new file mode 100644
index 0000000..49aa9e8
--- /dev/null
+++ b/frc971/control_loops/voltage_cap/voltage_cap.h
@@ -0,0 +1,33 @@
+#ifndef FRC971_CONTROL_LOOPS_VOLTAGE_CAP_VOLTAGE_CAP_H_
+#define FRC971_CONTROL_LOOPS_VOLTAGE_CAP_VOLTAGE_CAP_H_
+
+#include <stdio.h>
+
+namespace frc971 {
+namespace control_loops {
+
+// This function maintains the difference of power between two voltages passed in
+// that are outside of our range of possible voltage output.
+// This is because we figured that maintaining the difference rather than the ratio
+// between the voltages would get us to our goal as fast as possible.
+//
+//
+// The 'box' is a box formed on a graph by the maximum and minimun voltages of
+// voltage_one and voltage_two with
+// voltage_one on the y-axis and voltage_two on the x-axis.
+// If a line with a slope of one(45degrees) is plotted from the point formed
+// by graphing voltage_one(y) and voltage_two(x), the first intersecting point of
+// the box is the maximum voltage that we can output to get to the goal as
+// fast as possible.
+// If the line does not intersect the box, then we use the closest corner of
+// the box to the line in either quadrant two or quadrant four of the graph.
+void VoltageCap(double max_voltage, double voltage_one, double voltage_two,
+ double *out_voltage_one, double *out_voltage_two);
+ // Defaults to 12v if no voltage is specified.
+void VoltageCap(double voltage_one, double voltage_two, double *out_voltage_one,
+ double *out_voltage_two);
+
+} // namespace control_loops
+} // namespace frc971
+
+#endif // FRC971_CONTROL_LOOPS_VOLTAGE_CAP_VOLTAGE_CAP_H_
diff --git a/frc971/control_loops/voltage_cap/voltage_cap_test.cc b/frc971/control_loops/voltage_cap/voltage_cap_test.cc
new file mode 100644
index 0000000..f508b25
--- /dev/null
+++ b/frc971/control_loops/voltage_cap/voltage_cap_test.cc
@@ -0,0 +1,253 @@
+#include <unistd.h>
+
+#include "frc971/control_loops/voltage_cap/voltage_cap.h"
+
+#include "gtest/gtest.h"
+#include "aos/common/queue.h"
+#include "aos/common/queue_testutils.h"
+
+using ::aos::time::Time;
+
+namespace frc971 {
+namespace control_loops {
+namespace testing {
+
+class VoltageTest : public ::testing::Test {
+ protected:
+ // Bring up and down Core.
+ ::aos::common::testing::GlobalCoreInstance my_core;
+};
+
+// Tests that voltage inputs return the same if inside the box.
+TEST_F(VoltageTest, BasicVoltage12) {
+ double voltage_one, voltage_two;
+ VoltageCap(12.0, 11.3, 2.6, &voltage_one, &voltage_two);
+ EXPECT_EQ(11.3, voltage_one);
+ EXPECT_EQ(2.6, voltage_two);
+}
+// Tests that voltage inputs in the 4th quadrant both get capped to their
+// maximum.
+TEST_F(VoltageTest, QuadrantFourNoIntersect12) {
+ double voltage_one, voltage_two;
+ VoltageCap(12.0, -50.0, 50, &voltage_one, &voltage_two);
+ EXPECT_EQ(-12.0, voltage_one);
+ EXPECT_EQ(12.0, voltage_two);
+}
+
+// Tests if the difference between two voltages is more than 24.0v then default
+// to the most outputable voltage in that direction.
+TEST_F(VoltageTest, LargeDifference12) {
+ double voltage_one, voltage_two;
+ VoltageCap(12.0, -13.0, 13.0, &voltage_one, &voltage_two);
+ EXPECT_EQ(-12.0, voltage_one); // just off bottom right corner
+ EXPECT_EQ(12.0, voltage_two);
+
+ VoltageCap(12.0, 13.0, -13.0, &voltage_one, &voltage_two);
+ EXPECT_EQ(12.0, voltage_one); // just off top left corner
+ EXPECT_EQ(-12.0, voltage_two);
+
+ VoltageCap(12.0, 10.0, 36.0, &voltage_one, &voltage_two);
+ EXPECT_EQ(-12.0, voltage_one); // 1st quadrant line just off bottom right
+ EXPECT_EQ(12.0, voltage_two);
+
+ VoltageCap(12.0, 10.0, -36.0, &voltage_one, &voltage_two);
+ EXPECT_EQ(12.0, voltage_one); // 3rd quadrant line just off top left
+ EXPECT_EQ(-12.0, voltage_two);
+}
+
+// Tests that the 45degree angle line intersects the box and returns a value
+// within the box.
+TEST_F(VoltageTest, QuadrantOneIntersect12) {
+ double voltage_one, voltage_two;
+ VoltageCap(12.0, 50.0, 50.0, &voltage_one, &voltage_two);
+ EXPECT_EQ(12.0, voltage_one); // 45degree angle from origin
+ EXPECT_EQ(12.0, voltage_two);
+
+ voltage_one = 0.0;
+
+ VoltageCap(12.0, 13.0, 11.0, &voltage_one, &voltage_two);
+ EXPECT_EQ(12.0, voltage_one);
+ EXPECT_EQ(10.0, voltage_two);
+}
+TEST_F(VoltageTest, QuadrantTwoIntersect12) {
+ double voltage_one, voltage_two;
+ VoltageCap(12.0, 13.0, -2.0, &voltage_one, &voltage_two);
+ EXPECT_EQ(12.0, voltage_one);
+ EXPECT_EQ(-3.0, voltage_two);
+
+ VoltageCap(12.0, 2.0, -13.0, &voltage_one, &voltage_two);
+ EXPECT_EQ(3.0, voltage_one);
+ EXPECT_EQ(-12.0, voltage_two);
+}
+TEST_F(VoltageTest, QuadrantThreeIntersect12) {
+ double voltage_one, voltage_two;
+ VoltageCap(12.0, -50.0, -50.0, &voltage_one, &voltage_two);
+ EXPECT_EQ(-12.0, voltage_one); // 45degree angle from origin
+ EXPECT_EQ(-12.0, voltage_two);
+
+ voltage_one = 0.0;
+
+ VoltageCap(12.0, -13.0, -11.0, &voltage_one, &voltage_two);
+ EXPECT_EQ(-12.0, voltage_one);
+ EXPECT_EQ(-10.0, voltage_two);
+}
+TEST_F(VoltageTest, QuadrantFourIntersect12) {
+ double voltage_one, voltage_two;
+ VoltageCap(12.0, -13.0, 2.0, &voltage_one, &voltage_two);
+ EXPECT_EQ(-12.0, voltage_one);
+ EXPECT_EQ(3.0, voltage_two);
+
+ VoltageCap(12.0, -2.0, 13.0, &voltage_one, &voltage_two);
+ EXPECT_EQ(-3.0, voltage_one);
+ EXPECT_EQ(12.0, voltage_two);
+}
+
+// Tests whether cross quadrants works (also supplies additional points to
+// test).
+TEST_F(VoltageTest, QuadrantOneToTwo12) {
+ // Point in Quadrant 1 intersects box in Quadrant 2.
+ double voltage_one, voltage_two;
+ VoltageCap(12.0, 33.0, 11.0, &voltage_one, &voltage_two);
+ EXPECT_EQ(12.0, voltage_one);
+ EXPECT_EQ(-10.0, voltage_two);
+}
+TEST_F(VoltageTest, QuadrantOneToFour12){
+ // Point in Quadrant 1 intersects box in Quadrant 4.
+ double voltage_one, voltage_two;
+ VoltageCap(12.0, 11.0, 33.0, &voltage_one, &voltage_two);
+ EXPECT_EQ(-10.0, voltage_one);
+ EXPECT_EQ(12.0, voltage_two);
+}
+TEST_F(VoltageTest, QuadrantThreeToTwo12) {
+ // Point in Quadrant 3 intersects box in Quadrant 2.
+ double voltage_one, voltage_two;
+ VoltageCap(12.0, -11.0, -33.0, &voltage_one, &voltage_two);
+ EXPECT_EQ(10.0, voltage_one);
+ EXPECT_EQ(-12.0, voltage_two);
+}
+TEST_F(VoltageTest, QuadrantThreeToFour12) {
+ // Point in Quadrant 3 intersects box in Quadrant 4.
+ double voltage_one, voltage_two;
+ VoltageCap(12.0, -33.0, -11.0, &voltage_one, &voltage_two);
+ EXPECT_EQ(-12.0, voltage_one);
+ EXPECT_EQ(10.0, voltage_two);
+}
+
+// Tests that voltage inputs return the same if inside the box.
+TEST_F(VoltageTest, BasicVoltage6) {
+ double voltage_one, voltage_two;
+ VoltageCap(6.0, 5.3, 2.6, &voltage_one, &voltage_two);
+ EXPECT_EQ(5.3, voltage_one);
+ EXPECT_EQ(2.6, voltage_two);
+}
+// Tests that voltage inputs in the 4th quadrant both get capped to their
+// maximum.
+TEST_F(VoltageTest, QuadrantFourNoIntersect6) {
+ double voltage_one, voltage_two;
+ VoltageCap(6.0, -50.0, 50, &voltage_one, &voltage_two);
+ EXPECT_EQ(-6.0, voltage_one);
+ EXPECT_EQ(6.0, voltage_two);
+}
+
+// Tests if the difference between two voltages is more than 12.0v then default
+// to the most outputable voltage in that direction.
+TEST_F(VoltageTest, LargeDifference6) {
+ double voltage_one, voltage_two;
+ VoltageCap(6.0, -13.0, 13.0, &voltage_one, &voltage_two);
+ EXPECT_EQ(-6.0, voltage_one); // just off bottom right corner
+ EXPECT_EQ(6.0, voltage_two);
+
+ VoltageCap(6.0, 13.0, -13.0, &voltage_one, &voltage_two);
+ EXPECT_EQ(6.0, voltage_one); // just off top left corner
+ EXPECT_EQ(-6.0, voltage_two);
+
+ VoltageCap(6.0, 10.0, 36.0, &voltage_one, &voltage_two);
+ EXPECT_EQ(-6.0, voltage_one); // 1st quadrant line just off bottom right
+ EXPECT_EQ(6.0, voltage_two);
+
+ VoltageCap(6.0, 10.0, -36.0, &voltage_one, &voltage_two);
+ EXPECT_EQ(6.0, voltage_one); // 3rd quadrant line just off top left
+ EXPECT_EQ(-6.0, voltage_two);
+}
+
+// Tests that the 45degree angle line intersects the box and returns a value
+// within the box
+TEST_F(VoltageTest, QuadrantOneIntersect6) {
+ double voltage_one, voltage_two;
+ VoltageCap(6.0, 50.0, 50.0, &voltage_one, &voltage_two);
+ EXPECT_EQ(6.0, voltage_one); // 45degree angle from origin
+ EXPECT_EQ(6.0, voltage_two);
+
+ voltage_one = 0.0;
+
+ VoltageCap(6.0, 13.0, 11.0, &voltage_one, &voltage_two);
+ EXPECT_EQ(6.0, voltage_one);
+ EXPECT_EQ(4.0, voltage_two);
+}
+TEST_F(VoltageTest, QuadrantTwoIntersect6) {
+ double voltage_one, voltage_two;
+ VoltageCap(6.0, 9.0, -2.0, &voltage_one, &voltage_two);
+ EXPECT_EQ(6.0, voltage_one);
+ EXPECT_EQ(-5.0, voltage_two);
+
+ VoltageCap(6.0, 2.0, -9.0, &voltage_one, &voltage_two);
+ EXPECT_EQ(5.0, voltage_one);
+ EXPECT_EQ(-6.0, voltage_two);
+}
+TEST_F(VoltageTest, QuadrantThreeIntersect6) {
+ double voltage_one, voltage_two;
+ VoltageCap(6.0, -50.0, -50.0, &voltage_one, &voltage_two);
+ EXPECT_EQ(-6.0, voltage_one); // 45degree angle from origin
+ EXPECT_EQ(-6.0, voltage_two);
+
+ voltage_one = 0.0;
+
+ VoltageCap(6.0, -13.0, -11.0, &voltage_one, &voltage_two);
+ EXPECT_EQ(-6.0, voltage_one);
+ EXPECT_EQ(-4.0, voltage_two);
+}
+TEST_F(VoltageTest, QuadrantFourIntersect6) {
+ double voltage_one, voltage_two;
+ VoltageCap(6.0, -9.0, 2.0, &voltage_one, &voltage_two);
+ EXPECT_EQ(-6.0, voltage_one);
+ EXPECT_EQ(5.0, voltage_two);
+
+ VoltageCap(6.0, -2.0, 9.0, &voltage_one, &voltage_two);
+ EXPECT_EQ(-5.0, voltage_one);
+ EXPECT_EQ(6.0, voltage_two);
+}
+
+// Tests whether cross quadrants works (also supplies additional points to
+// test).
+TEST_F(VoltageTest, QuadrantOneToTwo6) {
+ // Point in Quadrant 1 intersects box in Quadrant 2.
+ double voltage_one, voltage_two;
+ VoltageCap(6.0, 33.0, 22.0, &voltage_one, &voltage_two);
+ EXPECT_EQ(6.0, voltage_one);
+ EXPECT_EQ(-5.0, voltage_two);
+}
+TEST_F(VoltageTest, QuadrantOneToFour6){
+ // Point in Quadrant 1 intersects box in Quadrant 4.
+ double voltage_one, voltage_two;
+ VoltageCap(6.0, 22.0, 33.0, &voltage_one, &voltage_two);
+ EXPECT_EQ(-5.0, voltage_one);
+ EXPECT_EQ(6.0, voltage_two);
+}
+TEST_F(VoltageTest, QuadrantThreeToTw6) {
+ // Point in Quadrant 3 intersects box in Quadrant 2.
+ double voltage_one, voltage_two;
+ VoltageCap(6.0, -22.0, -33.0, &voltage_one, &voltage_two);
+ EXPECT_EQ(5.0, voltage_one);
+ EXPECT_EQ(-6.0, voltage_two);
+}
+TEST_F(VoltageTest, QuadrantThreeToFour6) {
+ // Point in Quadrant 3 intersects box in Quadrant 4.
+ double voltage_one, voltage_two;
+ VoltageCap(6.0, -33.0, -22.0, &voltage_one, &voltage_two);
+ EXPECT_EQ(-6.0, voltage_one);
+ EXPECT_EQ(5.0, voltage_two);
+}
+
+} // namespace testing
+} // namespace control_loops
+} // namespace frc971
diff --git a/frc971/control_loops/zeroed_joint.h b/frc971/control_loops/zeroed_joint.h
new file mode 100644
index 0000000..f25434f
--- /dev/null
+++ b/frc971/control_loops/zeroed_joint.h
@@ -0,0 +1,457 @@
+#ifndef FRC971_CONTROL_LOOPS_ZEROED_JOINT_H_
+#define FRC971_CONTROL_LOOPS_ZEROED_JOINT_H_
+
+#include <memory>
+
+#include "aos/common/controls/control_loop.h"
+#include "frc971/control_loops/state_feedback_loop.h"
+
+namespace frc971 {
+namespace control_loops {
+namespace testing {
+class WristTest_NoWindupPositive_Test;
+class WristTest_NoWindupNegative_Test;
+};
+
+// Note: Everything in this file assumes that there is a 1 cycle delay between
+// power being requested and it showing up at the motor. It assumes that
+// X_hat(2, 1) is the voltage being applied as well. It will go unstable if
+// that isn't true.
+
+template<int kNumZeroSensors>
+class ZeroedJoint;
+
+// This class implements the CapU function correctly given all the extra
+// information that we know about from the wrist motor.
+template<int kNumZeroSensors>
+class ZeroedStateFeedbackLoop : public StateFeedbackLoop<3, 1, 1> {
+ public:
+ ZeroedStateFeedbackLoop(StateFeedbackLoop<3, 1, 1> loop,
+ ZeroedJoint<kNumZeroSensors> *zeroed_joint)
+ : StateFeedbackLoop<3, 1, 1>(loop),
+ zeroed_joint_(zeroed_joint),
+ voltage_(0.0),
+ last_voltage_(0.0) {
+ }
+
+ // Caps U, but this time respects the state of the wrist as well.
+ virtual void CapU();
+
+ // Returns the accumulated voltage.
+ double voltage() const { return voltage_; }
+
+ // Returns the uncapped voltage.
+ double uncapped_voltage() const { return uncapped_voltage_; }
+
+ // Zeros the accumulator.
+ void ZeroPower() { voltage_ = 0.0; }
+ private:
+ ZeroedJoint<kNumZeroSensors> *zeroed_joint_;
+
+ // The accumulated voltage to apply to the motor.
+ double voltage_;
+ double last_voltage_;
+ double uncapped_voltage_;
+};
+
+template<int kNumZeroSensors>
+void ZeroedStateFeedbackLoop<kNumZeroSensors>::CapU() {
+ const double old_voltage = voltage_;
+ voltage_ += U(0, 0);
+
+ uncapped_voltage_ = voltage_;
+
+ // Do all our computations with the voltage, and then compute what the delta
+ // is to make that happen.
+ if (zeroed_joint_->state_ == ZeroedJoint<kNumZeroSensors>::READY) {
+ if (Y(0, 0) >= zeroed_joint_->config_data_.upper_limit) {
+ voltage_ = std::min(0.0, voltage_);
+ }
+ if (Y(0, 0) <= zeroed_joint_->config_data_.lower_limit) {
+ voltage_ = std::max(0.0, voltage_);
+ }
+ }
+
+ const bool is_ready =
+ zeroed_joint_->state_ == ZeroedJoint<kNumZeroSensors>::READY;
+ double limit = is_ready ?
+ 12.0 : zeroed_joint_->config_data_.max_zeroing_voltage;
+
+ // Make sure that reality and the observer can't get too far off. There is a
+ // delay by one cycle between the applied voltage and X_hat(2, 0), so compare
+ // against last cycle's voltage.
+ if (X_hat(2, 0) > last_voltage_ + 2.0) {
+ //X_hat(2, 0) = last_voltage_ + 2.0;
+ voltage_ -= X_hat(2, 0) - (last_voltage_ + 2.0);
+ LOG(DEBUG, "X_hat(2, 0) = %f\n", X_hat(2, 0));
+ } else if (X_hat(2, 0) < last_voltage_ -2.0) {
+ //X_hat(2, 0) = last_voltage_ - 2.0;
+ voltage_ += X_hat(2, 0) - (last_voltage_ - 2.0);
+ LOG(DEBUG, "X_hat(2, 0) = %f\n", X_hat(2, 0));
+ }
+
+ voltage_ = std::min(limit, voltage_);
+ voltage_ = std::max(-limit, voltage_);
+ U(0, 0) = voltage_ - old_voltage;
+ LOG(DEBUG, "abc %f\n", X_hat(2, 0) - voltage_);
+ LOG(DEBUG, "error %f\n", X_hat(0, 0) - R(0, 0));
+
+ last_voltage_ = voltage_;
+}
+
+
+// Class to zero and control a joint with any number of zeroing sensors with a
+// state feedback controller.
+template<int kNumZeroSensors>
+class ZeroedJoint {
+ public:
+ // Sturcture to hold the hardware configuration information.
+ struct ConfigurationData {
+ // Angle at the lower hardware limit.
+ double lower_limit;
+ // Angle at the upper hardware limit.
+ double upper_limit;
+ // Speed (and direction) to move while zeroing.
+ double zeroing_speed;
+ // Speed (and direction) to move while moving off the sensor.
+ double zeroing_off_speed;
+ // Maximum voltage to apply when zeroing.
+ double max_zeroing_voltage;
+ // Deadband voltage.
+ double deadband_voltage;
+ // Angles where we see a positive edge from the hall effect sensors.
+ double hall_effect_start_angle[kNumZeroSensors];
+ };
+
+ // Current position data for the encoder and hall effect information.
+ struct PositionData {
+ // Current encoder position.
+ double position;
+ // Array of hall effect values.
+ bool hall_effects[kNumZeroSensors];
+ // Array of the last positive edge position for the sensors.
+ double hall_effect_positions[kNumZeroSensors];
+ };
+
+ ZeroedJoint(StateFeedbackLoop<3, 1, 1> loop)
+ : loop_(new ZeroedStateFeedbackLoop<kNumZeroSensors>(loop, this)),
+ last_good_time_(0, 0),
+ state_(UNINITIALIZED),
+ error_count_(0),
+ zero_offset_(0.0),
+ capped_goal_(false) {
+ }
+
+ // Copies the provided configuration data locally.
+ void set_config_data(const ConfigurationData &config_data) {
+ config_data_ = config_data;
+ }
+
+ // Clips the goal to be inside the limits and returns the clipped goal.
+ // Requires the constants to have already been fetched.
+ double ClipGoal(double goal) const {
+ return ::std::min(config_data_.upper_limit,
+ std::max(config_data_.lower_limit, goal));
+ }
+
+ // Updates the loop and state machine.
+ // position is null if the position data is stale, output_enabled is true if
+ // the output will actually go to the motors, and goal_angle and goal_velocity
+ // are the goal position and velocities.
+ double Update(const ZeroedJoint<kNumZeroSensors>::PositionData *position,
+ bool output_enabled,
+ double goal_angle, double goal_velocity);
+
+ // True if the code is zeroing.
+ bool is_zeroing() const { return state_ == ZEROING; }
+
+ // True if the code is moving off the hall effect.
+ bool is_moving_off() const { return state_ == MOVING_OFF; }
+
+ // True if the state machine is uninitialized.
+ bool is_uninitialized() const { return state_ == UNINITIALIZED; }
+
+ // True if the state machine is ready.
+ bool is_ready() const { return state_ == READY; }
+
+ // Returns the uncapped voltage.
+ double U_uncapped() const { return loop_->U_uncapped(0, 0); }
+
+ // True if the goal was moved to avoid goal windup.
+ bool capped_goal() const { return capped_goal_; }
+
+ // Timestamp
+ static const double dt;
+
+ double absolute_position() const { return loop_->X_hat(0, 0); }
+
+ private:
+ friend class ZeroedStateFeedbackLoop<kNumZeroSensors>;
+ // Friend the wrist test cases so that they can simulate windeup.
+ friend class testing::WristTest_NoWindupPositive_Test;
+ friend class testing::WristTest_NoWindupNegative_Test;
+
+ static const ::aos::time::Time kRezeroTime;
+
+ // The state feedback control loop to talk to.
+ ::std::unique_ptr<ZeroedStateFeedbackLoop<kNumZeroSensors>> loop_;
+
+ ConfigurationData config_data_;
+
+ ::aos::time::Time last_good_time_;
+
+ // Returns the index of the first active sensor, or -1 if none are active.
+ int ActiveSensorIndex(
+ const ZeroedJoint<kNumZeroSensors>::PositionData *position) {
+ if (!position) {
+ return -1;
+ }
+ int active_index = -1;
+ for (int i = 0; i < kNumZeroSensors; ++i) {
+ if (position->hall_effects[i]) {
+ if (active_index != -1) {
+ LOG(ERROR, "More than one hall effect sensor is active\n");
+ } else {
+ active_index = i;
+ }
+ }
+ }
+ return active_index;
+ }
+ // Returns true if any of the sensors are active.
+ bool AnySensorsActive(
+ const ZeroedJoint<kNumZeroSensors>::PositionData *position) {
+ return ActiveSensorIndex(position) != -1;
+ }
+
+ // Enum to store the state of the internal zeroing state machine.
+ enum State {
+ UNINITIALIZED,
+ MOVING_OFF,
+ ZEROING,
+ READY,
+ ESTOP
+ };
+
+ // Internal state for zeroing.
+ State state_;
+
+ // Missed position packet count.
+ int error_count_;
+ // Offset from the raw encoder value to the absolute angle.
+ double zero_offset_;
+ // Position that gets incremented when zeroing the wrist to slowly move it to
+ // the hall effect sensor.
+ double zeroing_position_;
+ // Last position at which the hall effect sensor was off.
+ double last_off_position_;
+
+ // True if the zeroing goal was capped during this cycle.
+ bool capped_goal_;
+
+ // Returns true if number is between first and second inclusive.
+ bool is_between(double first, double second, double number) {
+ if ((number >= first || number >= second) &&
+ (number <= first || number <= second)) {
+ return true;
+ }
+ return false;
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(ZeroedJoint);
+};
+
+template <int kNumZeroSensors>
+const ::aos::time::Time ZeroedJoint<kNumZeroSensors>::kRezeroTime =
+ ::aos::time::Time::InSeconds(2);
+
+template <int kNumZeroSensors>
+/*static*/ const double ZeroedJoint<kNumZeroSensors>::dt = 0.01;
+
+// Updates the zeroed joint controller and state machine.
+template <int kNumZeroSensors>
+double ZeroedJoint<kNumZeroSensors>::Update(
+ const ZeroedJoint<kNumZeroSensors>::PositionData *position,
+ bool output_enabled,
+ double goal_angle, double goal_velocity) {
+ // Uninitialize the bot if too many cycles pass without an encoder.
+ if (position == NULL) {
+ LOG(WARNING, "no new pos given\n");
+ error_count_++;
+ }
+ if (error_count_ >= 4) {
+ output_enabled = false;
+ LOG(WARNING, "err_count is %d so disabling\n", error_count_);
+
+ if ((::aos::time::Time::Now() - last_good_time_) > kRezeroTime) {
+ LOG(WARNING, "err_count is %d (or 1st time) so forcing a re-zero\n",
+ error_count_);
+ state_ = UNINITIALIZED;
+ loop_->Reset();
+ }
+ }
+ if (position != NULL) {
+ last_good_time_ = ::aos::time::Time::Now();
+ error_count_ = 0;
+ }
+
+ // Compute the absolute position of the wrist.
+ double absolute_position;
+ if (position) {
+ absolute_position = position->position;
+ if (state_ == READY) {
+ absolute_position -= zero_offset_;
+ }
+ loop_->Y << absolute_position;
+ if (!AnySensorsActive(position)) {
+ last_off_position_ = position->position;
+ }
+ } else {
+ // Dead recon for now.
+ absolute_position = loop_->X_hat(0, 0);
+ }
+
+ switch (state_) {
+ case UNINITIALIZED:
+ LOG(DEBUG, "UNINITIALIZED\n");
+ if (position) {
+ // Reset the zeroing goal.
+ zeroing_position_ = absolute_position;
+ // Clear the observer state.
+ loop_->X_hat << absolute_position, 0.0, 0.0;
+ loop_->ZeroPower();
+ // Set the goal to here to make it so it doesn't move when disabled.
+ loop_->R = loop_->X_hat;
+ // Only progress if we are enabled.
+ if (::aos::joystick_state->enabled) {
+ if (AnySensorsActive(position)) {
+ state_ = MOVING_OFF;
+ } else {
+ state_ = ZEROING;
+ }
+ }
+ }
+ break;
+ case MOVING_OFF:
+ LOG(DEBUG, "MOVING_OFF\n");
+ {
+ // Move off the hall effect sensor.
+ if (!::aos::joystick_state->enabled) {
+ // Start over if disabled.
+ state_ = UNINITIALIZED;
+ } else if (position && !AnySensorsActive(position)) {
+ // We are now off the sensor. Time to zero now.
+ state_ = ZEROING;
+ } else {
+ // Slowly creep off the sensor.
+ zeroing_position_ -= config_data_.zeroing_off_speed * dt;
+ loop_->R << zeroing_position_, -config_data_.zeroing_off_speed, 0.0;
+ break;
+ }
+ }
+ case ZEROING:
+ LOG(DEBUG, "ZEROING\n");
+ {
+ int active_sensor_index = ActiveSensorIndex(position);
+ if (!::aos::joystick_state->enabled) {
+ // Start over if disabled.
+ state_ = UNINITIALIZED;
+ } else if (position && active_sensor_index != -1) {
+ state_ = READY;
+ // Verify that the calibration number is between the last off position
+ // and the current on position. If this is not true, move off and try
+ // again.
+ const double calibration =
+ position->hall_effect_positions[active_sensor_index];
+ if (!is_between(last_off_position_, position->position,
+ calibration)) {
+ LOG(ERROR, "Got a bogus calibration number. Trying again.\n");
+ LOG(ERROR,
+ "Last off position was %f, current is %f, calibration is %f\n",
+ last_off_position_, position->position,
+ position->hall_effect_positions[active_sensor_index]);
+ state_ = MOVING_OFF;
+ } else {
+ // Save the zero, and then offset the observer to deal with the
+ // phantom step change.
+ const double old_zero_offset = zero_offset_;
+ zero_offset_ =
+ position->hall_effect_positions[active_sensor_index] -
+ config_data_.hall_effect_start_angle[active_sensor_index];
+ loop_->X_hat(0, 0) += old_zero_offset - zero_offset_;
+ loop_->Y(0, 0) += old_zero_offset - zero_offset_;
+ }
+ } else {
+ // Slowly creep towards the sensor.
+ zeroing_position_ += config_data_.zeroing_speed * dt;
+ loop_->R << zeroing_position_, config_data_.zeroing_speed, 0.0;
+ }
+ break;
+ }
+
+ case READY:
+ LOG(DEBUG, "READY\n");
+ {
+ const double limited_goal = ClipGoal(goal_angle);
+ loop_->R << limited_goal, goal_velocity, 0.0;
+ break;
+ }
+
+ case ESTOP:
+ LOG(DEBUG, "ESTOP\n");
+ LOG(WARNING, "have already given up\n");
+ return 0.0;
+ }
+
+ // Update the observer.
+ loop_->Update(position != NULL, !output_enabled);
+
+ LOG(DEBUG, "X_hat={%f, %f, %f}\n",
+ loop_->X_hat(0, 0), loop_->X_hat(1, 0), loop_->X_hat(2, 0));
+
+ capped_goal_ = false;
+ // Verify that the zeroing goal hasn't run away.
+ switch (state_) {
+ case UNINITIALIZED:
+ case READY:
+ case ESTOP:
+ // Not zeroing. No worries.
+ break;
+ case MOVING_OFF:
+ case ZEROING:
+ // Check if we have cliped and adjust the goal.
+ if (loop_->uncapped_voltage() > config_data_.max_zeroing_voltage) {
+ double dx = (loop_->uncapped_voltage() -
+ config_data_.max_zeroing_voltage) / loop_->K(0, 0);
+ zeroing_position_ -= dx;
+ capped_goal_ = true;
+ } else if(loop_->uncapped_voltage() < -config_data_.max_zeroing_voltage) {
+ double dx = (loop_->uncapped_voltage() +
+ config_data_.max_zeroing_voltage) / loop_->K(0, 0);
+ zeroing_position_ -= dx;
+ capped_goal_ = true;
+ }
+ break;
+ }
+ if (output_enabled) {
+ double voltage = loop_->voltage();
+ if (voltage > 0) {
+ voltage += config_data_.deadband_voltage;
+ } else if (voltage < 0) {
+ voltage -= config_data_.deadband_voltage;
+ }
+ if (voltage > 12.0) {
+ voltage = 12.0;
+ } else if (voltage < -12.0) {
+ voltage = -12.0;
+ }
+ return voltage;
+ } else {
+ return 0.0;
+ }
+}
+
+} // namespace control_loops
+} // namespace frc971
+
+#endif // FRC971_CONTROL_LOOPS_ZEROED_JOINT_H_
diff --git a/frc971/frc971.gyp b/frc971/frc971.gyp
new file mode 100644
index 0000000..0f38180
--- /dev/null
+++ b/frc971/frc971.gyp
@@ -0,0 +1,16 @@
+{
+ 'targets': [
+ {
+ 'target_name': 'All',
+ 'type': 'none',
+ 'dependencies': [
+ '<(AOS)/build/aos_all.gyp:Prime',
+
+ 'control_loops/control_loops.gyp:state_feedback_loop_test',
+ 'control_loops/control_loops.gyp:position_sensor_sim_test',
+ 'zeroing/zeroing.gyp:zeroing_test',
+ 'control_loops/voltage_cap/voltage_cap.gyp:voltage_cap_test',
+ ],
+ },
+ ],
+}
diff --git a/frc971/queues/gyro.q b/frc971/queues/gyro.q
new file mode 100644
index 0000000..7b234a1
--- /dev/null
+++ b/frc971/queues/gyro.q
@@ -0,0 +1,13 @@
+package frc971.sensors;
+
+message GyroReading {
+ // Positive is counter-clockwise (Austin says "it's Positive").
+ // Right-hand coordinate system around the Z-axis going up.
+ double angle;
+};
+queue GyroReading gyro_reading;
+
+message Uid {
+ uint32_t uid;
+};
+queue Uid gyro_part_id;
diff --git a/frc971/queues/queues.gyp b/frc971/queues/queues.gyp
new file mode 100644
index 0000000..1818f89
--- /dev/null
+++ b/frc971/queues/queues.gyp
@@ -0,0 +1,15 @@
+{
+ 'targets': [
+ {
+ 'target_name': 'gyro',
+ 'type': 'static_library',
+ 'sources': [
+ 'gyro.q',
+ ],
+ 'variables': {
+ 'header_path': 'frc971/queues',
+ },
+ 'includes': ['../../aos/build/queues.gypi'],
+ },
+ ],
+}
diff --git a/frc971/shifter_hall_effect.h b/frc971/shifter_hall_effect.h
new file mode 100644
index 0000000..aaa5ebd
--- /dev/null
+++ b/frc971/shifter_hall_effect.h
@@ -0,0 +1,21 @@
+#ifndef FRC971_SHIFTER_HALL_EFFECT_H_
+#define FRC971_SHIFTER_HALL_EFFECT_H_
+
+namespace frc971 {
+namespace constants {
+
+// Contains the voltages for an analog hall effect sensor on a shifter.
+struct ShifterHallEffect {
+ // The numbers to use for scaling raw voltages to 0-1.
+ // Low is near 0.0, high is near 1.0
+ double low_gear_middle, low_gear_low;
+ double high_gear_high, high_gear_middle;
+
+ // The numbers for when the dog is clear of each gear.
+ double clear_low, clear_high;
+};
+
+} // constants
+} // frc971
+
+#endif
diff --git a/frc971/wpilib/buffered_pcm.cc b/frc971/wpilib/buffered_pcm.cc
new file mode 100644
index 0000000..084d4b5
--- /dev/null
+++ b/frc971/wpilib/buffered_pcm.cc
@@ -0,0 +1,29 @@
+#include "frc971/wpilib/buffered_pcm.h"
+
+#include <inttypes.h>
+
+#include "aos/common/logging/logging.h"
+
+namespace frc971 {
+namespace wpilib {
+
+::std::unique_ptr<BufferedSolenoid> BufferedPcm::MakeSolenoid(int number) {
+ return ::std::unique_ptr<BufferedSolenoid>(
+ new BufferedSolenoid(number, this));
+}
+
+void BufferedPcm::Set(int number, bool value) {
+ if (value) {
+ values_ |= 1 << number;
+ } else {
+ values_ &= ~(1 << number);
+ }
+}
+
+void BufferedPcm::Flush() {
+ LOG(DEBUG, "sending solenoids 0x%" PRIx8 "\n", values_);
+ SolenoidBase::Set(values_, 0xFF);
+}
+
+} // namespace wpilib
+} // namespace frc971
diff --git a/frc971/wpilib/buffered_pcm.h b/frc971/wpilib/buffered_pcm.h
new file mode 100644
index 0000000..50f86fc
--- /dev/null
+++ b/frc971/wpilib/buffered_pcm.h
@@ -0,0 +1,44 @@
+#ifndef FRC971_WPILIB_BUFFERED_PCM_H_
+#define FRC971_WPILIB_BUFFERED_PCM_H_
+
+#include <memory>
+
+#include "SolenoidBase.h"
+
+#include "frc971/wpilib/buffered_solenoid.h"
+
+namespace frc971 {
+namespace wpilib {
+
+// Manages setting values for all solenoids for a single PCM in a single CAN
+// message.
+//
+// The way to use this is to call MakeSolenoid for whichever solenoid numbers
+// you want, call Set on those, and then periodically call Flush on this object
+// to write all of the buffered values out.
+class BufferedPcm : public SolenoidBase {
+ public:
+ explicit BufferedPcm(uint8_t module = GetDefaultSolenoidModule())
+ : SolenoidBase(module) {}
+
+ // Creates a new BufferedSolenoid for a specified port number.
+ ::std::unique_ptr<BufferedSolenoid> MakeSolenoid(int number);
+
+ // Actually sends all of the values out.
+ void Flush();
+
+ private:
+ // WPILib declares this pure virtual and then never calls it...
+ virtual void InitSolenoid() override {}
+
+ void Set(int number, bool value);
+
+ uint8_t values_ = 0;
+
+ friend class BufferedSolenoid;
+};
+
+} // namespace wpilib
+} // namespace frc971
+
+#endif // FRC971_WPILIB_BUFFERED_PCM_H_
diff --git a/frc971/wpilib/buffered_solenoid.cc b/frc971/wpilib/buffered_solenoid.cc
new file mode 100644
index 0000000..cd68da8
--- /dev/null
+++ b/frc971/wpilib/buffered_solenoid.cc
@@ -0,0 +1,13 @@
+#include "frc971/wpilib/buffered_solenoid.h"
+
+#include "frc971/wpilib/buffered_pcm.h"
+
+namespace frc971 {
+namespace wpilib {
+
+void BufferedSolenoid::Set(bool value) {
+ pcm_->Set(number_, value);
+}
+
+} // namespace wpilib
+} // namespace frc971
diff --git a/frc971/wpilib/buffered_solenoid.h b/frc971/wpilib/buffered_solenoid.h
new file mode 100644
index 0000000..f0a4c11
--- /dev/null
+++ b/frc971/wpilib/buffered_solenoid.h
@@ -0,0 +1,28 @@
+#ifndef FRC971_WPILIB_BUFFERED_SOLENOID_H_
+#define FRC971_WPILIB_BUFFERED_SOLENOID_H_
+
+namespace frc971 {
+namespace wpilib {
+
+class BufferedPcm;
+
+// Handles sending values for a single solenoid to a BufferedPcm. Instances are
+// created with BufferedPcm::MakeSolenoid.
+class BufferedSolenoid {
+ public:
+ // Sets or unsets the solenoid.
+ void Set(bool value);
+
+ private:
+ BufferedSolenoid(int number, BufferedPcm *pcm) : number_(number), pcm_(pcm) {}
+
+ const int number_;
+ BufferedPcm *const pcm_;
+
+ friend class BufferedPcm;
+};
+
+} // namespace wpilib
+} // namespace frc971
+
+#endif // FRC971_WPILIB_BUFFERED_SOLENOID_H_
diff --git a/frc971/wpilib/dma_edge_counting.cc b/frc971/wpilib/dma_edge_counting.cc
new file mode 100644
index 0000000..77d84b5
--- /dev/null
+++ b/frc971/wpilib/dma_edge_counting.cc
@@ -0,0 +1,65 @@
+#include "frc971/wpilib/dma_edge_counting.h"
+
+#include "aos/common/logging/logging.h"
+
+namespace frc971 {
+namespace wpilib {
+
+bool DMAEdgeCounter::ExtractValue(const DMASample &sample) const {
+ if (inverted_) {
+ return !sample.Get(input_);
+ } else {
+ return sample.Get(input_);
+ }
+}
+
+void DMAEdgeCounter::UpdateFromSample(const DMASample &sample) {
+ if (!have_prev_sample_) {
+ have_prev_sample_ = true;
+ } else {
+ if (!ExtractValue(prev_sample_) && ExtractValue(sample)) {
+ pos_edge_count_++;
+ pos_edge_time_ = sample.GetTimestamp();
+ pos_last_encoder_ = sample.GetRaw(encoder_);
+ } else if (ExtractValue(prev_sample_) && !ExtractValue(sample)) {
+ neg_edge_count_++;
+ neg_edge_time_ = sample.GetTimestamp();
+ neg_last_encoder_ = sample.GetRaw(encoder_);
+ }
+ }
+
+ prev_sample_ = sample;
+}
+
+void DMASynchronizer::CheckDMA() {
+ DMASample current_sample;
+
+ size_t remaining = 0;
+ while (true) {
+ switch (dma_->Read(¤t_sample, 0, &remaining)) {
+ case DMA::STATUS_OK:
+ for (auto &c : handlers_) {
+ c->UpdateFromSample(current_sample);
+ }
+
+ if (remaining == 0) {
+ if (sample_time_ < current_sample.GetTimestamp()) {
+ // If the latest DMA sample happened after we started polling, then
+ // just use the values from it because they're more recent.
+ for (auto &c : handlers_) {
+ c->PollFromSample(current_sample);
+ }
+ }
+ return;
+ }
+ case DMA::STATUS_TIMEOUT:
+ return;
+ case DMA::STATUS_ERROR:
+ LOG(WARNING, "DMA read failed\n");
+ break;
+ }
+ }
+}
+
+} // namespace wpilib
+} // namespace frc971
diff --git a/frc971/wpilib/dma_edge_counting.h b/frc971/wpilib/dma_edge_counting.h
new file mode 100644
index 0000000..939dc5e
--- /dev/null
+++ b/frc971/wpilib/dma_edge_counting.h
@@ -0,0 +1,185 @@
+#ifndef FRC971_WPILIB_DMA_EDGE_COUNTING_H_
+#define FRC971_WPILIB_DMA_EDGE_COUNTING_H_
+
+#include <memory>
+#include <vector>
+
+#include "aos/common/macros.h"
+
+#include "frc971/wpilib/hall_effect.h"
+
+#include "DigitalSource.h"
+#include "Encoder.h"
+#include "AnalogInput.h"
+#include "Utility.h"
+#include "dma.h"
+
+namespace frc971 {
+namespace wpilib {
+
+// Generic interface for classes that do something with DMA samples and also
+// poll current sensor values.
+class DMASampleHandlerInterface {
+ public:
+ virtual ~DMASampleHandlerInterface() {}
+
+ // Updates values based on a new DMA sample.
+ virtual void UpdateFromSample(const DMASample &sample) = 0;
+
+ // Polls the current values and saves them for later reference.
+ virtual void UpdatePolledValue() = 0;
+
+ // Fills in the "polled" values from sample.
+ // This is only called when a DMA event happens right as we're polling values.
+ virtual void PollFromSample(const DMASample &sample) = 0;
+
+ // Adds readings and triggers appropriate to this object to dma.
+ virtual void AddToDMA(DMA *dma) = 0;
+};
+
+// Counts edges on a sensor using DMA data and latches encoder values
+// corresponding to those edges.
+class DMAEdgeCounter : public DMASampleHandlerInterface {
+ public:
+ DMAEdgeCounter(Encoder *encoder, DigitalSource *input)
+ : encoder_(encoder), input_(input), inverted_(false) {}
+ DMAEdgeCounter(Encoder *encoder, HallEffect *input)
+ : encoder_(encoder), input_(input), inverted_(true) {}
+
+ int positive_count() const { return pos_edge_count_; }
+ int negative_count() const { return neg_edge_count_; }
+ int last_positive_encoder_value() const { return pos_last_encoder_; }
+ int last_negative_encoder_value() const { return neg_last_encoder_; }
+
+ // Returns the value of the sensor in the last-read DMA sample.
+ bool last_value() const { return ExtractValue(prev_sample_); }
+ // Returns the most recent polled value of the sensor.
+ bool polled_value() const { return polled_value_; }
+ // Returns the most recent polled reading from the encoder.
+ int polled_encoder() const { return polled_encoder_; }
+
+ private:
+ void UpdateFromSample(const DMASample &sample) override;
+ void UpdatePolledValue() override {
+ polled_value_ = input_->Get();
+ polled_encoder_ = encoder_->GetRaw();
+ }
+ void PollFromSample(const DMASample &sample) override {
+ polled_value_ = ExtractValue(sample);
+ polled_encoder_ = sample.GetRaw(encoder_);
+ }
+ void AddToDMA(DMA *dma) override {
+ dma->Add(encoder_);
+ dma->Add(input_);
+ }
+
+ bool ExtractValue(const DMASample &sample) const;
+
+ Encoder *const encoder_;
+ DigitalSource *const input_;
+ const bool inverted_;
+
+ // The last DMA reading we got.
+ DMASample prev_sample_;
+ // Whether or not we actually have anything in prev_sample_.
+ bool have_prev_sample_ = false;
+
+ // Values related to the positive edge.
+ int pos_edge_count_ = 0;
+ double pos_edge_time_ = 0.0;
+ int pos_last_encoder_ = 0;
+
+ // Values related to the negative edge.
+ int neg_edge_count_ = 0;
+ double neg_edge_time_ = 0.0;
+ int neg_last_encoder_ = 0;
+
+ bool polled_value_ = false;
+ int polled_encoder_ = 0;
+
+ DISALLOW_COPY_AND_ASSIGN(DMAEdgeCounter);
+};
+
+// Reads a hall effect in sync with DMA samples.
+class DMADigitalReader : public DMASampleHandlerInterface {
+ public:
+ DMADigitalReader(DigitalSource *input) : input_(input), inverted_(false) {}
+ DMADigitalReader(HallEffect *input) : input_(input), inverted_(true) {}
+
+ bool value() const { return value_; }
+
+ private:
+ void UpdateFromSample(const DMASample & /*sample*/) override {}
+ void UpdatePolledValue() override { value_ = input_->Get(); }
+ void PollFromSample(const DMASample &sample) override {
+ if (inverted_) {
+ value_ = !sample.Get(input_);
+ } else {
+ value_ = sample.Get(input_);
+ }
+ }
+ void AddToDMA(DMA *dma) override {
+ dma->Add(input_);
+ }
+
+ DigitalSource *const input_;
+ const bool inverted_;
+
+ bool value_;
+
+ DISALLOW_COPY_AND_ASSIGN(DMADigitalReader);
+};
+
+// This class handles updating the sampled data on multiple
+// DMASampleHandlerInterfaces. The caller should create an instance and then
+// periodically call RunIteration, retrieving whatever data from the
+// DMASampleHandlerInterfaces after each iteration.
+class DMASynchronizer {
+ public:
+ DMASynchronizer(::std::unique_ptr<DMA> dma) : dma_(::std::move(dma)) {}
+
+ // Adds a new handler to this object. This method must not be called after
+ // Start().
+ void Add(DMASampleHandlerInterface *handler) {
+ handler->AddToDMA(dma_.get());
+ handlers_.emplace_back(handler);
+ }
+
+ // Actually starts watching for DMA samples.
+ // Add may not be called any more after this.
+ void Start() {
+ dma_->Start(1024);
+ }
+
+ // Updates all sensor values.
+ void RunIteration() {
+ SampleSensors();
+ CheckDMA();
+ }
+
+ private:
+ // Reads the state of all the sensors and records it as the polled values of
+ // all the inputs.
+ void SampleSensors() {
+ sample_time_ = GetFPGATime();
+ for (auto &c : handlers_) {
+ c->UpdatePolledValue();
+ }
+ }
+
+ // Gets called by the DMA handler to update edge counts.
+ void CheckDMA();
+
+ const ::std::unique_ptr<DMA> dma_;
+ ::std::vector<DMASampleHandlerInterface *> handlers_;
+
+ // The time at which we most recently read the sensor values.
+ double sample_time_ = 0.0;
+
+ DISALLOW_COPY_AND_ASSIGN(DMASynchronizer);
+};
+
+} // namespace wpilib
+} // namespace frc971
+
+#endif // FRC971_WPILIB_DMA_EDGE_COUNTING_H_
diff --git a/frc971/wpilib/encoder_and_potentiometer.cc b/frc971/wpilib/encoder_and_potentiometer.cc
new file mode 100644
index 0000000..800c49b
--- /dev/null
+++ b/frc971/wpilib/encoder_and_potentiometer.cc
@@ -0,0 +1,57 @@
+#include "frc971/wpilib/encoder_and_potentiometer.h"
+
+#include "aos/linux_code/init.h"
+#include "aos/common/logging/logging.h"
+
+namespace frc971 {
+namespace wpilib {
+
+void DMAEncoderAndPotentiometer::UpdateFromSample(const DMASample &sample) {
+ if (index_last_value_) {
+ // It was already true last time, so check if it's reset back to false yet.
+ index_last_value_ = sample.Get(index_.get());
+ } else if (sample.Get(index_.get())) {
+ // This sample is posedge, so record all the values.
+ index_last_value_ = true;
+ ++index_posedge_count_;
+ last_encoder_value_ = sample.GetRaw(encoder_.get());
+ last_potentiometer_voltage_ = sample.GetVoltage(potentiometer_.get());
+ }
+}
+
+void InterruptEncoderAndPotentiometer::Start() {
+ CHECK_NE(nullptr, encoder_);
+ CHECK_NE(nullptr, index_);
+ CHECK_NE(nullptr, potentiometer_);
+ CHECK_NE(0, priority_);
+ thread_ = ::std::thread(::std::ref(*this));
+}
+
+void InterruptEncoderAndPotentiometer::operator()() {
+ ::aos::SetCurrentThreadName("IntEncPot_" +
+ ::std::to_string(potentiometer_->GetChannel()));
+
+ index_->RequestInterrupts();
+ index_->SetUpSourceEdge(true, false);
+
+ ::aos::SetCurrentThreadRealtimePriority(priority_);
+
+ InterruptableSensorBase::WaitResult result = InterruptableSensorBase::kBoth;
+ while (run_) {
+ result = index_->WaitForInterrupt(
+ 0.1, result != InterruptableSensorBase::kTimeout);
+ if (result == InterruptableSensorBase::kTimeout) {
+ continue;
+ }
+
+ {
+ ::aos::MutexLocker locker(&mutex_);
+ last_potentiometer_voltage_ = potentiometer_->GetVoltage();
+ last_encoder_value_ = encoder_->GetRaw();
+ ++index_posedge_count_;
+ }
+ }
+}
+
+} // namespace wpilib
+} // namespace frc971
diff --git a/frc971/wpilib/encoder_and_potentiometer.h b/frc971/wpilib/encoder_and_potentiometer.h
new file mode 100644
index 0000000..2450841
--- /dev/null
+++ b/frc971/wpilib/encoder_and_potentiometer.h
@@ -0,0 +1,169 @@
+#ifndef FRC971_ENCODER_AND_POTENTIOMETER_H_
+#define FRC971_ENCODER_AND_POTENTIOMETER_H_
+
+#include <atomic>
+#include <thread>
+
+#include "aos/common/macros.h"
+#include "aos/common/mutex.h"
+
+#include "Encoder.h"
+#include "DigitalSource.h"
+#include "AnalogInput.h"
+#include "dma.h"
+
+#include "frc971/wpilib/dma_edge_counting.h"
+
+namespace frc971 {
+namespace wpilib {
+
+// Latches values from an encoder and potentiometer on positive edges from
+// another input using an interrupt.
+class InterruptEncoderAndPotentiometer {
+ public:
+ // priority is the priority the thread will run at.
+ InterruptEncoderAndPotentiometer(int priority) : priority_(priority) {}
+
+ // Starts the thread running so it can receive interrupts.
+ void Start();
+
+ // Tells the thread to stop running and then waits for it to finish.
+ void Stop() {
+ run_ = false;
+ thread_.join();
+ }
+
+ // Loops until Stop() is called, reading interrupts.
+ // Designed to be called by ::std::thread internally.
+ void operator()();
+
+ // Returns the mutex which must be held while calling index_posedge_count(),
+ // last_encoder_value(), and last_potentiometer_voltage().
+ // Holding this mutex will increase the handling latency.
+ ::aos::Mutex *mutex() { return &mutex_; }
+
+ void set_encoder(::std::unique_ptr<Encoder> encoder) {
+ encoder_ = ::std::move(encoder);
+ }
+ Encoder *encoder() const { return encoder_.get(); }
+
+ void set_index(::std::unique_ptr<DigitalSource> index) {
+ index_ = ::std::move(index);
+ }
+ DigitalSource *index() const { return index_.get(); }
+
+ void set_potentiometer(::std::unique_ptr<AnalogInput> potentiometer) {
+ potentiometer_ = ::std::move(potentiometer);
+ }
+ AnalogInput *potentiometer() const { return potentiometer_.get(); }
+
+ // Returns the number of poseges that have happened on the index input.
+ // mutex() must be held while calling this.
+ uint32_t index_posedge_count() const { return index_posedge_count_; }
+ // Returns the value of the encoder at the last index posedge.
+ // mutex() must be held while calling this.
+ int32_t last_encoder_value() const { return last_encoder_value_; }
+ // Returns the voltage of the potentiometer at the last index posedge.
+ // mutex() must be held while calling this.
+ float last_potentiometer_voltage() const {
+ return last_potentiometer_voltage_;
+ }
+
+ private:
+ ::std::unique_ptr<Encoder> encoder_;
+ ::std::unique_ptr<DigitalSource> index_;
+ ::std::unique_ptr<AnalogInput> potentiometer_;
+
+ int32_t last_encoder_value_{0};
+ float last_potentiometer_voltage_{0.0f};
+ uint32_t index_posedge_count_{0};
+
+ ::aos::Mutex mutex_;
+
+ const int priority_;
+
+ ::std::atomic<bool> run_{true};
+ ::std::thread thread_;
+
+ DISALLOW_COPY_AND_ASSIGN(InterruptEncoderAndPotentiometer);
+};
+
+// Latches values from an encoder and potentiometer on positive edges from
+// another input using DMA.
+class DMAEncoderAndPotentiometer : public DMASampleHandlerInterface {
+ public:
+ DMAEncoderAndPotentiometer() {}
+
+ void set_encoder(::std::unique_ptr<Encoder> encoder) {
+ encoder_ = ::std::move(encoder);
+ }
+ Encoder *encoder() const { return encoder_.get(); }
+
+ void set_index(::std::unique_ptr<DigitalSource> index) {
+ index_ = ::std::move(index);
+ }
+ DigitalSource *index() const { return index_.get(); }
+
+ void set_potentiometer(::std::unique_ptr<AnalogInput> potentiometer) {
+ potentiometer_ = ::std::move(potentiometer);
+ }
+ AnalogInput *potentiometer() const { return potentiometer_.get(); }
+
+ // Returns the most recent polled value of the encoder.
+ uint32_t polled_encoder_value() const { return polled_encoder_value_; }
+ // Returns the most recent polled voltage of the potentiometer.
+ float polled_potentiometer_voltage() const {
+ return polled_potentiometer_voltage_;
+ }
+
+ // Returns the number of poseges that have happened on the index input.
+ uint32_t index_posedge_count() const { return index_posedge_count_; }
+ // Returns the value of the encoder at the last index posedge.
+ int32_t last_encoder_value() const { return last_encoder_value_; }
+ // Returns the voltage of the potentiometer at the last index posedge.
+ float last_potentiometer_voltage() const {
+ return last_potentiometer_voltage_;
+ }
+
+ virtual void UpdateFromSample(const DMASample &sample) override;
+
+ virtual void PollFromSample(const DMASample &sample) override {
+ polled_encoder_value_ = sample.GetRaw(encoder_.get());
+ polled_potentiometer_voltage_ = sample.GetVoltage(potentiometer_.get());
+ }
+
+ virtual void UpdatePolledValue() override {
+ polled_encoder_value_ = encoder_->GetRaw();
+ polled_potentiometer_voltage_ = potentiometer_->GetVoltage();
+ }
+
+ virtual void AddToDMA(DMA *dma) override {
+ dma->Add(encoder_.get());
+ dma->Add(index_.get());
+ dma->Add(potentiometer_.get());
+ dma->SetExternalTrigger(index_.get(), true, true);
+ }
+
+ private:
+ ::std::unique_ptr<Encoder> encoder_;
+ ::std::unique_ptr<DigitalSource> index_;
+ ::std::unique_ptr<AnalogInput> potentiometer_;
+
+ int32_t polled_encoder_value_ = 0;
+ float polled_potentiometer_voltage_ = 0.0f;
+
+ int32_t last_encoder_value_ = 0;
+ float last_potentiometer_voltage_ = 0.0f;
+
+ uint32_t index_posedge_count_ = 0;
+
+ // Whether or not it was triggered in the last sample.
+ bool index_last_value_ = false;
+
+ DISALLOW_COPY_AND_ASSIGN(DMAEncoderAndPotentiometer);
+};
+
+} // namespace wpilib
+} // namespace frc971
+
+#endif // FRC971_ENCODER_AND_POTENTIOMETER_H_
diff --git a/frc971/wpilib/gyro_interface.cc b/frc971/wpilib/gyro_interface.cc
new file mode 100644
index 0000000..4b440a9
--- /dev/null
+++ b/frc971/wpilib/gyro_interface.cc
@@ -0,0 +1,144 @@
+#include "frc971/wpilib/gyro_interface.h"
+
+#include <inttypes.h>
+
+#include "aos/common/logging/logging.h"
+#include "aos/common/time.h"
+
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif
+
+namespace frc971 {
+namespace wpilib {
+
+GyroInterface::GyroInterface() : gyro_(new SPI(SPI::kOnboardCS0)) {
+ // The gyro goes up to 8.08MHz.
+ // The myRIO goes up to 4MHz, so the roboRIO probably does too.
+ gyro_->SetClockRate(4e6);
+ gyro_->SetChipSelectActiveLow();
+ gyro_->SetClockActiveHigh();
+ gyro_->SetSampleDataOnRising();
+ gyro_->SetMSBFirst();
+}
+
+bool GyroInterface::InitializeGyro() {
+ uint32_t result;
+ if (!DoTransaction(0x20000003, &result)) {
+ LOG(WARNING, "failed to start a self-check\n");
+ return false;
+ }
+ if (result != 1) {
+ // We might have hit a parity error or something and are now retrying, so
+ // this isn't a very big deal.
+ LOG(INFO, "gyro unexpected initial response 0x%" PRIx32 "\n", result);
+ }
+
+ // Wait for it to assert the fault conditions before reading them.
+ ::aos::time::SleepFor(::aos::time::Time::InMS(50));
+
+ if (!DoTransaction(0x20000000, &result)) {
+ LOG(WARNING, "failed to clear latched non-fault data\n");
+ return false;
+ }
+ LOG(DEBUG, "gyro dummy response is 0x%" PRIx32 "\n", result);
+
+ if (!DoTransaction(0x20000000, &result)) {
+ LOG(WARNING, "failed to read self-test data\n");
+ return false;
+ }
+ if (ExtractStatus(result) != 2) {
+ LOG(WARNING, "gyro first value 0x%" PRIx32 " not self-test data\n", result);
+ return false;
+ }
+ if (ExtractErrors(result) != 0x7F) {
+ LOG(WARNING, "gyro first value 0x%" PRIx32 " does not have all errors\n",
+ result);
+ return false;
+ }
+
+ if (!DoTransaction(0x20000000, &result)) {
+ LOG(WARNING, "failed to clear latched self-test data\n");
+ return false;
+ }
+ if (ExtractStatus(result) != 2) {
+ LOG(WARNING, "gyro second value 0x%" PRIx32 " not self-test data\n",
+ result);
+ return false;
+ }
+
+ return true;
+}
+
+bool GyroInterface::DoTransaction(uint32_t to_write, uint32_t *result) {
+ static const uint8_t kBytes = 4;
+ static_assert(kBytes == sizeof(to_write),
+ "need the same number of bytes as sizeof(the data)");
+
+ if (__builtin_parity(to_write & ~1) == 0) to_write |= 1;
+
+ uint8_t to_send[kBytes], to_receive[kBytes];
+ const uint32_t to_write_flipped = __builtin_bswap32(to_write);
+ memcpy(to_send, &to_write_flipped, kBytes);
+
+ switch (gyro_->Transaction(to_send, to_receive, kBytes)) {
+ case -1:
+ LOG(INFO, "SPI::Transaction failed\n");
+ return false;
+ case kBytes:
+ break;
+ default:
+ LOG(FATAL, "SPI::Transaction returned something weird\n");
+ }
+
+ memcpy(result, to_receive, kBytes);
+ if (__builtin_parity(*result & 0xFFFF) != 1) {
+ LOG(INFO, "high byte parity failure\n");
+ return false;
+ }
+ if (__builtin_parity(*result) != 1) {
+ LOG(INFO, "whole value parity failure\n");
+ return false;
+ }
+
+ *result = __builtin_bswap32(*result);
+ return true;
+}
+
+uint16_t GyroInterface::DoRead(uint8_t address) {
+ const uint32_t command = (0x8 << 28) | (address << 17);
+ uint32_t response;
+ while (true) {
+ if (!DoTransaction(command, &response)) {
+ LOG(WARNING, "reading 0x%" PRIx8 " failed\n", address);
+ continue;
+ }
+ if ((response & 0xEFE00000) != 0x4E000000) {
+ LOG(WARNING, "gyro read from 0x%" PRIx8
+ " gave unexpected response 0x%" PRIx32 "\n",
+ address, response);
+ continue;
+ }
+ return (response >> 5) & 0xFFFF;
+ }
+}
+
+double GyroInterface::ExtractAngle(uint32_t value) {
+ const int16_t reading = -(int16_t)(value >> 10 & 0xFFFF);
+ return static_cast<double>(reading) * 2.0 * M_PI / 360.0 / 80.0;
+}
+
+uint32_t GyroInterface::ReadPartID() {
+ return (DoRead(0x0E) << 16) | DoRead(0x10);
+}
+
+uint32_t GyroInterface::GetReading() {
+ uint32_t result;
+ if (!DoTransaction(0x20000000, &result)) {
+ return 0;
+ }
+ return result;
+}
+
+} // namespace wpilib
+} // namespace frc971
diff --git a/frc971/wpilib/gyro_interface.h b/frc971/wpilib/gyro_interface.h
new file mode 100644
index 0000000..baba248
--- /dev/null
+++ b/frc971/wpilib/gyro_interface.h
@@ -0,0 +1,55 @@
+#ifndef FRC971_WPILIB_GYRO_INTERFACE_H_
+#define FRC971_WPILIB_GYRO_INTERFACE_H_
+
+#include <memory>
+
+#include "SPI.h"
+
+namespace frc971 {
+namespace wpilib {
+
+class GyroInterface {
+ public:
+ GyroInterface();
+
+ // Runs the recommended gyro startup procedure including checking all of the
+ // self-test bits.
+ // Returns true if it succeeds.
+ bool InitializeGyro();
+
+ // Reads one of the gyro's "registers" and returns the value.
+ // Retries until it succeeds.
+ uint16_t DoRead(uint8_t address);
+
+ // Returns all of the non-data bits in the "header" except the parity from
+ // value.
+ uint8_t ExtractStatus(uint32_t value) { return (value >> 26) & ~4; }
+ // Returns all of the error bits in the "footer" from value.
+ uint8_t ExtractErrors(uint32_t value) { return (value >> 1) & 0x7F; }
+
+ // Returns the anglular rate contained in value.
+ double ExtractAngle(uint32_t value);
+
+ // Performs a transaction with the gyro.
+ // to_write is the value to write. This function handles setting the checksum
+ // bit.
+ // result is where to stick the result. This function verifies the parity bit.
+ // Returns true for success.
+ bool DoTransaction(uint32_t to_write, uint32_t *result);
+
+ // Returns the part ID from the gyro.
+ // Retries until it succeeds.
+ uint32_t ReadPartID();
+
+ // Gets a reading from the gyro.
+ // Returns a value to be passed to the Extract* methods or 0 for error.
+ uint32_t GetReading();
+
+ private:
+ ::std::unique_ptr<SPI> gyro_;
+};
+
+} // namespace wpilib
+} // namespace frc971
+
+#endif // FRC971_WPILIB_GYRO_INTERFACE_H_
diff --git a/frc971/wpilib/gyro_sender.cc b/frc971/wpilib/gyro_sender.cc
new file mode 100644
index 0000000..d3b9500
--- /dev/null
+++ b/frc971/wpilib/gyro_sender.cc
@@ -0,0 +1,138 @@
+#include "frc971/wpilib/gyro_sender.h"
+
+#include <inttypes.h>
+
+#include "aos/common/logging/logging.h"
+#include "aos/common/logging/queue_logging.h"
+#include "aos/common/util/phased_loop.h"
+#include "aos/common/messages/robot_state.q.h"
+#include "aos/common/time.h"
+#include "aos/linux_code/init.h"
+
+#include "frc971/queues/gyro.q.h"
+
+namespace frc971 {
+namespace wpilib {
+
+GyroSender::GyroSender() {}
+
+void GyroSender::operator()() {
+ ::aos::SetCurrentThreadName("Gyro");
+
+ // Try to initialize repeatedly as long as we're supposed to be running.
+ while (run_ && !gyro_.InitializeGyro()) {
+ ::aos::time::SleepFor(::aos::time::Time::InMS(50));
+ }
+ LOG(INFO, "gyro initialized successfully\n");
+
+ auto message = ::frc971::sensors::gyro_part_id.MakeMessage();
+ message->uid = gyro_.ReadPartID();
+ LOG_STRUCT(INFO, "gyro ID", *message);
+ message.Send();
+
+ // In radians, ready to send out.
+ double angle = 0;
+
+ int startup_cycles_left = 2 * kReadingRate;
+
+ double zeroing_data[6 * kReadingRate];
+ size_t zeroing_index = 0;
+ bool zeroed = false;
+ bool have_zeroing_data = false;
+ double zero_offset = 0;
+
+ while (run_) {
+ ::aos::time::PhasedLoopXMS(1000 / kReadingRate, 0);
+
+ const uint32_t result = gyro_.GetReading();
+ if (result == 0) {
+ LOG(WARNING, "normal gyro read failed\n");
+ continue;
+ }
+ switch (gyro_.ExtractStatus(result)) {
+ case 0:
+ LOG(WARNING, "gyro says data is bad\n");
+ continue;
+ case 1:
+ break;
+ default:
+ LOG(WARNING, "gyro gave weird status 0x%" PRIx8 "\n",
+ gyro_.ExtractStatus(result));
+ continue;
+ }
+ if (gyro_.ExtractErrors(result) != 0) {
+ const uint8_t errors = gyro_.ExtractErrors(result);
+ if (errors & (1 << 6)) {
+ LOG(WARNING, "gyro gave PLL error\n");
+ }
+ if (errors & (1 << 5)) {
+ LOG(WARNING, "gyro gave quadrature error\n");
+ }
+ if (errors & (1 << 4)) {
+ LOG(WARNING, "gyro gave non-volatile memory error\n");
+ }
+ if (errors & (1 << 3)) {
+ LOG(WARNING, "gyro gave volatile memory error\n");
+ }
+ if (errors & (1 << 2)) {
+ LOG(WARNING, "gyro gave power error\n");
+ }
+ if (errors & (1 << 1)) {
+ LOG(WARNING, "gyro gave continuous self-test error\n");
+ }
+ if (errors & 1) {
+ LOG(WARNING, "gyro gave unexpected self-test mode\n");
+ }
+ continue;
+ }
+
+ if (startup_cycles_left > 0) {
+ --startup_cycles_left;
+ continue;
+ }
+
+ const double new_angle =
+ gyro_.ExtractAngle(result) / static_cast<double>(kReadingRate);
+ auto message = ::frc971::sensors::gyro_reading.MakeMessage();
+ if (zeroed) {
+ angle += new_angle;
+ angle += zero_offset;
+ message->angle = angle;
+ LOG_STRUCT(DEBUG, "sending", *message);
+ message.Send();
+ } else {
+ // TODO(brian): Don't break without 6 seconds of standing still before
+ // enabling. Ideas:
+ // Don't allow driving until we have at least some data?
+ // Some kind of indicator light?
+ {
+ message->angle = new_angle;
+ LOG_STRUCT(DEBUG, "collected while zeroing", *message);
+ message->angle = 0.0;
+ message.Send();
+ }
+ zeroing_data[zeroing_index] = new_angle;
+ ++zeroing_index;
+ if (zeroing_index >= sizeof(zeroing_data) / sizeof(zeroing_data[0])) {
+ zeroing_index = 0;
+ have_zeroing_data = true;
+ }
+
+ ::aos::joystick_state.FetchLatest();
+ if (::aos::joystick_state.get() && ::aos::joystick_state->enabled &&
+ have_zeroing_data) {
+ zero_offset = 0;
+ for (size_t i = 0; i < sizeof(zeroing_data) / sizeof(zeroing_data[0]);
+ ++i) {
+ zero_offset -= zeroing_data[i];
+ }
+ zero_offset /= sizeof(zeroing_data) / sizeof(zeroing_data[0]);
+ LOG(INFO, "total zero offset %f\n", zero_offset);
+ zeroed = true;
+ }
+ }
+ }
+}
+
+} // namespace wpilib
+} // namespace frc971
diff --git a/frc971/wpilib/gyro_sender.h b/frc971/wpilib/gyro_sender.h
new file mode 100644
index 0000000..029ace9
--- /dev/null
+++ b/frc971/wpilib/gyro_sender.h
@@ -0,0 +1,41 @@
+#ifndef FRC971_WPILIB_GYRO_H_
+#define FRC971_WPILIB_GYRO_H_
+
+#include <stdint.h>
+
+#include <atomic>
+
+#include "frc971/wpilib/gyro_interface.h"
+
+namespace frc971 {
+namespace wpilib {
+
+// Handles reading the gyro over SPI and sending out angles on a queue.
+//
+// This is designed to be passed into ::std::thread's constructor so it will run
+// as a separate thread.
+class GyroSender {
+ public:
+ GyroSender();
+
+ // For ::std::thread to call.
+ //
+ // Initializes the gyro and then loops forever taking readings.
+ void operator()();
+
+ void Quit() { run_ = false; }
+
+ private:
+
+ // Readings per second.
+ static const int kReadingRate = 200;
+
+ GyroInterface gyro_;
+
+ ::std::atomic<bool> run_{true};
+};
+
+} // namespace wpilib
+} // namespace frc971
+
+#endif // FRC971_WPILIB_GYRO_H_
diff --git a/frc971/wpilib/hall_effect.h b/frc971/wpilib/hall_effect.h
new file mode 100644
index 0000000..407f2b9
--- /dev/null
+++ b/frc971/wpilib/hall_effect.h
@@ -0,0 +1,19 @@
+#ifndef FRC971_WPILIB_HALL_EFFECT_H_
+#define FRC971_WPILIB_HALL_EFFECT_H_
+
+#include "DigitalInput.h"
+
+namespace frc971 {
+namespace wpilib {
+
+// Inverts the output from a digital input.
+class HallEffect : public DigitalInput {
+ public:
+ HallEffect(int index) : DigitalInput(index) {}
+ virtual bool Get() override { return !DigitalInput::Get(); }
+};
+
+} // namespace wpilib
+} // namespace frc971
+
+#endif // FRC971_WPILIB_HALL_EFFECT_H_
diff --git a/frc971/wpilib/interrupt_edge_counting.cc b/frc971/wpilib/interrupt_edge_counting.cc
new file mode 100644
index 0000000..4bdb2d2
--- /dev/null
+++ b/frc971/wpilib/interrupt_edge_counting.cc
@@ -0,0 +1,104 @@
+#include "frc971/wpilib/interrupt_edge_counting.h"
+
+#include "aos/common/time.h"
+#include "aos/linux_code/init.h"
+
+namespace frc971 {
+namespace wpilib {
+
+void EdgeCounter::GatherPolledValue() {
+ shadow_values_.polled_value = input_->Get();
+ bool miss_match = (shadow_values_.polled_value != current_value_);
+ if (miss_match && last_miss_match_) {
+ current_value_ = shadow_values_.polled_value;
+ last_miss_match_ = false;
+ } else {
+ last_miss_match_ = miss_match;
+ }
+}
+
+void EdgeCounter::operator()() {
+ ::aos::SetCurrentThreadName("EdgeCounter_" +
+ ::std::to_string(input_->GetChannelForRouting()));
+
+ input_->RequestInterrupts();
+ input_->SetUpSourceEdge(true, true);
+
+ {
+ ::std::unique_lock<::aos::stl_mutex> mutex_guard(*mutex());
+ current_value_ = input_->Get();
+ }
+
+ ::aos::SetCurrentThreadRealtimePriority(priority());
+ InterruptableSensorBase::WaitResult result = InterruptableSensorBase::kBoth;
+ while (should_run()) {
+ result = input_->WaitForInterrupt(
+ 0.1, result != InterruptableSensorBase::kTimeout);
+ if (result == InterruptableSensorBase::kTimeout) {
+ continue;
+ }
+ interrupt_received();
+
+ ::std::unique_lock<::aos::stl_mutex> mutex_guard(*mutex());
+ int32_t encoder_value = encoder_->GetRaw();
+ bool hall_value = input_->Get();
+ if (current_value_ != hall_value) {
+ if (hall_value) {
+ ++shadow_values_.positive_interrupt_count;
+ shadow_values_.last_positive_encoder_value = encoder_value;
+ } else {
+ ++shadow_values_.negative_interrupt_count;
+ shadow_values_.last_negative_encoder_value = encoder_value;
+ }
+ current_value_ = hall_value;
+ } else {
+ LOG(WARNING, "Detected spurious edge on %d. Dropping it.\n",
+ input_->GetChannelForRouting());
+ }
+ }
+}
+
+void InterruptSynchronizer::RunIteration() {
+ while (true) {
+ if (!TryStartIteration()) continue;
+
+ // Wait more than the amount of time it takes for a digital input change
+ // to go from visible to software to having triggered an interrupt.
+ ::aos::time::SleepFor(::aos::time::Time::InUS(120));
+
+ if (TryFinishingIteration()) return;
+ }
+}
+
+bool InterruptSynchronizer::TryStartIteration() {
+ for (auto &c : handlers_) {
+ c->save_interrupt_count();
+ }
+
+ {
+ ::std::unique_lock<::aos::stl_mutex> mutex_guard(mutex_);
+ for (auto &c : handlers_) {
+ c->GatherPolledValue();
+ }
+ }
+ return true;
+}
+
+bool InterruptSynchronizer::TryFinishingIteration() {
+ // Make sure no interrupts have occurred while we were waiting. If they
+ // have, we are in an inconsistent state and need to try again.
+ ::std::unique_lock<::aos::stl_mutex> mutex_guard(mutex_);
+ for (auto &c : handlers_) {
+ if (c->interrupt_count_changed()) {
+ LOG(WARNING, "got an interrupt while sampling. retrying\n");
+ return false;
+ }
+ }
+ for (auto &c : handlers_) {
+ c->CommitValue();
+ }
+ return true;
+}
+
+} // namespace wpilib
+} // namespace frc971
diff --git a/frc971/wpilib/interrupt_edge_counting.h b/frc971/wpilib/interrupt_edge_counting.h
new file mode 100644
index 0000000..f0b5e32
--- /dev/null
+++ b/frc971/wpilib/interrupt_edge_counting.h
@@ -0,0 +1,234 @@
+#ifndef FRC971_WPILIB_INTERRUPT_EDGE_COUNTING_H_
+#define FRC971_WPILIB_INTERRUPT_EDGE_COUNTING_H_
+
+#include <memory>
+#include <atomic>
+#include <thread>
+#include <vector>
+
+#include "aos/common/stl_mutex.h"
+#include "aos/common/macros.h"
+
+#include "DigitalSource.h"
+#include "Encoder.h"
+#include "AnalogInput.h"
+#include "Utility.h"
+
+namespace frc971 {
+namespace wpilib {
+
+class InterruptSynchronizer;
+
+// Handles interrupts arriving from a single source.
+//
+// Instances of subclasses should be passed to InterruptSynchronizer::Add to use
+// them. All methods which are called with the lock held should avoid taking too
+// long because they directly contribute to interrupt-handling latency for all
+// InterruptHandlers in the same InterruptSynchronizer.
+//
+// Each instance handles any important sensor reading which must happen right
+// after an interrupt triggers from a single source. It is also important that
+// some sensors are sampled after any pending interrupts have been processed.
+// This is handled using a per-InterruptSynchronizer mutex. Each
+// InterruptHandler records that it has received an interrupt, locks the mutex,
+// and then updates a "shadow" state. The InterruptSynchronizer then triggers
+// making this "shadow" state visible after making sure no more interrupts have
+// arrived while holding the mutex.
+class InterruptHandler {
+ public:
+ virtual ~InterruptHandler() {}
+
+ // Stops the thread which actually does the sampling and waits for it to
+ // finish.
+ void Quit() {
+ run_ = false;
+ thread_.join();
+ }
+
+ // Starts the thread running.
+ // set_priority and set_mutex must be called first.
+ void Start() {
+ CHECK_NE(nullptr, mutex_);
+ CHECK_NE(0, priority_);
+ thread_ = ::std::thread(::std::ref(*this));
+ }
+
+ // Polls the current values and saves them to the "shadow" output.
+ // Called while the lock is held.
+ virtual void GatherPolledValue() = 0;
+
+ // Actually outputs the "shadow" state collected during the most recent
+ // GatherPolledValue.
+ // Called while the lock is held.
+ virtual void CommitValue() = 0;
+
+ // Saves the current interrupt count to be compared when
+ // interrupt_count_changed() is called.
+ void save_interrupt_count() { saved_interrupt_count_ = interrupt_count_; }
+ // Returns whether or not the interrupt count has changed since
+ // save_interrupt_count() was last called.
+ bool interrupt_count_changed() const {
+ return saved_interrupt_count_ != interrupt_count_;
+ }
+
+ // Sets the priority the thread will run at.
+ // This must be called before Start.
+ void set_priority(int priority) { priority_ = priority; }
+
+ // Sets the mutex to use for synchronizing readings.
+ // This must be called before Start.
+ void set_mutex(::aos::stl_mutex *mutex) { mutex_ = mutex; }
+
+ // Waits for interrupts, locks the mutex, and updates the internal state.
+ // Should only be called by the (internal) ::std::thread.
+ virtual void operator()() = 0;
+
+ protected:
+ // Indicates that another interrupt has been received (not handled yet).
+ void interrupt_received() { ++interrupt_count_; }
+
+ int priority() const { return priority_; }
+
+ ::aos::stl_mutex *mutex() { return mutex_; }
+
+ // Returns true if the thread should continue running.
+ bool should_run() const { return run_; }
+
+ private:
+ ::std::atomic<int> interrupt_count_{0};
+ int saved_interrupt_count_;
+
+ ::std::atomic<bool> run_{true};
+ ::std::thread thread_;
+
+ int priority_ = 0;
+ ::aos::stl_mutex *mutex_ = nullptr;
+};
+
+// Latches the value of an encoder on rising and falling edges of a digital
+// input.
+class EdgeCounter : public InterruptHandler {
+ public:
+ EdgeCounter(Encoder *encoder, DigitalSource *input)
+ : encoder_(encoder), input_(input) {}
+
+ // Returns the current interrupt edge counts and encoder values.
+ int positive_interrupt_count() const {
+ return output_.positive_interrupt_count;
+ }
+ int negative_interrupt_count() const {
+ return output_.negative_interrupt_count;
+ }
+ int32_t last_positive_encoder_value() const {
+ return output_.last_positive_encoder_value;
+ }
+ int32_t last_negative_encoder_value() const {
+ return output_.last_negative_encoder_value;
+ }
+ // Returns the current polled value.
+ bool polled_value() const { return output_.polled_value; }
+
+ private:
+ struct OutputValues {
+ bool polled_value = false;
+ int positive_interrupt_count = 0, negative_interrupt_count = 0;
+ int32_t last_positive_encoder_value = 0, last_negative_encoder_value = 0;
+ };
+
+ void GatherPolledValue() override;
+ void CommitValue() override { output_ = shadow_values_; }
+ void operator()() override;
+
+ Encoder *encoder_;
+ DigitalSource *input_;
+
+ // The following variables represent the current "shadow" state.
+ bool current_value_ = false;
+ bool last_miss_match_ = true;
+ OutputValues shadow_values_;
+
+ // The actual output values.
+ OutputValues output_;
+
+ DISALLOW_COPY_AND_ASSIGN(EdgeCounter);
+};
+
+// Synchronizes reading an encoder with interrupt handling.
+class InterruptSynchronizedEncoder : public InterruptHandler {
+ public:
+ InterruptSynchronizedEncoder(Encoder *encoder) : encoder_(encoder) {}
+
+ int32_t get() const { return output_; }
+
+ private:
+ void GatherPolledValue() override { shadow_ = encoder_->GetRaw(); }
+ void CommitValue() override { output_ = shadow_; }
+ void operator()() override {}
+
+ Encoder *const encoder_;
+
+ int32_t shadow_, output_;
+};
+
+// Synchronizes interrupts with poll-based sampling on multiple
+// InterruptHandlers.
+//
+// See InterruptHandler for an overview of the logic.
+//
+// Usage is to create an instance, call Add 1 or more times, call Start, and
+// then call RunIteration during normal sensor sampling. After RunIteration
+// returns, the output values from the various InterruptHandlers can be
+// retrieved.
+class InterruptSynchronizer {
+ public:
+ InterruptSynchronizer(int interrupt_priority)
+ : interrupt_priority_(interrupt_priority) {}
+
+ void Add(InterruptHandler *handler) {
+ handler->set_mutex(&mutex_);
+ handler->set_priority(interrupt_priority_);
+ handlers_.emplace_back(handler);
+ }
+
+ void Start() {
+ for (auto &c : handlers_) {
+ c->Start();
+ }
+ }
+
+ // Updates all of the counts and makes sure everything is synchronized.
+ // IMPORTANT: This will usually only take 120uS but WILL occasionally take
+ // longer, so be careful about letting that jitter get into control loops.
+ void RunIteration();
+
+ // Asks all of the InterruptHandlers to stop and waits until they have done
+ // so.
+ void Quit() {
+ for (auto &c : handlers_) {
+ c->Quit();
+ }
+ }
+
+ private:
+ // Starts a sampling iteration. See RunIteration for usage.
+ // Returns true if we are ready to go or false if we already need to retry.
+ bool TryStartIteration();
+
+ // Attempts to finish a sampling iteration. See RunIteration for usage.
+ // Returns true if the iteration succeeded, and false otherwise.
+ bool TryFinishingIteration();
+
+ const int interrupt_priority_;
+
+ // The mutex used to synchronize all the sampling.
+ ::aos::stl_mutex mutex_;
+
+ ::std::vector<InterruptHandler *> handlers_;
+
+ DISALLOW_COPY_AND_ASSIGN(InterruptSynchronizer);
+};
+
+} // namespace wpilib
+} // namespace frc971
+
+#endif // FRC971_WPILIB_INTERRUPT_EDGE_COUNTING_H_
diff --git a/frc971/wpilib/joystick_sender.cc b/frc971/wpilib/joystick_sender.cc
new file mode 100644
index 0000000..236cc0f
--- /dev/null
+++ b/frc971/wpilib/joystick_sender.cc
@@ -0,0 +1,50 @@
+#include "frc971/wpilib/joystick_sender.h"
+
+#include "aos/common/messages/robot_state.q.h"
+#include "aos/linux_code/init.h"
+#include "aos/common/network/team_number.h"
+#include "aos/common/logging/queue_logging.h"
+
+#include "DriverStation.h"
+#include "HAL/HAL.hpp"
+
+namespace frc971 {
+namespace wpilib {
+
+void JoystickSender::operator()() {
+ DriverStation *ds = DriverStation::GetInstance();
+ ::aos::SetCurrentThreadName("DSReader");
+ uint16_t team_id = ::aos::network::GetTeamNumber();
+
+ ::aos::SetCurrentThreadRealtimePriority(29);
+
+ while (run_) {
+ ds->WaitForData();
+ auto new_state = ::aos::joystick_state.MakeMessage();
+
+ HALControlWord control_word;
+ HALGetControlWord(&control_word);
+ new_state->test_mode = control_word.test;
+ new_state->fms_attached = control_word.fmsAttached;
+ new_state->enabled = control_word.enabled;
+ new_state->autonomous = control_word.autonomous;
+ new_state->team_id = team_id;
+ new_state->fake = false;
+
+ for (int i = 0; i < 4; ++i) {
+ new_state->joysticks[i].buttons = ds->GetStickButtons(i);
+ for (int j = 0; j < 4; ++j) {
+ new_state->joysticks[i].axis[j] = ds->GetStickAxis(i, j);
+ }
+ new_state->joysticks[i].pov = ds->GetStickPOV(i, 0);
+ }
+ LOG_STRUCT(DEBUG, "joystick_state", *new_state);
+
+ if (!new_state.Send()) {
+ LOG(WARNING, "sending joystick_state failed\n");
+ }
+ }
+}
+
+} // namespace wpilib
+} // namespace frc971
diff --git a/frc971/wpilib/joystick_sender.h b/frc971/wpilib/joystick_sender.h
new file mode 100644
index 0000000..205f9c4
--- /dev/null
+++ b/frc971/wpilib/joystick_sender.h
@@ -0,0 +1,22 @@
+#ifndef FRC971_WPILIB_JOYSTICK_SENDER_H_
+#define FRC971_WPILIB_JOYSTICK_SENDER_H_
+
+#include <atomic>
+
+namespace frc971 {
+namespace wpilib {
+
+class JoystickSender {
+ public:
+ void operator()();
+
+ void Quit() { run_ = false; }
+
+ private:
+ ::std::atomic<bool> run_{true};
+};
+
+} // namespace wpilib
+} // namespace frc971
+
+#endif // FRC971_WPILIB_JOYSTICK_SENDER_H_
diff --git a/frc971/wpilib/logging.q b/frc971/wpilib/logging.q
new file mode 100644
index 0000000..a2b3799
--- /dev/null
+++ b/frc971/wpilib/logging.q
@@ -0,0 +1,7 @@
+package frc971.wpilib;
+
+// Information about the current state of the pneumatics system to log.
+struct PneumaticsToLog {
+ bool compressor_on;
+ uint8_t read_solenoids;
+};
diff --git a/frc971/wpilib/loop_output_handler.cc b/frc971/wpilib/loop_output_handler.cc
new file mode 100644
index 0000000..cb5e4ec
--- /dev/null
+++ b/frc971/wpilib/loop_output_handler.cc
@@ -0,0 +1,72 @@
+#include "frc971/wpilib/loop_output_handler.h"
+
+#include <sys/timerfd.h>
+
+#include <thread>
+#include <functional>
+
+#include "aos/linux_code/init.h"
+#include "aos/common/messages/robot_state.q.h"
+
+namespace frc971 {
+namespace wpilib {
+
+constexpr ::aos::time::Time LoopOutputHandler::kStopTimeout;
+
+LoopOutputHandler::LoopOutputHandler() : watchdog_(this) {}
+
+void LoopOutputHandler::operator()() {
+ ::aos::SetCurrentThreadName("OutputHandler");
+ ::std::thread watchdog_thread(::std::ref(watchdog_));
+
+ ::aos::SetCurrentThreadRealtimePriority(30);
+ while (run_) {
+ no_joystick_state_.Print();
+ fake_joystick_state_.Print();
+ Read();
+ ::aos::joystick_state.FetchLatest();
+ if (!::aos::joystick_state.get()) {
+ LOG_INTERVAL(no_joystick_state_);
+ continue;
+ }
+ if (::aos::joystick_state->fake) {
+ LOG_INTERVAL(fake_joystick_state_);
+ continue;
+ }
+
+ watchdog_.Reset();
+ Write();
+ }
+
+ Stop();
+
+ watchdog_.Quit();
+ watchdog_thread.join();
+}
+
+LoopOutputHandler::Watchdog::Watchdog(LoopOutputHandler *handler)
+ : handler_(handler),
+ timerfd_(timerfd_create(::aos::time::Time::kDefaultClock, 0)) {
+ if (timerfd_.get() == -1) {
+ PLOG(FATAL, "timerfd_create(Time::kDefaultClock, 0)");
+ }
+ ::aos::SetCurrentThreadRealtimePriority(35);
+ ::aos::SetCurrentThreadName("OutputWatchdog");
+}
+
+void LoopOutputHandler::Watchdog::operator()() {
+ uint8_t buf[8];
+ while (run_) {
+ PCHECK(read(timerfd_.get(), buf, sizeof(buf)));
+ handler_->Stop();
+ }
+}
+
+void LoopOutputHandler::Watchdog::Reset() {
+ itimerspec value = itimerspec();
+ value.it_value = kStopTimeout.ToTimespec();
+ PCHECK(timerfd_settime(timerfd_.get(), 0, &value, nullptr));
+}
+
+} // namespace wpilib
+} // namespace frc971
diff --git a/frc971/wpilib/loop_output_handler.h b/frc971/wpilib/loop_output_handler.h
new file mode 100644
index 0000000..fe32763
--- /dev/null
+++ b/frc971/wpilib/loop_output_handler.h
@@ -0,0 +1,89 @@
+#ifndef FRC971_WPILIB_LOOP_OUTPUT_HANDLER_H_
+#define FRC971_WPILIB_LOOP_OUTPUT_HANDLER_H_
+
+#include "aos/common/scoped_fd.h"
+#include "aos/common/time.h"
+#include "aos/common/util/log_interval.h"
+
+#include <atomic>
+
+namespace frc971 {
+namespace wpilib {
+
+// Handles sending the output from a single control loop to the hardware.
+//
+// This class implements stopping motors when no new values are received for too
+// long efficiently.
+//
+// The intended use is to have a subclass for each loop which implements the
+// pure virtual methods and is then run in a separate thread. The operator()
+// loops writing values until Quit() is called.
+class LoopOutputHandler {
+ public:
+ LoopOutputHandler();
+
+ void Quit() { run_ = false; }
+
+ void operator()();
+
+ protected:
+ // Read a message from the appropriate queue.
+ // This must block.
+ virtual void Read() = 0;
+
+ // Send the output from the appropriate queue to the hardware.
+ // Read() will always be called at least once before per invocation of this.
+ virtual void Write() = 0;
+
+ // Stop all outputs. This will be called in a separate thread (if at all).
+ // The subclass implementation should handle any appropriate logging.
+ // This will be called exactly once if Read() blocks for too long and once
+ // after Quit is called.
+ virtual void Stop() = 0;
+
+ private:
+ // The thread that actually contains a timerfd to implement timeouts. The
+ // idea is to have a timerfd that is repeatedly set to the timeout expiration
+ // in the future so it will never actually expire unless it is not reset for
+ // too long.
+ //
+ // This class nicely encapsulates the raw fd and manipulating it. Its
+ // operator() loops until Quit() is called, calling Stop() on its associated
+ // LoopOutputHandler whenever the timerfd expires.
+ class Watchdog {
+ public:
+ Watchdog(LoopOutputHandler *handler);
+
+ void operator()();
+
+ void Reset();
+
+ void Quit() { run_ = false; }
+
+ private:
+ LoopOutputHandler *const handler_;
+
+ ::aos::ScopedFD timerfd_;
+
+ ::std::atomic<bool> run_{true};
+ };
+
+ static constexpr ::aos::time::Time kStopTimeout =
+ ::aos::time::Time::InSeconds(0.02);
+
+ Watchdog watchdog_;
+
+ ::std::atomic<bool> run_{true};
+
+ ::aos::util::SimpleLogInterval no_joystick_state_ =
+ ::aos::util::SimpleLogInterval(::aos::time::Time::InSeconds(0.5), INFO,
+ "no joystick state -> not outputting");
+ ::aos::util::SimpleLogInterval fake_joystick_state_ =
+ ::aos::util::SimpleLogInterval(::aos::time::Time::InSeconds(0.5), DEBUG,
+ "fake joystick state -> not outputting");
+};
+
+} // namespace wpilib
+} // namespace frc971
+
+#endif // FRC971_WPILIB_LOOP_OUTPUT_HANDLER_H_
diff --git a/frc971/wpilib/wpilib.gyp b/frc971/wpilib/wpilib.gyp
new file mode 100644
index 0000000..997b810
--- /dev/null
+++ b/frc971/wpilib/wpilib.gyp
@@ -0,0 +1,165 @@
+{
+ 'targets': [
+ {
+ 'target_name': 'logging_queue',
+ 'type': 'static_library',
+ 'sources': [
+ 'logging.q',
+ ],
+ 'variables': {
+ 'header_path': 'frc971/wpilib',
+ },
+ 'includes': [ '../../aos/build/queues.gypi' ],
+ },
+ {
+ 'target_name': 'encoder_and_potentiometer',
+ 'type': 'static_library',
+ 'sources': [
+ 'encoder_and_potentiometer.cc',
+ ],
+ 'dependencies': [
+ '<(EXTERNALS):WPILib',
+ 'dma_edge_counting',
+ '<(AOS)/linux_code/linux_code.gyp:init',
+ '<(AOS)/build/aos.gyp:logging',
+ '<(AOS)/common/common.gyp:mutex',
+ ],
+ 'export_dependent_settings': [
+ '<(EXTERNALS):WPILib',
+ 'dma_edge_counting',
+ '<(AOS)/common/common.gyp:mutex',
+ ],
+ },
+ {
+ 'target_name': 'dma_edge_counting',
+ 'type': 'static_library',
+ 'sources': [
+ 'dma_edge_counting.cc',
+ ],
+ 'dependencies': [
+ '<(EXTERNALS):WPILib',
+ '<(AOS)/build/aos.gyp:logging',
+ 'hall_effect',
+ ],
+ 'export_dependent_settings': [
+ '<(EXTERNALS):WPILib',
+ 'hall_effect',
+ ],
+ },
+ {
+ 'target_name': 'interrupt_edge_counting',
+ 'type': 'static_library',
+ 'sources': [
+ 'interrupt_edge_counting.cc',
+ ],
+ 'dependencies': [
+ '<(EXTERNALS):WPILib',
+ '<(AOS)/build/aos.gyp:logging',
+ '<(AOS)/common/common.gyp:stl_mutex',
+ '<(AOS)/common/common.gyp:time',
+ '<(AOS)/linux_code/linux_code.gyp:init',
+ ],
+ 'export_dependent_settings': [
+ '<(EXTERNALS):WPILib',
+ '<(AOS)/build/aos.gyp:logging',
+ '<(AOS)/common/common.gyp:stl_mutex',
+ ],
+ },
+ {
+ 'target_name': 'buffered_pcm',
+ 'type': 'static_library',
+ 'sources': [
+ 'buffered_solenoid.cc',
+ 'buffered_pcm.cc',
+ ],
+ 'dependencies': [
+ '<(EXTERNALS):WPILib',
+ '<(AOS)/build/aos.gyp:logging',
+ ],
+ 'export_dependent_settings': [
+ '<(EXTERNALS):WPILib',
+ ],
+ },
+ {
+ 'target_name': 'gyro_interface',
+ 'type': 'static_library',
+ 'sources': [
+ 'gyro_interface.cc',
+ ],
+ 'dependencies': [
+ '<(EXTERNALS):WPILib',
+ '<(AOS)/build/aos.gyp:logging',
+ ],
+ 'export_dependent_settings': [
+ '<(EXTERNALS):WPILib',
+ ],
+ },
+ {
+ 'target_name': 'gyro_sender',
+ 'type': 'static_library',
+ 'sources': [
+ 'gyro_sender.cc',
+ ],
+ 'dependencies': [
+ '<(DEPTH)/frc971/queues/queues.gyp:gyro',
+ 'gyro_interface',
+ '<(AOS)/build/aos.gyp:logging',
+ '<(AOS)/common/logging/logging.gyp:queue_logging',
+ '<(AOS)/common/util/util.gyp:phased_loop',
+ '<(AOS)/common/messages/messages.gyp:robot_state',
+ '<(AOS)/linux_code/linux_code.gyp:init',
+ '<(AOS)/common/common.gyp:time',
+ ],
+ 'export_dependent_settings': [
+ 'gyro_interface',
+ '<(AOS)/common/common.gyp:time',
+ ],
+ },
+ {
+ 'target_name': 'loop_output_handler',
+ 'type': 'static_library',
+ 'sources': [
+ 'loop_output_handler.cc',
+ ],
+ 'dependencies': [
+ '<(AOS)/common/common.gyp:scoped_fd',
+ '<(AOS)/linux_code/linux_code.gyp:init',
+ '<(AOS)/common/common.gyp:time',
+ '<(AOS)/common/util/util.gyp:log_interval',
+ '<(AOS)/common/messages/messages.gyp:robot_state',
+ ],
+ 'export_dependent_settings': [
+ '<(AOS)/common/common.gyp:scoped_fd',
+ '<(AOS)/common/common.gyp:time',
+ '<(AOS)/common/util/util.gyp:log_interval',
+ ],
+ },
+ {
+ 'target_name': 'joystick_sender',
+ 'type': 'static_library',
+ 'sources': [
+ 'joystick_sender.cc',
+ ],
+ 'dependencies': [
+ '<(EXTERNALS):WPILib',
+ '<(AOS)/common/messages/messages.gyp:robot_state',
+ '<(AOS)/linux_code/linux_code.gyp:init',
+ '<(AOS)/common/network/network.gyp:team_number',
+ '<(AOS)/common/logging/logging.gyp:queue_logging',
+ ],
+ },
+ {
+ 'target_name': 'hall_effect',
+ 'type': 'static_library',
+ 'sources': [
+ #'hall_effect.h',
+ ],
+ 'dependencies': [
+ '<(EXTERNALS):WPILib',
+ ],
+ 'export_dependent_settings': [
+ '<(EXTERNALS):WPILib',
+ ],
+ },
+ ],
+}
diff --git a/frc971/zeroing/zeroing.cc b/frc971/zeroing/zeroing.cc
new file mode 100644
index 0000000..e54446a
--- /dev/null
+++ b/frc971/zeroing/zeroing.cc
@@ -0,0 +1,124 @@
+#include "frc971/zeroing/zeroing.h"
+
+#include <cmath>
+#include <vector>
+
+namespace frc971 {
+namespace zeroing {
+
+void PopulateEstimatorState(const zeroing::ZeroingEstimator& estimator,
+ EstimatorState* state) {
+ state->error = estimator.error();
+ state->zeroed = estimator.zeroed();
+ state->position = estimator.position();
+}
+
+ZeroingEstimator::ZeroingEstimator(
+ const constants::ZeroingConstants& constants) {
+ index_diff_ = constants.index_difference;
+ max_sample_count_ = constants.average_filter_size;
+ known_index_pos_ = constants.measured_index_position;
+ allowable_encoder_error_ = constants.allowable_encoder_error;
+ start_pos_samples_.reserve(max_sample_count_);
+ Reset();
+}
+
+void ZeroingEstimator::Reset() {
+ samples_idx_ = 0;
+ start_pos_ = 0;
+ start_pos_samples_.clear();
+ zeroed_ = false;
+ wait_for_index_pulse_ = true;
+ last_used_index_pulse_count_ = 0;
+ first_start_pos_ = 0.0;
+ error_ = false;
+}
+
+void ZeroingEstimator::TriggerError() {
+ if (!error_) {
+ LOG(ERROR, "Manually triggered zeroing error.\n");
+ error_ = true;
+ }
+}
+
+double ZeroingEstimator::CalculateStartPosition(double start_average,
+ double latched_encoder) const {
+ // We calculate an aproximation of the value of the last index position.
+ // Also account for index pulses not lining up with integer multiples of the
+ // index_diff.
+ double index_pos = start_average + latched_encoder - known_index_pos_;
+ // We round index_pos to the closest valid value of the index.
+ double accurate_index_pos = (round(index_pos / index_diff_)) * index_diff_;
+ // Now we reverse the first calculation to get the accurate start position.
+ return accurate_index_pos - latched_encoder + known_index_pos_;
+}
+
+void ZeroingEstimator::UpdateEstimate(const PotAndIndexPosition& info) {
+ // We want to make sure that we encounter at least one index pulse while
+ // zeroing. So we take the index pulse count from the first sample after
+ // reset and wait for that count to change before we consider ourselves
+ // zeroed.
+ if (wait_for_index_pulse_) {
+ last_used_index_pulse_count_ = info.index_pulses;
+ wait_for_index_pulse_ = false;
+ }
+
+ if (start_pos_samples_.size() < max_sample_count_) {
+ start_pos_samples_.push_back(info.pot - info.encoder);
+ } else {
+ start_pos_samples_[samples_idx_] = info.pot - info.encoder;
+ }
+
+ // Drop the oldest sample when we run this function the next time around.
+ samples_idx_ = (samples_idx_ + 1) % max_sample_count_;
+
+ double sample_sum = 0.0;
+
+ for (size_t i = 0; i < start_pos_samples_.size(); ++i) {
+ sample_sum += start_pos_samples_[i];
+ }
+
+ // Calculates the average of the starting position.
+ double start_average = sample_sum / start_pos_samples_.size();
+
+ // If there are no index pulses to use or we don't have enough samples yet to
+ // have a well-filtered starting position then we use the filtered value as
+ // our best guess.
+ if (!zeroed_ && (info.index_pulses == last_used_index_pulse_count_ ||
+ offset_ratio_ready() < 1.0)) {
+ start_pos_ = start_average;
+ } else if (!zeroed_ || last_used_index_pulse_count_ != info.index_pulses) {
+ // Note the accurate start position and the current index pulse count so
+ // that we only run this logic once per index pulse. That should be more
+ // resilient to corrupted intermediate data.
+ start_pos_ = CalculateStartPosition(start_average, info.latched_encoder);
+ last_used_index_pulse_count_ = info.index_pulses;
+ // Save the first starting position.
+ if (!zeroed_) {
+ first_start_pos_ = start_pos_;
+ LOG(INFO, "latching start position %f\n", first_start_pos_);
+ }
+
+ // Now that we have an accurate starting position we can consider ourselves
+ // zeroed.
+ zeroed_ = true;
+ // Throw an error if first_start_pos is bigger/smaller than
+ // allowable_encoder_error_ * index_diff +
+ // start_pos.
+ if (::std::abs(first_start_pos_ - start_pos_) >
+ allowable_encoder_error_ * index_diff_) {
+ if (!error_) {
+ LOG(ERROR,
+ "Encoder ticks out of range since last index pulse. first start "
+ "position: %f recent starting position: %f\n",
+ first_start_pos_, start_pos_);
+ error_ = true;
+ }
+ }
+ }
+
+ pos_ = start_pos_ + info.encoder;
+}
+
+} // namespace zeroing
+} // namespace frc971
diff --git a/frc971/zeroing/zeroing.gyp b/frc971/zeroing/zeroing.gyp
new file mode 100644
index 0000000..0a7a2fc
--- /dev/null
+++ b/frc971/zeroing/zeroing.gyp
@@ -0,0 +1,33 @@
+{
+ 'targets': [
+ {
+ 'target_name': 'zeroing',
+ 'type': 'static_library',
+ 'sources': [
+ 'zeroing.cc',
+ ],
+ 'dependencies': [
+ '<(DEPTH)/frc971/control_loops/control_loops.gyp:queues',
+ ],
+ 'export_dependent_settings': [
+ '<(DEPTH)/frc971/control_loops/control_loops.gyp:queues',
+ ],
+ },
+ {
+ 'target_name': 'zeroing_test',
+ 'type': 'executable',
+ 'sources': [
+ 'zeroing_test.cc',
+ ],
+ 'dependencies': [
+ '<(EXTERNALS):gtest',
+ '<(AOS)/common/common.gyp:queue_testutils',
+ 'zeroing',
+ '<(AOS)/common/util/util.gyp:thread',
+ '<(AOS)/common/common.gyp:die',
+ '<(DEPTH)/frc971/control_loops/control_loops.gyp:position_sensor_sim',
+ '<(DEPTH)/frc971/control_loops/control_loops.gyp:queues',
+ ],
+ },
+ ],
+}
diff --git a/frc971/zeroing/zeroing.h b/frc971/zeroing/zeroing.h
new file mode 100644
index 0000000..4176f48
--- /dev/null
+++ b/frc971/zeroing/zeroing.h
@@ -0,0 +1,121 @@
+#ifndef FRC971_ZEROING_ZEROING_H_
+#define FRC971_ZEROING_ZEROING_H_
+
+#include <cstdint>
+#include <vector>
+
+#include "frc971/control_loops/control_loops.q.h"
+#include "frc971/constants.h"
+
+// TODO(pschrader): Flag an error if encoder index pulse is not n revolutions
+// away from the last one (i.e. got extra counts from noise, etc..)
+//
+// TODO(pschrader): Flag error if the pot disagrees too much with the encoder
+// after being zeroed.
+//
+// TODO(pschrader): Watch the offset over long periods of time and flag if it
+// gets too far away from the initial value.
+
+namespace frc971 {
+namespace zeroing {
+
+// Estimates the position with encoder,
+// the pot and the indices.
+class ZeroingEstimator {
+ public:
+ ZeroingEstimator(const constants::ZeroingConstants &constants);
+
+ // Update the internal logic with the next sensor values.
+ void UpdateEstimate(const PotAndIndexPosition &info);
+
+ // Reset the internal logic so it needs to be re-zeroed.
+ void Reset();
+
+ // Manually trigger an internal error. This is used for testing the error
+ // logic.
+ void TriggerError();
+
+ // Returns true if an error has occurred, false otherwise. This gets reset to
+ // false when the Reset() function is called.
+ bool error() const { return error_; }
+
+ // Returns true if the logic considers the corresponding mechanism to be
+ // zeroed. It return false otherwise. For example, right after a call to
+ // Reset() this returns false.
+ bool zeroed() const { return zeroed_; }
+
+ // Return the estimated position of the corresponding mechanism. This value
+ // is in SI units. For example, the estimator for the elevator would return a
+ // value in meters for the height relative to absolute zero.
+ double position() const { return pos_; }
+
+ // Return the estimated starting position of the corresponding mechansim. In
+ // some contexts we refer to this as the "offset".
+ double offset() const { return start_pos_; }
+
+ // Returns a number between 0 and 1 that represents the percentage of the
+ // samples being used in the moving average filter. A value of 0.0 means that
+ // no samples are being used. A value of 1.0 means that the filter is using
+ // as many samples as it has room for. For example, after a Reset() this
+ // value returns 0.0. As more samples get added with UpdateEstimate(...) the
+ // return value starts increasing to 1.0.
+ double offset_ratio_ready() const {
+ return start_pos_samples_.size() / static_cast<double>(max_sample_count_);
+ }
+
+ private:
+ // This function calculates the start position given the internal state and
+ // the provided `latched_encoder' value.
+ double CalculateStartPosition(double start_average,
+ double latched_encoder) const;
+
+ // The estimated position.
+ double pos_;
+ // The distance between two consecutive index positions.
+ double index_diff_;
+ // The next position in 'start_pos_samples_' to be used to store the next
+ // sample.
+ int samples_idx_;
+ // Last 'max_sample_count_' samples for start positions.
+ std::vector<double> start_pos_samples_;
+ // The number of the last samples of start position to consider in the
+ // estimation.
+ size_t max_sample_count_;
+ // The estimated starting position of the mechanism. We also call this the
+ // 'offset' in some contexts.
+ double start_pos_;
+ // The absolute position of any index pulse on the mechanism. This is used to
+ // account for the various ways the encoders get mounted into the robot.
+ double known_index_pos_;
+ // Flag for triggering logic that takes note of the current index pulse count
+ // after a reset. See `last_used_index_pulse_count_'.
+ bool wait_for_index_pulse_;
+ // After a reset we keep track of the index pulse count with this. Only after
+ // the index pulse count changes (i.e. increments at least once or wraps
+ // around) will we consider the mechanism zeroed. We also use this to store
+ // the most recent `PotAndIndexPosition::index_pulses' value when the start
+ // position was calculated. It helps us calculate the start position only on
+ // index pulses to reject corrupted intermediate data.
+ uint32_t last_used_index_pulse_count_;
+ // Marker to track whether we're fully zeroed yet or not.
+ bool zeroed_;
+ // Marker to track whether an error has occurred. This gets reset to false
+ // whenever Reset() is called.
+ bool error_;
+ // Stores the position "start_pos" variable the first time the program
+ // is zeroed.
+ double first_start_pos_;
+ // Value between 0 and 1 which determines a fraction of the index_diff
+ // you want to use.
+ double allowable_encoder_error_;
+};
+
+// Populates an EstimatorState struct with information from the zeroing
+// estimator.
+void PopulateEstimatorState(const ZeroingEstimator &estimator,
+ EstimatorState *state);
+
+} // namespace zeroing
+} // namespace frc971
+
+#endif // FRC971_ZEROING_ZEROING_H_
diff --git a/frc971/zeroing/zeroing_queue.q b/frc971/zeroing/zeroing_queue.q
new file mode 100644
index 0000000..e218173
--- /dev/null
+++ b/frc971/zeroing/zeroing_queue.q
@@ -0,0 +1,14 @@
+package frc971.zeroing;
+
+struct ZeroingInfo {
+ double pot;
+ double encoder;
+ double index_encoder;
+ int32_t index_count;
+};
+
+message TestMessage {
+ int32_t test_int;
+};
+
+queue TestMessage test_queue;
diff --git a/frc971/zeroing/zeroing_test.cc b/frc971/zeroing/zeroing_test.cc
new file mode 100644
index 0000000..4a82e04
--- /dev/null
+++ b/frc971/zeroing/zeroing_test.cc
@@ -0,0 +1,297 @@
+#include <unistd.h>
+
+#include <memory>
+
+#include <random>
+
+#include "gtest/gtest.h"
+#include "frc971/zeroing/zeroing.h"
+#include "frc971/control_loops/control_loops.q.h"
+#include "aos/common/queue_testutils.h"
+#include "aos/common/util/thread.h"
+#include "aos/common/die.h"
+#include "frc971/control_loops/position_sensor_sim.h"
+
+namespace frc971 {
+namespace zeroing {
+
+using control_loops::PositionSensorSimulator;
+using constants::ZeroingConstants;
+
+static const size_t kSampleSize = 30;
+static const double kAcceptableUnzeroedError = 0.2;
+static const double kIndexErrorFraction = 0.3;
+
+class ZeroingTest : public ::testing::Test {
+ protected:
+ void SetUp() override { aos::SetDieTestMode(true); }
+
+ void MoveTo(PositionSensorSimulator* simulator, ZeroingEstimator* estimator,
+ double new_position) {
+ PotAndIndexPosition sensor_values_;
+ simulator->MoveTo(new_position);
+ simulator->GetSensorValues(&sensor_values_);
+ estimator->UpdateEstimate(sensor_values_);
+ }
+
+ aos::common::testing::GlobalCoreInstance my_core;
+};
+
+TEST_F(ZeroingTest, TestMovingAverageFilter) {
+ const double index_diff = 1.0;
+ PositionSensorSimulator sim(index_diff);
+ sim.Initialize(3.6 * index_diff, index_diff / 3.0);
+ ZeroingEstimator estimator(ZeroingConstants{
+ kSampleSize, index_diff, 0.0, kIndexErrorFraction});
+
+ // The zeroing code is supposed to perform some filtering on the difference
+ // between the potentiometer value and the encoder value. We assume that 300
+ // samples are sufficient to have updated the filter.
+ for (int i = 0; i < 300; i++) {
+ MoveTo(&sim, &estimator, 3.3 * index_diff);
+ }
+ ASSERT_NEAR(3.3 * index_diff, estimator.position(),
+ kAcceptableUnzeroedError * index_diff);
+
+ for (int i = 0; i < 300; i++) {
+ MoveTo(&sim, &estimator, 3.9 * index_diff);
+ }
+ ASSERT_NEAR(3.9 * index_diff, estimator.position(),
+ kAcceptableUnzeroedError * index_diff);
+}
+
+TEST_F(ZeroingTest, NotZeroedBeforeEnoughSamplesCollected) {
+ double index_diff = 0.5;
+ double position = 3.6 * index_diff;
+ PositionSensorSimulator sim(index_diff);
+ sim.Initialize(position, index_diff / 3.0);
+ ZeroingEstimator estimator(ZeroingConstants{
+ kSampleSize, index_diff, 0.0, kIndexErrorFraction});
+
+ // Make sure that the zeroing code does not consider itself zeroed until we
+ // collect a good amount of samples. In this case we're waiting until the
+ // moving average filter is full.
+ for (unsigned int i = 0; i < kSampleSize - 1; i++) {
+ MoveTo(&sim, &estimator, position += index_diff);
+ ASSERT_FALSE(estimator.zeroed());
+ }
+
+ MoveTo(&sim, &estimator, position);
+ ASSERT_TRUE(estimator.zeroed());
+}
+
+TEST_F(ZeroingTest, TestLotsOfMovement) {
+ double index_diff = 1.0;
+ PositionSensorSimulator sim(index_diff);
+ sim.Initialize(3.6, index_diff / 3.0);
+ ZeroingEstimator estimator(ZeroingConstants{
+ kSampleSize, index_diff, 0.0, kIndexErrorFraction});
+
+ // The zeroing code is supposed to perform some filtering on the difference
+ // between the potentiometer value and the encoder value. We assume that 300
+ // samples are sufficient to have updated the filter.
+ for (int i = 0; i < 300; i++) {
+ MoveTo(&sim, &estimator, 3.6);
+ }
+ ASSERT_NEAR(3.6, estimator.position(), kAcceptableUnzeroedError * index_diff);
+
+ // With a single index pulse the zeroing estimator should be able to lock
+ // onto the true value of the position.
+ MoveTo(&sim, &estimator, 4.01);
+ ASSERT_NEAR(4.01, estimator.position(), 0.001);
+
+ MoveTo(&sim, &estimator, 4.99);
+ ASSERT_NEAR(4.99, estimator.position(), 0.001);
+
+ MoveTo(&sim, &estimator, 3.99);
+ ASSERT_NEAR(3.99, estimator.position(), 0.001);
+
+ MoveTo(&sim, &estimator, 3.01);
+ ASSERT_NEAR(3.01, estimator.position(), 0.001);
+
+ MoveTo(&sim, &estimator, 13.55);
+ ASSERT_NEAR(13.55, estimator.position(), 0.001);
+}
+
+TEST_F(ZeroingTest, TestDifferentIndexDiffs) {
+ double index_diff = 0.89;
+ PositionSensorSimulator sim(index_diff);
+ sim.Initialize(3.5 * index_diff, index_diff / 3.0);
+ ZeroingEstimator estimator(ZeroingConstants{
+ kSampleSize, index_diff, 0.0, kIndexErrorFraction});
+
+ // The zeroing code is supposed to perform some filtering on the difference
+ // between the potentiometer value and the encoder value. We assume that 300
+ // samples are sufficient to have updated the filter.
+ for (int i = 0; i < 300; i++) {
+ MoveTo(&sim, &estimator, 3.5 * index_diff);
+ }
+ ASSERT_NEAR(3.5 * index_diff, estimator.position(),
+ kAcceptableUnzeroedError * index_diff);
+
+ // With a single index pulse the zeroing estimator should be able to lock
+ // onto the true value of the position.
+ MoveTo(&sim, &estimator, 4.01);
+ ASSERT_NEAR(4.01, estimator.position(), 0.001);
+
+ MoveTo(&sim, &estimator, 4.99);
+ ASSERT_NEAR(4.99, estimator.position(), 0.001);
+
+ MoveTo(&sim, &estimator, 3.99);
+ ASSERT_NEAR(3.99, estimator.position(), 0.001);
+
+ MoveTo(&sim, &estimator, 3.01);
+ ASSERT_NEAR(3.01, estimator.position(), 0.001);
+
+ MoveTo(&sim, &estimator, 13.55);
+ ASSERT_NEAR(13.55, estimator.position(), 0.001);
+}
+
+TEST_F(ZeroingTest, TestPercentage) {
+ double index_diff = 0.89;
+ PositionSensorSimulator sim(index_diff);
+ sim.Initialize(3.5 * index_diff, index_diff / 3.0);
+ ZeroingEstimator estimator(ZeroingConstants{
+ kSampleSize, index_diff, 0.0, kIndexErrorFraction});
+
+ for (unsigned int i = 0; i < kSampleSize / 2; i++) {
+ MoveTo(&sim, &estimator, 3.5 * index_diff);
+ }
+ ASSERT_NEAR(0.5, estimator.offset_ratio_ready(), 0.001);
+}
+
+TEST_F(ZeroingTest, TestOffset) {
+ double index_diff = 0.89;
+ PositionSensorSimulator sim(index_diff);
+ sim.Initialize(3.1 * index_diff, index_diff / 3.0);
+ ZeroingEstimator estimator(ZeroingConstants{
+ kSampleSize, index_diff, 0.0, kIndexErrorFraction});
+
+ MoveTo(&sim, &estimator, 3.1 * index_diff);
+
+ for (unsigned int i = 0; i < kSampleSize; i++) {
+ MoveTo(&sim, &estimator, 5.0 * index_diff);
+ }
+
+ ASSERT_NEAR(3.1 * index_diff, estimator.offset(), 0.001);
+}
+
+TEST_F(ZeroingTest, WaitForIndexPulseAfterReset) {
+ double index_diff = 0.6;
+ PositionSensorSimulator sim(index_diff);
+ sim.Initialize(3.1 * index_diff, index_diff / 3.0);
+ ZeroingEstimator estimator(ZeroingConstants{
+ kSampleSize, index_diff, 0.0, kIndexErrorFraction});
+
+ // Make sure to fill up the averaging filter with samples.
+ for (unsigned int i = 0; i < kSampleSize; i++) {
+ MoveTo(&sim, &estimator, 3.1 * index_diff);
+ }
+
+ // Make sure we're not zeroed until we hit an index pulse.
+ ASSERT_FALSE(estimator.zeroed());
+
+ // Trigger an index pulse; we should now be zeroed.
+ MoveTo(&sim, &estimator, 4.5 * index_diff);
+ ASSERT_TRUE(estimator.zeroed());
+
+ // Reset the zeroing logic and supply a bunch of samples within the current
+ // index segment.
+ estimator.Reset();
+ for (unsigned int i = 0; i < kSampleSize; i++) {
+ MoveTo(&sim, &estimator, 4.2 * index_diff);
+ }
+
+ // Make sure we're not zeroed until we hit an index pulse.
+ ASSERT_FALSE(estimator.zeroed());
+
+ // Trigger another index pulse; we should be zeroed again.
+ MoveTo(&sim, &estimator, 3.1 * index_diff);
+ ASSERT_TRUE(estimator.zeroed());
+}
+
+TEST_F(ZeroingTest, TestNonZeroIndexPulseOffsets) {
+ const double index_diff = 0.9;
+ const double known_index_pos = 3.5 * index_diff;
+ PositionSensorSimulator sim(index_diff);
+ sim.Initialize(3.3 * index_diff, index_diff / 3.0, known_index_pos);
+ ZeroingEstimator estimator(ZeroingConstants{
+ kSampleSize, index_diff, known_index_pos, kIndexErrorFraction});
+
+ // Make sure to fill up the averaging filter with samples.
+ for (unsigned int i = 0; i < kSampleSize; i++) {
+ MoveTo(&sim, &estimator, 3.3 * index_diff);
+ }
+
+ // Make sure we're not zeroed until we hit an index pulse.
+ ASSERT_FALSE(estimator.zeroed());
+
+ // Trigger an index pulse; we should now be zeroed.
+ MoveTo(&sim, &estimator, 3.7 * index_diff);
+ ASSERT_TRUE(estimator.zeroed());
+ ASSERT_DOUBLE_EQ(3.3 * index_diff, estimator.offset());
+ ASSERT_DOUBLE_EQ(3.7 * index_diff, estimator.position());
+
+ // Trigger one more index pulse and check the offset.
+ MoveTo(&sim, &estimator, 4.7 * index_diff);
+ ASSERT_DOUBLE_EQ(3.3 * index_diff, estimator.offset());
+ ASSERT_DOUBLE_EQ(4.7 * index_diff, estimator.position());
+}
+
+TEST_F(ZeroingTest, BasicErrorAPITest) {
+ const double index_diff = 1.0;
+ ZeroingEstimator estimator(ZeroingConstants{
+ kSampleSize, index_diff, 0.0, kIndexErrorFraction});
+ PositionSensorSimulator sim(index_diff);
+ sim.Initialize(1.5 * index_diff, index_diff / 3.0, 0.0);
+
+ // Perform a simple move and make sure that no error occured.
+ MoveTo(&sim, &estimator, 3.5 * index_diff);
+ ASSERT_FALSE(estimator.error());
+
+ // Trigger an error and make sure it's reported.
+ estimator.TriggerError();
+ ASSERT_TRUE(estimator.error());
+
+ // Make sure that it can recover after a reset.
+ estimator.Reset();
+ ASSERT_FALSE(estimator.error());
+ MoveTo(&sim, &estimator, 4.5 * index_diff);
+ MoveTo(&sim, &estimator, 5.5 * index_diff);
+ ASSERT_FALSE(estimator.error());
+}
+
+// I want to test that the the zeroing class can
+// detect an error when the starting position
+// changes too much. I do so by creating the
+// simulator at an 'X' positon, making sure
+// that the estimator is zeroed, and then
+// initializing the simulator at another
+// position. After making sure it's zeroed,
+// if the error() function returns true,
+// then, it works.
+TEST_F(ZeroingTest, TestOffsetError) {
+ const double index_diff = 0.8;
+ const double known_index_pos = 2 * index_diff;
+ int sample_size = 30;
+ PositionSensorSimulator sim(index_diff);
+ sim.Initialize(10 * index_diff, index_diff / 3.0, known_index_pos);
+ ZeroingEstimator estimator(ZeroingConstants{
+ sample_size, index_diff, known_index_pos, kIndexErrorFraction});
+
+ for (int i = 0; i < sample_size; i++) {
+ MoveTo(&sim, &estimator, 13 * index_diff);
+ }
+ MoveTo(&sim, &estimator, 8 * index_diff);
+
+ ASSERT_TRUE(estimator.zeroed());
+ ASSERT_FALSE(estimator.error());
+ sim.Initialize(9.0 * index_diff + 0.31 * index_diff, index_diff / 3.0,
+ known_index_pos);
+ MoveTo(&sim, &estimator, 9 * index_diff);
+ ASSERT_TRUE(estimator.zeroed());
+ ASSERT_TRUE(estimator.error());
+}
+
+} // namespace zeroing
+} // namespace frc971
diff --git a/output/.gitignore b/output/.gitignore
new file mode 100644
index 0000000..d675b14
--- /dev/null
+++ b/output/.gitignore
@@ -0,0 +1,16 @@
+/prime/
+/crio/
+/flasher/
+/compiled-*/
+/ip_address.txt
+/ccache_dir/
+
+/crio/
+/crio-debug/
+
+/arm_frc-[^-]*-[^-]*/
+/arm_frc-[^-]*-debug-[^-]*/
+/amd64-[^-]*-[^-]*/
+/amd64-[^-]*-debug-[^-]*/
+/bot3-[^-]*-[^-]*-[^-]*/
+/bot3-[^-]*-[^-]*-debug-[^-]*/
diff --git a/output/downloaded/.gitignore b/output/downloaded/.gitignore
new file mode 100644
index 0000000..b59b5d2
--- /dev/null
+++ b/output/downloaded/.gitignore
@@ -0,0 +1,21 @@
+/ctemplate-129.tar.gz
+/ctemplate-129/
+/eigen-3.2.1.tar.bz2
+/eigen-3.2.1/
+/gtest-1.6.0-p2/
+/gtest-1.6.0.zip
+/gyp-1738/
+/jpeg-8d/
+/jpegsrc.v8d.tar.gz
+/libjpeg/
+/ninja-v1.4.0/
+/gflags-2.0.tar.gz
+/gflags-2.0/
+/libevent-2.0.21.tar.gz
+/libevent-2.0.21/
+/libcdd-094g.tar.gz
+/libcdd-094g/
+/gmp-5.1.3.tar.lz
+/gperftools-2.3.tar.gz
+/libunwind-1.1.tar.gz
+/seasocks-1.1.2-p1/
diff --git a/third_party/gflags/.gitattributes b/third_party/gflags/.gitattributes
new file mode 100644
index 0000000..87fe9c0
--- /dev/null
+++ b/third_party/gflags/.gitattributes
@@ -0,0 +1,3 @@
+# treat all files in this repository as text files
+# and normalize them to LF line endings when committed
+* text
diff --git a/third_party/gflags/.gitignore b/third_party/gflags/.gitignore
new file mode 100644
index 0000000..4281522
--- /dev/null
+++ b/third_party/gflags/.gitignore
@@ -0,0 +1,17 @@
+/xcode/
+/build/
+/build-*/
+.DS_Store
+CMakeCache.txt
+DartConfiguration.tcl
+Makefile
+CMakeFiles/
+/Testing/
+/include/gflags/config.h
+/include/gflags/gflags_completions.h
+/include/gflags/gflags_declare.h
+/include/gflags/gflags.h
+/lib/
+/test/gflags_unittest_main.cc
+/test/gflags_unittest-main.cc
+/packages/
diff --git a/third_party/gflags/.gitmodules b/third_party/gflags/.gitmodules
new file mode 100644
index 0000000..aa2072c
--- /dev/null
+++ b/third_party/gflags/.gitmodules
@@ -0,0 +1,4 @@
+[submodule "doc"]
+ path = doc
+ url = https://github.com/gflags/gflags.git
+ branch = gh-pages
diff --git a/third_party/gflags/AUTHORS.txt b/third_party/gflags/AUTHORS.txt
new file mode 100644
index 0000000..887918b
--- /dev/null
+++ b/third_party/gflags/AUTHORS.txt
@@ -0,0 +1,2 @@
+google-gflags@googlegroups.com
+
diff --git a/third_party/gflags/CMakeLists.txt b/third_party/gflags/CMakeLists.txt
new file mode 100644
index 0000000..c32bce3
--- /dev/null
+++ b/third_party/gflags/CMakeLists.txt
@@ -0,0 +1,510 @@
+cmake_minimum_required (VERSION 2.8.12 FATAL_ERROR)
+
+if (POLICY CMP0042)
+ cmake_policy (SET CMP0042 NEW)
+endif ()
+
+# ----------------------------------------------------------------------------
+# includes
+set (CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
+include (utils)
+
+# ----------------------------------------------------------------------------
+# package information
+set (PACKAGE_NAME "gflags")
+set (PACKAGE_VERSION "2.2.0")
+set (PACKAGE_STRING "${PACKAGE_NAME} ${PACKAGE_VERSION}")
+set (PACKAGE_TARNAME "${PACKAGE_NAME}-${PACKAGE_VERSION}")
+set (PACKAGE_BUGREPORT "https://github.com/schuhschuh/gflags/issues")
+
+project (${PACKAGE_NAME} CXX)
+if (CMAKE_VERSION VERSION_LESS 100)
+ # C language still needed because the following required CMake modules
+ # (or their dependencies, respectively) are not correctly handling
+ # the case where only CXX is enabled.
+ # - CheckTypeSize.cmake (fixed in CMake 3.1, cf. http://www.cmake.org/Bug/view.php?id=14056)
+ # - FindThreads.cmake (--> CheckIncludeFiles.cmake <--)
+ enable_language (C)
+endif ()
+
+version_numbers (
+ ${PACKAGE_VERSION}
+ PACKAGE_VERSION_MAJOR
+ PACKAGE_VERSION_MINOR
+ PACKAGE_VERSION_PATCH
+)
+
+set (PACKAGE_SOVERSION "${PACKAGE_VERSION_MAJOR}")
+
+# ----------------------------------------------------------------------------
+# options
+if (NOT GFLAGS_NAMESPACE)
+ # maintain binary backwards compatibility with gflags library version <= 2.0,
+ # but at the same time enable the use of the preferred new "gflags" namespace
+ set (GFLAGS_NAMESPACE "google;${PACKAGE_NAME}" CACHE STRING "Name(s) of library namespace (separate multiple options by semicolon)")
+ mark_as_advanced (GFLAGS_NAMESPACE)
+endif ()
+set (GFLAGS_NAMESPACE_SECONDARY "${GFLAGS_NAMESPACE}")
+list (REMOVE_DUPLICATES GFLAGS_NAMESPACE_SECONDARY)
+if (NOT GFLAGS_NAMESPACE_SECONDARY)
+ message (FATAL_ERROR "GFLAGS_NAMESPACE must be set to one (or more) valid C++ namespace identifier(s separated by semicolon \";\").")
+endif ()
+foreach (ns IN LISTS GFLAGS_NAMESPACE_SECONDARY)
+ if (NOT ns MATCHES "^[a-zA-Z][a-zA-Z0-9_]*$")
+ message (FATAL_ERROR "GFLAGS_NAMESPACE contains invalid namespace identifier: ${ns}")
+ endif ()
+endforeach ()
+list (GET GFLAGS_NAMESPACE_SECONDARY 0 GFLAGS_NAMESPACE)
+list (REMOVE_AT GFLAGS_NAMESPACE_SECONDARY 0)
+
+option (BUILD_SHARED_LIBS "Request build of shared libraries." OFF)
+option (BUILD_STATIC_LIBS "Request build of static libraries (default if BUILD_SHARED_LIBS is OFF)." OFF)
+option (BUILD_gflags_LIB "Request build of the multi-threaded gflags library." ON)
+option (BUILD_gflags_nothreads_LIB "Request build of the single-threaded gflags library." ON)
+option (BUILD_PACKAGING "Enable build of distribution packages using CPack." OFF)
+option (BUILD_TESTING "Enable build of the unit tests and their execution using CTest." OFF)
+option (INSTALL_HEADERS "Request packaging of headers and other development files." ON)
+
+mark_as_advanced (CLEAR CMAKE_INSTALL_PREFIX)
+mark_as_advanced (CMAKE_CONFIGURATION_TYPES
+ BUILD_STATIC_LIBS
+ INSTALL_HEADERS)
+if (APPLE)
+ mark_as_advanced(CMAKE_OSX_ARCHITECTURES
+ CMAKE_OSX_DEPLOYMENT_TARGET
+ CMAKE_OSX_SYSROOT)
+endif ()
+
+if (NOT BUILD_SHARED_LIBS AND NOT BUILD_STATIC_LIBS)
+ set (BUILD_STATIC_LIBS ON)
+endif ()
+if (NOT BUILD_gflags_LIB AND NOT BUILD_gflags_nothreads_LIB)
+ message (FATAL_ERROR "At least one of BUILD_gflags_LIB and BUILD_gflags_nothreads_LIB must be ON.")
+endif ()
+
+if (NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CXX_FLAGS)
+ set_property (CACHE CMAKE_BUILD_TYPE PROPERTY VALUE Release)
+endif ()
+
+if (NOT GFLAGS_INCLUDE_DIR)
+ set (GFLAGS_INCLUDE_DIR "${PACKAGE_NAME}" CACHE STRING "Name of include directory of installed header files")
+ mark_as_advanced (GFLAGS_INCLUDE_DIR)
+else ()
+ if (IS_ABSOLUTE GFLAGS_INCLUDE_DIR)
+ message (FATAL_ERROR "GFLAGS_INCLUDE_DIR must be a path relative to CMAKE_INSTALL_PREFIX/include")
+ endif ()
+ if (GFLAGS_INCLUDE_DIR MATCHES "^\\.\\.[/\\]")
+ message (FATAL_ERROR "GFLAGS_INCLUDE_DIR must not start with parent directory reference (../)")
+ endif ()
+endif ()
+
+# ----------------------------------------------------------------------------
+# system checks
+include (CheckTypeSize)
+include (CheckIncludeFileCXX)
+include (CheckCXXSymbolExists)
+
+if (WIN32 AND NOT CYGWIN)
+ set (OS_WINDOWS 1)
+else ()
+ set (OS_WINDOWS 0)
+endif ()
+
+if (MSVC)
+ set (HAVE_SYS_TYPES_H 1)
+ set (HAVE_STDINT_H 1)
+ set (HAVE_STDDEF_H 1) # used by CheckTypeSize module
+ set (HAVE_INTTYPES_H 0)
+ set (HAVE_UNISTD_H 0)
+ set (HAVE_SYS_STAT_H 1)
+ set (HAVE_SHLWAPI_H 1)
+else ()
+ foreach (fname IN ITEMS unistd stdint inttypes sys/types sys/stat fnmatch)
+ string (TOUPPER "${fname}" FNAME)
+ string (REPLACE "/" "_" FNAME "${FNAME}")
+ if (NOT HAVE_${FNAME}_H)
+ check_include_file_cxx ("${fname}.h" HAVE_${FNAME}_H)
+ endif ()
+ endforeach ()
+ # the following are used in #if directives not #ifdef
+ bool_to_int (HAVE_STDINT_H)
+ bool_to_int (HAVE_SYS_TYPES_H)
+ bool_to_int (HAVE_INTTYPES_H)
+ if (NOT HAVE_FNMATCH_H AND OS_WINDOWS)
+ check_include_file_cxx ("shlwapi.h" HAVE_SHLWAPI_H)
+ endif ()
+endif ()
+
+set (GFLAGS_INTTYPES_FORMAT "" CACHE STRING "Format of integer types: \"C99\" (uint32_t), \"BSD\" (u_int32_t), \"VC7\" (__int32)")
+set_property (CACHE GFLAGS_INTTYPES_FORMAT PROPERTY STRINGS "C99;BSD;VC7")
+mark_as_advanced (GFLAGS_INTTYPES_FORMAT)
+if (NOT GFLAGS_INTTYPES_FORMAT)
+ set (TYPES uint32_t u_int32_t)
+ if (MSVC)
+ list (INSERT TYPES 0 __int32)
+ endif ()
+ foreach (type IN LISTS TYPES)
+ check_type_size (${type} ${type} LANGUAGE CXX)
+ if (HAVE_${type})
+ break ()
+ endif ()
+ endforeach ()
+ if (HAVE_uint32_t)
+ set_property (CACHE GFLAGS_INTTYPES_FORMAT PROPERTY VALUE C99)
+ elseif (HAVE_u_int32_t)
+ set_property (CACHE GFLAGS_INTTYPES_FORMAT PROPERTY VALUE BSD)
+ elseif (HAVE___int32)
+ set_property (CACHE GFLAGS_INTTYPES_FORMAT PROPERTY VALUE VC7)
+ else ()
+ mark_as_advanced (CLEAR GFLAGS_INTTYPES_FORMAT)
+ message (FATAL_ERROR "Do not know how to define a 32-bit integer quantity on your system!"
+ " Neither uint32_t, u_int32_t, nor __int32 seem to be available."
+ " Set GFLAGS_INTTYPES_FORMAT to either C99, BSD, or VC7 and try again.")
+ endif ()
+endif ()
+# use of special characters in strings to circumvent bug #0008226
+if ("^${GFLAGS_INTTYPES_FORMAT}$" STREQUAL "^WIN$")
+ set_property (CACHE GFLAGS_INTTYPES_FORMAT PROPERTY VALUE VC7)
+endif ()
+if (NOT GFLAGS_INTTYPES_FORMAT MATCHES "^(C99|BSD|VC7)$")
+ message (FATAL_ERROR "Invalid value for GFLAGS_INTTYPES_FORMAT! Choose one of \"C99\", \"BSD\", or \"VC7\"")
+endif ()
+set (GFLAGS_INTTYPES_FORMAT_C99 0)
+set (GFLAGS_INTTYPES_FORMAT_BSD 0)
+set (GFLAGS_INTTYPES_FORMAT_VC7 0)
+set ("GFLAGS_INTTYPES_FORMAT_${GFLAGS_INTTYPES_FORMAT}" 1)
+
+if (MSVC)
+ set (HAVE_strtoll 0)
+ set (HAVE_strtoq 0)
+else ()
+ check_cxx_symbol_exists (strtoll stdlib.h HAVE_STRTOLL)
+ if (NOT HAVE_STRTOLL)
+ check_cxx_symbol_exists (strtoq stdlib.h HAVE_STRTOQ)
+ endif ()
+endif ()
+
+set (CMAKE_THREAD_PREFER_PTHREAD TRUE)
+find_package (Threads)
+if (Threads_FOUND AND CMAKE_USE_PTHREADS_INIT)
+ set (HAVE_PTHREAD 1)
+ check_type_size (pthread_rwlock_t RWLOCK LANGUAGE CXX)
+else ()
+ set (HAVE_PTHREAD 0)
+endif ()
+
+if (UNIX AND NOT HAVE_PTHREAD AND BUILD_gflags_LIB)
+ if (CMAKE_HAVE_PTHREAD_H)
+ set (what "library")
+ else ()
+ set (what ".h file")
+ endif ()
+ message (FATAL_ERROR "Could not find pthread${what}. Check the log file"
+ "\n\t${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log"
+ "\nor disable the build of the multi-threaded gflags library (BUILD_gflags_LIB=OFF).")
+endif ()
+
+# ----------------------------------------------------------------------------
+# source files - excluding root subdirectory and/or .in suffix
+set (PUBLIC_HDRS
+ "gflags.h"
+ "gflags_declare.h"
+ "gflags_completions.h"
+)
+
+if (GFLAGS_NAMESPACE_SECONDARY)
+ set (INCLUDE_GFLAGS_NS_H "// Import gflags library symbols into alternative/deprecated namespace(s)")
+ foreach (ns IN LISTS GFLAGS_NAMESPACE_SECONDARY)
+ string (TOUPPER "${ns}" NS)
+ set (gflags_ns_h "${PROJECT_BINARY_DIR}/include/${GFLAGS_INCLUDE_DIR}/gflags_${ns}.h")
+ configure_file ("${PROJECT_SOURCE_DIR}/src/gflags_ns.h.in" "${gflags_ns_h}" @ONLY)
+ list (APPEND PUBLIC_HDRS "${gflags_ns_h}")
+ set (INCLUDE_GFLAGS_NS_H "${INCLUDE_GFLAGS_NS_H}\n#include \"gflags_${ns}.h\"")
+ endforeach ()
+else ()
+ set (INCLUDE_GFLAGS_NS_H)
+endif ()
+
+set (PRIVATE_HDRS
+ "config.h"
+ "util.h"
+ "mutex.h"
+)
+
+set (GFLAGS_SRCS
+ "gflags.cc"
+ "gflags_reporting.cc"
+ "gflags_completions.cc"
+)
+
+if (OS_WINDOWS)
+ list (APPEND PRIVATE_HDRS "windows_port.h")
+ list (APPEND GFLAGS_SRCS "windows_port.cc")
+endif ()
+
+# ----------------------------------------------------------------------------
+# configure source files
+if (CMAKE_COMPILER_IS_GNUCXX)
+ set (GFLAGS_ATTRIBUTE_UNUSED "__attribute((unused))")
+else ()
+ set (GFLAGS_ATTRIBUTE_UNUSED)
+endif ()
+
+# whenever we build a shared library (DLL on Windows), configure the public
+# headers of the API for use of this library rather than the optionally
+# also build statically linked library; users can override GFLAGS_DLL_DECL
+if (BUILD_SHARED_LIBS)
+ set (GFLAGS_IS_A_DLL 1)
+else ()
+ set (GFLAGS_IS_A_DLL 0)
+endif ()
+
+configure_headers (PUBLIC_HDRS ${PUBLIC_HDRS})
+configure_sources (PRIVATE_HDRS ${PRIVATE_HDRS})
+configure_sources (GFLAGS_SRCS ${GFLAGS_SRCS})
+
+# ----------------------------------------------------------------------------
+# output directories
+set (CMAKE_RUNTIME_OUTPUT_DIRECTORY "bin")
+set (CMAKE_LIBRARY_OUTPUT_DIRECTORY "lib")
+set (CMAKE_ARCHIVE_OUTPUT_DIRECTORY "lib")
+
+# ----------------------------------------------------------------------------
+# installation directories
+if (OS_WINDOWS)
+ set (RUNTIME_INSTALL_DIR Bin)
+ set (LIBRARY_INSTALL_DIR Lib)
+ set (INCLUDE_INSTALL_DIR Include)
+ set (CONFIG_INSTALL_DIR CMake)
+else ()
+ set (RUNTIME_INSTALL_DIR bin)
+ # The LIB_INSTALL_DIR and LIB_SUFFIX variables are used by the Fedora
+ # package maintainers. Also package maintainers of other distribution
+ # packages need to be able to specify the name of the library directory.
+ if (NOT LIB_INSTALL_DIR)
+ set (LIB_INSTALL_DIR "lib${LIB_SUFFIX}")
+ endif ()
+ set (LIBRARY_INSTALL_DIR "${LIB_INSTALL_DIR}"
+ CACHE PATH "Directory of installed libraries, e.g., \"lib64\""
+ )
+ mark_as_advanced (LIBRARY_INSTALL_DIR)
+ set (INCLUDE_INSTALL_DIR include)
+ set (CONFIG_INSTALL_DIR ${LIBRARY_INSTALL_DIR}/cmake/${PACKAGE_NAME})
+endif ()
+
+# ----------------------------------------------------------------------------
+# add library targets
+set (TARGETS)
+# static vs. shared
+foreach (TYPE IN ITEMS STATIC SHARED)
+ if (BUILD_${TYPE}_LIBS)
+ # whether or not targets are a DLL
+ if (OS_WINDOWS AND "^${TYPE}$" STREQUAL "^SHARED$")
+ set (GFLAGS_IS_A_DLL 1)
+ else ()
+ set (GFLAGS_IS_A_DLL 0)
+ endif ()
+ string (TOLOWER "${TYPE}" type)
+ # multi-threaded vs. single-threaded
+ foreach (opts IN ITEMS "" _nothreads)
+ if (BUILD_gflags${opts}_LIB)
+ add_library (gflags${opts}-${type} ${TYPE} ${GFLAGS_SRCS} ${PRIVATE_HDRS} ${PUBLIC_HDRS})
+ set (include_dirs "$<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/include>")
+ if (INSTALL_HEADERS)
+ list (APPEND include_dirs "$<INSTALL_INTERFACE:${INCLUDE_INSTALL_DIR}>")
+ endif ()
+ target_include_directories (gflags${opts}-${type}
+ PUBLIC "${include_dirs}"
+ PRIVATE "${PROJECT_SOURCE_DIR}/src;${PROJECT_BINARY_DIR}/include/${GFLAGS_INCLUDE_DIR}"
+ )
+ if (opts MATCHES "nothreads")
+ set (defines "GFLAGS_IS_A_DLL=${GFLAGS_IS_A_DLL};NOTHREADS")
+ else ()
+ set (defines "GFLAGS_IS_A_DLL=${GFLAGS_IS_A_DLL}")
+ if (CMAKE_USE_PTHREADS_INIT)
+ target_link_libraries (gflags${opts}-${type} ${CMAKE_THREAD_LIBS_INIT})
+ endif ()
+ endif ()
+ set_target_properties (
+ gflags${opts}-${type} PROPERTIES COMPILE_DEFINITIONS "${defines}"
+ OUTPUT_NAME "gflags${opts}"
+ VERSION "${PACKAGE_VERSION}"
+ SOVERSION "${PACKAGE_SOVERSION}"
+ )
+ if (HAVE_SHLWAPI_H)
+ target_link_libraries (gflags${opts}-${type} shlwapi.lib)
+ endif ()
+ if (NOT TARGET gflags${opts})
+ add_custom_target (gflags${opts})
+ endif ()
+ add_dependencies (gflags${opts} gflags${opts}-${type})
+ list (APPEND TARGETS gflags${opts}-${type})
+ endif ()
+ endforeach ()
+ endif ()
+endforeach ()
+
+# ----------------------------------------------------------------------------
+# installation rules
+file (RELATIVE_PATH INSTALL_PREFIX_REL2CONFIG_DIR "${CMAKE_INSTALL_PREFIX}/${CONFIG_INSTALL_DIR}" "${CMAKE_INSTALL_PREFIX}")
+configure_file (cmake/config.cmake.in "${PROJECT_BINARY_DIR}/${PACKAGE_NAME}-config-install.cmake" @ONLY)
+configure_file (cmake/version.cmake.in "${PROJECT_BINARY_DIR}/${PACKAGE_NAME}-config-version.cmake" @ONLY)
+
+install (TARGETS ${TARGETS} DESTINATION ${LIBRARY_INSTALL_DIR} EXPORT gflags-lib)
+if (INSTALL_HEADERS)
+ install (FILES ${PUBLIC_HDRS} DESTINATION ${INCLUDE_INSTALL_DIR}/${GFLAGS_INCLUDE_DIR})
+ install (
+ FILES "${PROJECT_BINARY_DIR}/${PACKAGE_NAME}-config-install.cmake"
+ RENAME ${PACKAGE_NAME}-config.cmake
+ DESTINATION ${CONFIG_INSTALL_DIR}
+ )
+ install (
+ FILES "${PROJECT_BINARY_DIR}/${PACKAGE_NAME}-config-version.cmake"
+ DESTINATION ${CONFIG_INSTALL_DIR}
+ )
+ install (EXPORT gflags-lib DESTINATION ${CONFIG_INSTALL_DIR} FILE ${PACKAGE_NAME}-export.cmake)
+ if (UNIX)
+ install (PROGRAMS src/gflags_completions.sh DESTINATION ${RUNTIME_INSTALL_DIR})
+ endif ()
+endif ()
+
+# ----------------------------------------------------------------------------
+# support direct use of build tree
+set (INSTALL_PREFIX_REL2CONFIG_DIR .)
+export (TARGETS ${TARGETS} FILE "${PROJECT_BINARY_DIR}/${PACKAGE_NAME}-export.cmake")
+export (PACKAGE gflags)
+configure_file (cmake/config.cmake.in "${PROJECT_BINARY_DIR}/${PACKAGE_NAME}-config.cmake" @ONLY)
+
+# ----------------------------------------------------------------------------
+# testing - MUST follow the generation of the build tree config file
+if (BUILD_TESTING)
+ include (CTest)
+ enable_testing ()
+ add_subdirectory (test)
+endif ()
+
+# ----------------------------------------------------------------------------
+# packaging
+if (BUILD_PACKAGING)
+
+ if (NOT BUILD_SHARED_LIBS AND NOT INSTALL_HEADERS)
+ message (WARNING "Package will contain static libraries without headers!"
+ "\nRecommended options for generation of runtime package:"
+ "\n BUILD_SHARED_LIBS=ON"
+ "\n BUILD_STATIC_LIBS=OFF"
+ "\n INSTALL_HEADERS=OFF"
+ "\nRecommended options for generation of development package:"
+ "\n BUILD_SHARED_LIBS=ON"
+ "\n BUILD_STATIC_LIBS=ON"
+ "\n INSTALL_HEADERS=ON")
+ endif ()
+
+ # default package generators
+ if (APPLE)
+ set (PACKAGE_GENERATOR "PackageMaker")
+ set (PACKAGE_SOURCE_GENERATOR "TGZ;ZIP")
+ elseif (UNIX)
+ set (PACKAGE_GENERATOR "DEB;RPM")
+ set (PACKAGE_SOURCE_GENERATOR "TGZ;ZIP")
+ else ()
+ set (PACKAGE_GENERATOR "ZIP")
+ set (PACKAGE_SOURCE_GENERATOR "ZIP")
+ endif ()
+
+ # used package generators
+ set (CPACK_GENERATOR "${PACKAGE_GENERATOR}" CACHE STRING "List of binary package generators (CPack).")
+ set (CPACK_SOURCE_GENERATOR "${PACKAGE_SOURCE_GENERATOR}" CACHE STRING "List of source package generators (CPack).")
+ mark_as_advanced (CPACK_GENERATOR CPACK_SOURCE_GENERATOR)
+
+ # some package generators (e.g., PackageMaker) do not allow .md extension
+ configure_file ("${CMAKE_CURRENT_LIST_DIR}/README.md" "${CMAKE_CURRENT_BINARY_DIR}/README.txt" COPYONLY)
+
+ # common package information
+ set (CPACK_PACKAGE_VENDOR "Andreas Schuh")
+ set (CPACK_PACKAGE_CONTACT "google-gflags@googlegroups.com")
+ set (CPACK_PACKAGE_NAME "${PACKAGE_NAME}")
+ set (CPACK_PACKAGE_VERSION "${PACKAGE_VERSION}")
+ set (CPACK_PACKAGE_VERSION_MAJOR "${PACKAGE_VERSION_MAJOR}")
+ set (CPACK_PACKAGE_VERSION_MINOR "${PACKAGE_VERSION_MINOR}")
+ set (CPACK_PACKAGE_VERSION_PATCH "${PACKAGE_VERSION_PATCH}")
+ set (CPACK_PACKAGE_DESCRIPTION_SUMMARY "A commandline flags library that allows for distributed flags.")
+ set (CPACK_RESOURCE_FILE_WELCOME "${CMAKE_CURRENT_BINARY_DIR}/README.txt")
+ set (CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_LIST_DIR}/COPYING.txt")
+ set (CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_CURRENT_BINARY_DIR}/README.txt")
+ set (CPACK_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}")
+ set (CPACK_OUTPUT_FILE_PREFIX packages)
+ set (CPACK_PACKAGE_RELOCATABLE TRUE)
+ set (CPACK_MONOLITHIC_INSTALL TRUE)
+
+ # RPM package information -- used in cmake/package.cmake.in also for DEB
+ set (CPACK_RPM_PACKAGE_GROUP "Development/Libraries")
+ set (CPACK_RPM_PACKAGE_LICENSE "BSD")
+ set (CPACK_RPM_PACKAGE_URL "http://schuhschuh.github.com/gflags")
+ set (CPACK_RPM_CHANGELOG_FILE "${CMAKE_CURRENT_LIST_DIR}/ChangeLog.txt")
+
+ if (INSTALL_HEADERS)
+ set (CPACK_RESOURCE_FILE_README "${CMAKE_CURRENT_LIST_DIR}/doc/index.html")
+ else ()
+ set (CPACK_RESOURCE_FILE_README "${CMAKE_CURRENT_LIST_DIR}/cmake/README_runtime.txt")
+ endif ()
+
+ # system/architecture
+ if (WINDOWS)
+ if (CMAKE_CL_64)
+ set (CPACK_SYSTEM_NAME "win64")
+ else ()
+ set (CPACK_SYSTEM_NAME "win32")
+ endif ()
+ set (CPACK_PACKAGE_ARCHITECTURE)
+ elseif (APPLE)
+ set (CPACK_PACKAGE_ARCHITECTURE darwin)
+ else ()
+ string (TOLOWER "${CMAKE_SYSTEM_NAME}" CPACK_SYSTEM_NAME)
+ if (CMAKE_CXX_FLAGS MATCHES "-m32")
+ set (CPACK_PACKAGE_ARCHITECTURE i386)
+ else ()
+ execute_process (
+ COMMAND dpkg --print-architecture
+ RESULT_VARIABLE RV
+ OUTPUT_VARIABLE CPACK_PACKAGE_ARCHITECTURE
+ )
+ if (RV EQUAL 0)
+ string (STRIP "${CPACK_PACKAGE_ARCHITECTURE}" CPACK_PACKAGE_ARCHITECTURE)
+ else ()
+ execute_process (COMMAND uname -m OUTPUT_VARIABLE CPACK_PACKAGE_ARCHITECTURE)
+ if (CPACK_PACKAGE_ARCHITECTURE MATCHES "x86_64")
+ set (CPACK_PACKAGE_ARCHITECTURE amd64)
+ else ()
+ set (CPACK_PACKAGE_ARCHITECTURE i386)
+ endif ()
+ endif ()
+ endif ()
+ endif ()
+
+ # source package settings
+ set (CPACK_SOURCE_TOPLEVEL_TAG "source")
+ set (CPACK_SOURCE_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}")
+ set (CPACK_SOURCE_IGNORE_FILES "/\\\\.git/;\\\\.swp$;\\\\.#;/#;\\\\.*~;cscope\\\\.*;/[Bb]uild[.+-_a-zA-Z0-9]*/")
+
+ # default binary package settings
+ set (CPACK_INCLUDE_TOPLEVEL_DIRECTORY TRUE)
+ set (CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}-${CPACK_SYSTEM_NAME}")
+ if (CPACK_PACKAGE_ARCHITECTURE)
+ set (CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_FILE_NAME}-${CPACK_PACKAGE_ARCHITECTURE}")
+ endif ()
+
+ # generator specific configuration file
+ #
+ # allow package maintainers to use their own configuration file
+ # $ cmake -DCPACK_PROJECT_CONFIG_FILE:FILE=/path/to/package/config
+ if (NOT CPACK_PROJECT_CONFIG_FILE)
+ configure_file (
+ "${CMAKE_CURRENT_LIST_DIR}/cmake/package.cmake.in"
+ "${PROJECT_BINARY_DIR}/${PACKAGE_NAME}-package.cmake" @ONLY
+ )
+ set (CPACK_PROJECT_CONFIG_FILE "${PROJECT_BINARY_DIR}/${PACKAGE_NAME}-package.cmake")
+ endif ()
+
+ include (CPack)
+
+endif () # BUILD_PACKAGING
diff --git a/third_party/gflags/COPYING.txt b/third_party/gflags/COPYING.txt
new file mode 100644
index 0000000..d15b0c2
--- /dev/null
+++ b/third_party/gflags/COPYING.txt
@@ -0,0 +1,28 @@
+Copyright (c) 2006, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/third_party/gflags/ChangeLog.txt b/third_party/gflags/ChangeLog.txt
new file mode 100644
index 0000000..eea9f83
--- /dev/null
+++ b/third_party/gflags/ChangeLog.txt
@@ -0,0 +1,218 @@
+* Tue Mar 24 2014 - Andreas Schuh <andreas.schuh.84@gmail.com>
+
+- gflags: version 2.1.2
+- Moved project to GitHub
+- Added GFLAGS_NAMESPACE definition to gflags_declare.h
+- Fixed issue 94: Keep "google" as primary namespace and import symbols into "gflags" namespace
+- Fixed issue 96: Fix binary ABI compatibility with gflags 2.0 using "google" as primary namespace
+- Fixed issue 97/101: Removed (patched) CMake modules and enabled C language instead
+- Fixed issue 103: Set CMake policy CMP0042 to silence warning regarding MACOS_RPATH setting
+
+* Sun Mar 20 2014 - Andreas Schuh <google-gflags@googlegroups.com>
+
+- gflags: version 2.1.1
+- Fixed issue 77: GFLAGS_IS_A_DLL expands to empty string in gflags_declare.h
+- Fixed issue 79: GFLAGS_NAMESPACE not expanded to actual namespace in gflags_declare.h
+- Fixed issue 80: Allow include path to differ from GFLAGS_NAMESPACE
+
+* Thu Mar 20 2014 - Andreas Schuh <google-gflags@googlegroups.com>
+
+- gflags: version 2.1.0
+- Build system configuration using CMake instead of autotools
+- CPack packaging support for Debian/Ubuntu, Red Hat, and Mac OS X
+- Fixed issue 54: Fix "invalid suffix on literal" (C++11)
+- Fixed issue 57: Use _strdup instead of strdup on Windows
+- Fixed issue 62: Change all preprocessor include guards to start with GFLAGS_
+- Fixed issue 64: Add DEFINE_validator macro
+- Fixed issue 73: Warnings in Visual Studio 2010 and unable to compile unit test
+
+* Wed Jan 25 2012 - Google Inc. <google-gflags@googlegroups.com>
+
+- gflags: version 2.0
+- Changed the 'official' gflags email in setup.py/etc
+- Renamed google-gflags.sln to gflags.sln
+- Changed copyright text to reflect Google's relinquished ownership
+
+* Tue Dec 20 2011 - Google Inc. <opensource@google.com>
+
+- google-gflags: version 1.7
+- Add CommandLineFlagInfo::flag_ptr pointing to current storage (musji)
+- PORTING: flush after writing to stderr, needed on cygwin
+- PORTING: Clean up the GFLAGS_DLL_DECL stuff better
+- Fix a bug in StringPrintf() that affected large strings (csilvers)
+- Die at configure-time when g++ isn't installed
+
+* Fri Jul 29 2011 - Google Inc. <opensource@google.com>
+
+- google-gflags: version 1.6
+- BUGFIX: Fix a bug where we were leaving out a required $(top_srcdir)
+- Fix definition of clstring (jyrki)
+- Split up flag declares into its own file (jyrki)
+- Add --version support (csilvers)
+- Update the README for gflags with static libs
+- Update acx_pthread.m4 for nostdlib
+- Change ReparseCommandLineFlags to return void (csilvers)
+- Some doc typofixes and example augmentation (various)
+
+* Mon Jan 24 2011 - Google Inc. <opensource@google.com>
+
+- google-gflags: version 1.5
+- Better reporting of current vs default value (handler)
+- Add API for cleaning up of memory at program-exit (jmarantz)
+- Fix macros to work inside namespaces (csilvers)
+- Use our own string typedef in case string is redefined (csilvers)
+- Updated to autoconf 2.65
+
+* Wed Oct 13 2010 - Google Inc. <opensource@google.com>
+
+- google-gflags: version 1.4
+- Add a check to prevent passing 0 to DEFINE_string (jorg)
+- Reduce compile (.o) size (jyrki)
+- Some small changes to quiet debug compiles (alexk)
+- PORTING: better support static linking on windows (csilvers)
+- DOCUMENTATION: change default values, use validators, etc.
+- Update the NEWS file to be non-empty
+- Add pkg-config (.pc) files for libgflags and libgflags_nothreads
+
+* Mon Jan 4 2010 - Google Inc. <opensource@google.com>
+
+- google-gflags: version 1.3
+- PORTABILITY: can now build and run tests under MSVC (csilvers)
+- Remove the python gflags code, which is now its own package (tansell)
+- Clarify that "last flag wins" in the docs (csilvers)
+- Comment danger of using GetAllFlags in validators (wojtekm)
+- PORTABILITY: Some fixes necessary for c++0x (mboerger)
+- Makefile fix: $(srcdir) -> $(top_srcdir) in one place (csilvres)
+- INSTALL: autotools to autoconf v2.64 + automake v1.11 (csilvers)
+
+* Thu Sep 10 2009 - Google Inc. <opensource@google.com>
+
+- google-gflags: version 1.2
+- PORTABILITY: can now build and run tests under mingw (csilvers)
+- Using a string arg for a bool flag is a compile-time error (rbayardo)
+- Add --helpxml to gflags.py (salcianu)
+- Protect against a hypothetical global d'tor mutex problem (csilvers)
+- BUGFIX: can now define a flag after 'using namespace google' (hamaji)
+
+* Tue Apr 14 2009 - Google Inc. <opensource@google.com>
+
+- google-gflags: version 1.1
+- Add both foo and nofoo for boolean flags, with --undefok (andychu)
+- Better document how validators work (wojtekm)
+- Improve binary-detection for bash-completion (mtamsky)
+- Python: Add a concept of "key flags", used with --help (salcianu)
+- Python: Robustify flag_values (salcianu)
+- Python: Add a new DEFINE_bool alias (keir, andrewliu)
+- Python: Do module introspection based on module name (dsturtevant)
+- Fix autoconf a bit better, especially on windows and solaris (ajenjo)
+- BUG FIX: gflags_nothreads was linking against the wrong lib (ajenjo)
+- BUG FIX: threads-detection failed on FreeBSD; replace it (ajenjo)
+- PORTABILITY: Quiet an internal compiler error with SUSE 10 (csilvers)
+- PORTABILITY: Update deb.sh for more recenty debuilds (csilvers)
+- PORTABILITY: #include more headers to satify new gcc's (csilvers)
+- INSTALL: Updated to autoconf 2.61 and libtool 1.5.26 (csilvers)
+
+* Fri Oct 3 2008 - Google Inc. <opensource@google.com>
+
+- google-gflags: version 1.0
+- Add a missing newline to an error string (bcmills)
+- (otherwise exactly the same as gflags 1.0rc2)
+
+* Thu Sep 18 2008 - Google Inc. <opensource@google.com>
+
+- google-gflags: version 1.0rc2
+- Report current flag values in --helpxml (hdn)
+- Fix compilation troubles with gcc 4.3.3 (simonb)
+- BUG FIX: I was missing a std:: in DECLARE_string (csilvers)
+- BUG FIX: Clarify in docs how to specify --bool flags (csilvers)
+- BUG FIX: Fix --helpshort for source files not in a subdir (csilvers)
+- BUG FIX: Fix python unittest for 64-bit builds (bcmills)
+
+* Tue Aug 19 2008 - Google Inc. <opensource@google.com>
+
+- google-gflags: version 1.0rc1
+- Move #include files from google/ to gflags/ (csilvers)
+- Small optimizations to reduce binary (library) size (jyrki)
+- BUGFIX: forgot a std:: in one of the .h files (csilvers)
+- Speed up locking by making sure calls are inlined (ajenjo)
+- 64-BIT COMPATIBILITY: Use %PRId64 instead of %lld (csilvers)
+- PORTABILITY: fix Makefile to work with Cygwin (ajenjo)
+- PORTABILITY: fix code to compile under Visual Studio (ajenjo)
+- PORTABILITY: fix code to compile under Solaris 10 with CC (csilvers)
+
+* Mon Jul 21 2008 - Google Inc. <opensource@google.com>
+
+- google-gflags: version 0.9
+- Add the ability to validate a command-line flag (csilvers)
+- Add completion support for commandline flags in bash (daven)
+- Add -W compile flags to Makefile, when using gcc (csilvers)
+- Allow helpstring to be NULL (cristianoc)
+- Improved documentation of classes in the .cc file (csilvers)
+- Fix python bug with AppendFlagValues + shortnames (jjtswan)
+- Use bool instead of int for boolean flags in gflags.py (bcmills)
+- Simplify the way we declare flags, now more foolproof (csilvers)
+- Better error messages when bool flags collide (colohan)
+- Only evaluate DEFINE_foo macro args once (csilvers)
+
+* Wed Mar 26 2008 - Google Inc. <opensource@google.com>
+
+- google-gflags: version 0.8
+- Export DescribeOneFlag() in the API
+- Add support for automatic line wrapping at 80 cols for gflags.py
+- Bugfix: do not treat an isolated "-" the same as an isolated "--"
+- Update rpm spec to point to Google Code rather than sourceforge (!)
+- Improve documentation (including documenting thread-safety)
+- Improve #include hygiene
+- Improve testing
+
+* Thu Oct 18 2007 - Google Inc. <opensource@google.com>
+
+- google-gflags: version 0.7
+- Deal even more correctly with libpthread not linked in (csilvers)
+- Add STRIP_LOG, an improved DO_NOT_SHOW_COMMANDLINE_HELP (sioffe)
+- Be more accurate printing default flag values in --help (dsturtevant)
+- Reduce .o file size a bit by using shorter namespace names (jeff)
+- Use relative install path, so 'setup.py --home' works (csilvers)
+- Notice when a boolean flag has a non-boolean default (bnmouli)
+- Broaden --helpshort to match foo-main.cc and foo_main.cc (hendrie)
+- Fix "no modules match" message for --helpshort, etc (hendrie)
+
+* Wed Aug 15 2007 - Google Inc. <opensource@google.com>
+
+- google-gflags: version 0.6
+- Deal correctly with case that libpthread is not linked in (csilvers)
+- Update Makefile/tests so we pass "make distcheck" (csilvers)
+- Document and test that last assignment to a flag wins (wan)
+
+* Tue Jun 12 2007 - Google Inc. <opensource@google.com>
+
+- google-gflags: version 0.5
+- Include all m4 macros in the distribution (csilvers)
+- Python: Fix broken data_files field in setup.py (sidlon)
+- Python: better string serliaizing and unparsing (abo, csimmons)
+- Fix checks for NaN and inf to work with Mac OS X (csilvers)
+
+* Thu Apr 19 2007 - Google Inc. <opensource@google.com>
+
+- google-gflags: version 0.4
+- Remove is_default from GetCommandLineFlagInfo (csilvers)
+- Portability fixes: includes, strtoll, gcc4.3 errors (csilvers)
+- A few doc typo cleanups (csilvers)
+
+* Wed Mar 28 2007 - Google Inc. <opensource@google.com>
+
+- google-gflags: version 0.3
+- python portability fix: use popen instead of subprocess (csilvers)
+- Add is_default to CommandLineFlagInfo (pchien)
+- Make docs a bit prettier (csilvers)
+- Actually include the python files in the distribution! :-/ (csilvers)
+
+* Mon Jan 22 2007 - Google Inc. <opensource@google.com>
+
+- google-gflags: version 0.2
+- added support for python commandlineflags, as well as c++
+- gflags2man, a script to turn flags into a man page (dchristian)
+
+* Wed Dec 13 2006 - Google Inc. <opensource@google.com>
+
+- google-gflags: version 0.1
diff --git a/third_party/gflags/INSTALL.md b/third_party/gflags/INSTALL.md
new file mode 100644
index 0000000..b89d86d
--- /dev/null
+++ b/third_party/gflags/INSTALL.md
@@ -0,0 +1,67 @@
+Installing a binary distribution package
+========================================
+
+No official binary distribution packages are provided by the gflags developers.
+There may, however, be binary packages available for your OS. Please consult
+also the package repositories of your Linux distribution.
+
+For example on Debian/Ubuntu Linux, gflags can be installed using the
+following command:
+
+ sudo apt-get install gflags
+
+
+Compiling the source code
+=========================
+
+The build system of gflags is since version 2.1 based on [CMake](http://cmake.org).
+The common steps to build, test, and install software are therefore:
+
+1. Extract source files.
+2. Create build directory and change to it.
+3. Run CMake to configure the build tree.
+4. Build the software using selected build tool.
+5. Test the built software.
+6. Install the built files.
+
+On Unix-like systems with GNU Make as build tool, these build steps can be
+summarized by the following sequence of commands executed in a shell,
+where ```$package``` and ```$version``` are shell variables which represent
+the name of this package and the obtained version of the software.
+
+ $ tar xzf gflags-$version-source.tar.gz
+ $ cd gflags-$version
+ $ mkdir build && cd build
+ $ ccmake ..
+
+ - Press 'c' to configure the build system and 'e' to ignore warnings.
+ - Set CMAKE_INSTALL_PREFIX and other CMake variables and options.
+ - Continue pressing 'c' until the option 'g' is available.
+ - Then press 'g' to generate the configuration files for GNU Make.
+
+ $ make
+ $ make test (optional)
+ $ make install (optional)
+
+In the following, only gflags-specific CMake settings available to
+configure the build and installation are documented. Note that most of these
+variables are for advanced users and binary package maintainers only.
+They usually do not have to be modified.
+
+
+CMake Option | Description
+--------------------------- | -------------------------------------------------------
+CMAKE_INSTALL_PREFIX | Installation directory, e.g., "/usr/local" on Unix and "C:\Program Files\gflags" on Windows.
+BUILD_SHARED_LIBS | Request build of dynamic link libraries.
+BUILD_STATIC_LIBS | Request build of static link libraries. Implied if BUILD_SHARED_LIBS is OFF.
+BUILD_PACKAGING | Enable binary package generation using CPack.
+BUILD_TESTING | Build tests for execution by CTest.
+BUILD_NC_TESTS | Request inclusion of negative compilation tests (requires Python).
+BUILD_CONFIG_TESTS | Request inclusion of package configuration tests (requires Python).
+BUILD_gflags_LIBS | Request build of multi-threaded gflags libraries (if threading library found).
+BUILD_gflags_nothreads_LIBS | Request build of single-threaded gflags libraries.
+GFLAGS_NAMESPACE | Name of the C++ namespace to be used by the gflags library. Note that the public source header files are installed in a subdirectory named after this namespace. To maintain backwards compatibility with the Google Commandline Flags, set this variable to "google". The default is "gflags".
+GFLAGS_INTTYPES_FORMAT | String identifying format of built-in integer types.
+GFLAGS_INCLUDE_DIR | Name of headers installation directory relative to CMAKE_INSTALL_PREFIX.
+LIBRARY_INSTALL_DIR | Name of library installation directory relative to CMAKE_INSTALL_PREFIX.
+INSTALL_HEADERS | Request installation of public header files.
diff --git a/third_party/gflags/README.md b/third_party/gflags/README.md
new file mode 100644
index 0000000..79bd202
--- /dev/null
+++ b/third_party/gflags/README.md
@@ -0,0 +1,270 @@
+24 March 2015
+-------------
+
+I've just released gflags 2.1.2.
+
+This release completes the namespace change fixes. In particular,
+it restores binary ABI compatibility with release version 2.0.
+The deprecated "google" namespace is by default still kept as
+primary namespace while symbols are imported into the new "gflags" namespace.
+This can be overridden using the CMake variable GFLAGS_NAMESPACE.
+
+Other fixes of the build configuration are related to the (patched)
+CMake modules FindThreads.cmake and CheckTypeSize.cmake. These have
+been removed and instead the C language is enabled again even though
+gflags is written in C++ only.
+
+This release also marks the complete move of the gflags project
+from Google Code to GitHub. Email addresses of original issue
+reporters got lost in the process. Given the age of most issue reports,
+this should be negligable.
+
+Please report any further issues using the GitHub issue tracker.
+
+
+30 March 2014
+-------------
+
+I've just released gflags 2.1.1.
+
+This release fixes a few bugs in the configuration of gflags\_declare.h
+and adds a separate GFLAGS\_INCLUDE\_DIR CMake variable to the build configuration.
+Setting GFLAGS\_NAMESPACE to "google" no longer changes also the include
+path of the public header files. This allows the use of the library with
+other Google projects such as glog which still use the deprecated "google"
+namespace for the gflags library, but include it as "gflags/gflags.h".
+
+20 March 2014
+-------------
+
+I've just released gflags 2.1.
+
+The major changes are the use of CMake for the build configuration instead
+of the autotools and packaging support through CPack. The default namespace
+of all C++ symbols is now "gflags" instead of "google". This can be
+configured via the GFLAGS\_NAMESPACE variable.
+
+This release compiles with all major compilers without warnings and passed
+the unit tests on Ubuntu 12.04, Windows 7 (Visual Studio 2008 and 2010,
+Cygwin, MinGW), and Mac OS X (Xcode 5.1).
+
+The SVN repository on Google Code is now frozen and replaced by a Git
+repository such that it can be used as Git submodule by projects. The main
+hosting of this project remains at Google Code. Thanks to the distributed
+character of Git, I can push (and pull) changes from both GitHub and Google Code
+in order to keep the two public repositories in sync.
+When fixing an issue for a pull request through either of these hosting
+platforms, please reference the issue number as
+[described here](https://code.google.com/p/support/wiki/IssueTracker#Integration_with_version_control).
+For the further development, I am following the
+[Git branching model](http://nvie.com/posts/a-successful-git-branching-model/)
+with feature branch names prefixed by "feature/" and bugfix branch names
+prefixed by "bugfix/", respectively.
+
+Binary and source [packages](https://github.com/schuhschuh/gflags/releases) are available on GitHub.
+
+
+14 January 2014
+---------------
+
+The migration of the build system to CMake is almost complete.
+What remains to be done is rewriting the tests in Python such they can be
+executed on non-Unix platforms and splitting them up into separate CTest tests.
+Though merging these changes into the master branch yet remains to be done,
+it is recommended to already start using the
+[cmake-migration](https://github.com/schuhschuh/gflags/tree/cmake-migration) branch.
+
+
+20 April 2013
+-------------
+
+More than a year has past since I (Andreas) took over the maintenance for
+`gflags`. Only few minor changes have been made since then, much to my regret.
+To get more involved and stimulate participation in the further
+development of the library, I moved the project source code today to
+[GitHub](https://github.com/schuhschuh/gflags).
+I believe that the strengths of [Git](http://git-scm.com/) will allow for better community collaboration
+as well as ease the integration of changes made by others. I encourage everyone
+who would like to contribute to send me pull requests.
+Git's lightweight feature branches will also provide the right tool for more
+radical changes which should only be merged back into the master branch
+after these are complete and implement the desired behavior.
+
+The SVN repository remains accessible at Google Code and I will keep the
+master branch of the Git repository hosted at GitHub and the trunk of the
+Subversion repository synchronized. Initially, I was going to simply switch the
+Google Code project to Git, but in this case the SVN repository would be
+frozen and force everyone who would like the latest development changes to
+use Git as well. Therefore I decided to host the public Git repository at GitHub
+instead.
+
+Please continue to report any issues with gflags on Google Code. The GitHub project will
+only be used to host the Git repository.
+
+One major change of the project structure I have in mind for the next weeks
+is the migration from autotools to [CMake](http://www.cmake.org/).
+Check out the (unstable!)
+[cmake-migration](https://github.com/schuhschuh/gflags/tree/cmake-migration)
+branch on GitHub for details.
+
+
+25 January 2012
+---------------
+
+I've just released gflags 2.0.
+
+The `google-gflags` project has been renamed to `gflags`. I
+(csilvers) am stepping down as maintainer, to be replaced by Andreas
+Schuh. Welcome to the team, Andreas! I've seen the energy you have
+around gflags and the ideas you have for the project going forward,
+and look forward to having you on the team.
+
+I bumped the major version number up to 2 to reflect the new community
+ownership of the project. All the [changes](ChangeLog.txt)
+are related to the renaming. There are no functional changes from
+gflags 1.7. In particular, I've kept the code in the namespace
+`google`, though in a future version it should be renamed to `gflags`.
+I've also kept the `/usr/local/include/google/` subdirectory as
+synonym of `/usr/local/include/gflags/`, though the former name has
+been obsolete for some time now.
+
+
+18 January 2011
+---------------
+
+The `google-gflags` Google Code page has been renamed to
+`gflags`, in preparation for the project being renamed to
+`gflags`. In the coming weeks, I'll be stepping down as
+maintainer for the gflags project, and as part of that Google is
+relinquishing ownership of the project; it will now be entirely
+community run. The name change reflects that shift.
+
+
+20 December 2011
+----------------
+
+I've just released gflags 1.7. This is a minor release; the major
+change is that `CommandLineFlagInfo` now exports the address in memory
+where the flag is located. There has also been a bugfix involving
+very long --help strings, and some other minor [changes](ChangeLog.txt).
+
+29 July 2011
+------------
+
+I've just released gflags 1.6. The major new feature in this release
+is support for setting version info, so that --version does something
+useful.
+
+One minor change has required bumping the library number:
+`ReparseCommandlineFlags` now returns `void` instead of `int` (the int
+return value was always meaningless). Though I doubt anyone ever used
+this (meaningless) return value, technically it's a change to the ABI
+that requires a version bump. A bit sad.
+
+There's also a procedural change with this release: I've changed the
+internal tools used to integrate Google-supplied patches for gflags
+into the opensource release. These new tools should result in more
+frequent updates with better change descriptions. They will also
+result in future `ChangeLog` entries being much more verbose (for better
+or for worse).
+
+See the [ChangeLog](ChangeLog.txt) for a full list of changes for this release.
+
+24 January 2011
+---------------
+
+I've just released gflags 1.5. This release has only minor changes
+from 1.4, including some slightly better reporting in --help, and
+an new memory-cleanup function that can help when running gflags-using
+libraries under valgrind. The major change is to fix up the macros
+(`DEFINE_bool` and the like) to work more reliably inside namespaces.
+
+If you have not had a problem with these macros, and don't need any of
+the other changes described, there is no need to upgrade. See the
+[ChangeLog](ChangeLog.txt) for a full list of changes for this release.
+
+11 October 2010
+---------------
+
+I've just released gflags 1.4. This release has only minor changes
+from 1.3, including some documentation tweaks and some work to make
+the library smaller. If 1.3 is working well for you, there's no
+particular reason to upgrade.
+
+4 January 2010
+--------------
+
+I've just released gflags 1.3. gflags now compiles under MSVC, and
+all tests pass. I **really** never thought non-unix-y Windows folks
+would want gflags, but at least some of them do.
+
+The major news, though, is that I've separated out the python package
+into its own library, [python-gflags](http://code.google.com/p/python-gflags).
+If you're interested in the Python version of gflags, that's the place to
+get it now.
+
+10 September 2009
+-----------------
+
+I've just released gflags 1.2. The major change from gflags 1.1 is it
+now compiles under MinGW (as well as cygwin), and all tests pass. I
+never thought Windows folks would want unix-style command-line flags,
+since they're so different from the Windows style, but I guess I was
+wrong!
+
+The other changes are minor, such as support for --htmlxml in the
+python version of gflags.
+
+15 April 2009
+-------------
+
+I've just released gflags 1.1. It has only minor changes fdrom gflags
+1.0 (see the [ChangeLog](ChangeLog.txt) for details).
+The major change is that I moved to a new system for creating .deb and .rpm files.
+This allows me to create x86\_64 deb and rpm files.
+
+In the process of moving to this new system, I noticed an
+inconsistency: the tar.gz and .rpm files created libraries named
+libgflags.so, but the deb file created libgoogle-gflags.so. I have
+fixed the deb file to create libraries like the others. I'm no expert
+in debian packaging, but I believe this has caused the package name to
+change as well. Please let me know (at
+[[mailto:google-gflags@googlegroups.com](mailto:google-gflags@googlegroups.com)
+google-gflags@googlegroups.com]) if this causes problems for you --
+especially if you know of a fix! I would be happy to change the deb
+packages to add symlinks from the old library name to the new
+(libgoogle-gflags.so -> libgflags.so), but that is beyond my knowledge
+of how to make .debs.
+
+If you've tried to install a .rpm or .deb and it doesn't work for you,
+let me know. I'm excited to finally have 64-bit package files, but
+there may still be some wrinkles in the new system to iron out.
+
+1 October 2008
+--------------
+
+gflags 1.0rc2 was out for a few weeks without any issues, so gflags
+1.0 is now released. This is much like gflags 0.9. The major change
+is that the .h files have been moved from `/usr/include/google` to
+`/usr/include/gflags`. While I have backwards-compatibility
+forwarding headeds in place, please rewrite existing code to say
+```
+ #include <gflags/gflags.h>
+```
+instead of
+```
+ #include <google/gflags.h>
+```
+
+I've kept the default namespace to google. You can still change with
+with the appropriate flag to the configure script (`./configure
+--help` to see the flags). If you have feedback as to whether the
+default namespace should change to gflags, which would be a
+non-backwards-compatible change, send mail to
+`google-gflags@googlegroups.com`!
+
+Version 1.0 also has some neat new features, like support for bash
+commandline-completion of help flags. See the [ChangeLog](ChangeLog.txt)
+for more details.
+
+If I don't hear any bad news for a few weeks, I'll release 1.0-final.
diff --git a/third_party/gflags/cmake/README_runtime.txt b/third_party/gflags/cmake/README_runtime.txt
new file mode 100644
index 0000000..d2556b2
--- /dev/null
+++ b/third_party/gflags/cmake/README_runtime.txt
@@ -0,0 +1,4 @@
+This package contains runtime libraries only which are required
+by applications that use these libraries for the commandline flags
+processing. If you want to develop such application, download
+and install the development package instead.
diff --git a/third_party/gflags/cmake/config.cmake.in b/third_party/gflags/cmake/config.cmake.in
new file mode 100644
index 0000000..a7419c2
--- /dev/null
+++ b/third_party/gflags/cmake/config.cmake.in
@@ -0,0 +1,111 @@
+## gflags CMake configuration file
+
+# library version information
+set (@PACKAGE_NAME@_VERSION_STRING "@PACKAGE_VERSION@")
+set (@PACKAGE_NAME@_VERSION_MAJOR @PACKAGE_VERSION_MAJOR@)
+set (@PACKAGE_NAME@_VERSION_MINOR @PACKAGE_VERSION_MINOR@)
+set (@PACKAGE_NAME@_VERSION_PATCH @PACKAGE_VERSION_PATCH@)
+
+# import targets
+include ("${CMAKE_CURRENT_LIST_DIR}/@PACKAGE_NAME@-export.cmake")
+
+# installation prefix
+get_filename_component (CMAKE_CURRENT_LIST_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH)
+get_filename_component (_INSTALL_PREFIX "${CMAKE_CURRENT_LIST_DIR}/@INSTALL_PREFIX_REL2CONFIG_DIR@" ABSOLUTE)
+
+# include directory
+#
+# Newer versions of CMake set the INTERFACE_INCLUDE_DIRECTORIES property
+# of the imported targets. It is hence not necessary to add this path
+# manually to the include search path for targets which link to gflags.
+set (@PACKAGE_NAME@_INCLUDE_DIR "${_INSTALL_PREFIX}/@INCLUDE_INSTALL_DIR@")
+
+# default settings
+if (NOT DEFINED @PACKAGE_NAME@_SHARED)
+ if (TARGET @PACKAGE_NAME@-static OR TARGET @PACKAGE_NAME@_nothreads-static)
+ set (@PACKAGE_NAME@_SHARED FALSE)
+ else ()
+ set (@PACKAGE_NAME@_SHARED TRUE)
+ endif ()
+endif ()
+if (NOT DEFINED @PACKAGE_NAME@_NOTHREADS)
+ if (TARGET @PACKAGE_NAME@-static OR TARGET @PACKAGE_NAME@-shared)
+ set (@PACKAGE_NAME@_NOTHREADS FALSE)
+ else ()
+ set (@PACKAGE_NAME@_NOTHREADS TRUE)
+ endif ()
+endif ()
+
+# choose imported library target
+if (NOT @PACKAGE_NAME@_TARGET)
+ if (@PACKAGE_NAME@_SHARED)
+ if (@PACKAGE_NAME@_NOTHREADS)
+ set (@PACKAGE_NAME@_TARGET @PACKAGE_NAME@_nothreads-shared)
+ else ()
+ set (@PACKAGE_NAME@_TARGET @PACKAGE_NAME@-shared)
+ endif ()
+ else ()
+ if (@PACKAGE_NAME@_NOTHREADS)
+ set (@PACKAGE_NAME@_TARGET @PACKAGE_NAME@_nothreads-static)
+ else ()
+ set (@PACKAGE_NAME@_TARGET @PACKAGE_NAME@-static)
+ endif ()
+ endif ()
+endif ()
+if (NOT TARGET ${@PACKAGE_NAME@_TARGET})
+ message (FATAL_ERROR "Your @PACKAGE_NAME@ installation does not contain a ${@PACKAGE_NAME@_TARGET} library target!"
+ " Try a different combination of @PACKAGE_NAME@_SHARED and @PACKAGE_NAME@_NOTHREADS.")
+endif ()
+
+# add more convenient "@PACKAGE_NAME@" import target
+if (NOT TARGET @PACKAGE_NAME@)
+ if (@PACKAGE_NAME@_SHARED)
+ add_library (@PACKAGE_NAME@ SHARED IMPORTED)
+ else ()
+ add_library (@PACKAGE_NAME@ STATIC IMPORTED)
+ endif ()
+ # INTERFACE_INCLUDE_DIRECTORIES
+ get_target_property (_@PACKAGE_NAME@_INCLUDES ${@PACKAGE_NAME@_TARGET} INTERFACE_INCLUDE_DIRECTORIES)
+ if (_@PACKAGE_NAME@_INCLUDES)
+ set_target_properties(@PACKAGE_NAME@ PROPERTIES
+ INTERFACE_INCLUDE_DIRECTORIES "${_@PACKAGE_NAME@_INCLUDES}"
+ )
+ endif ()
+ unset (_@PACKAGE_NAME@_INCLUDES)
+ # set configuration specific properties
+ get_target_property (_@PACKAGE_NAME@_CONFIGURATIONS ${@PACKAGE_NAME@_TARGET} IMPORTED_CONFIGURATIONS)
+ set_target_properties (@PACKAGE_NAME@ PROPERTIES IMPORTED_CONFIGURATIONS "${_@PACKAGE_NAME@_CONFIGURATIONS}")
+ foreach (_@PACKAGE_NAME@_CONFIG IN LISTS _@PACKAGE_NAME@_CONFIGURATIONS)
+ # IMPORTED_LOCATION_<config>
+ get_target_property (_@PACKAGE_NAME@_LOCATION ${@PACKAGE_NAME@_TARGET} IMPORTED_LOCATION_${_@PACKAGE_NAME@_CONFIG})
+ if (_@PACKAGE_NAME@_LOCATION)
+ set_target_properties(@PACKAGE_NAME@ PROPERTIES
+ IMPORTED_LOCATION_${_@PACKAGE_NAME@_CONFIG} "${_@PACKAGE_NAME@_LOCATION}"
+ )
+ endif ()
+ unset (_@PACKAGE_NAME@_LOCATION)
+ # IMPORTED_LINK_INTERFACE_LANGUAGES_<config> (static)
+ get_target_property (_@PACKAGE_NAME@_LANGUAGES ${@PACKAGE_NAME@_TARGET} IMPORTED_LINK_INTERFACE_LANGUAGES_${_@PACKAGE_NAME@_CONFIG})
+ if (_@PACKAGE_NAME@_LANGUAGES)
+ set_target_properties(@PACKAGE_NAME@ PROPERTIES
+ IMPORTED_LINK_INTERFACE_LANGUAGES_${_@PACKAGE_NAME@_CONFIG} "${_@PACKAGE_NAME@_LANGUAGES}"
+ )
+ endif ()
+ unset (_@PACKAGE_NAME@_LANGUAGES)
+ # IMPORTED_SONAME_<config> (shared)
+ get_target_property (_@PACKAGE_NAME@_SONAME ${@PACKAGE_NAME@_TARGET} IMPORTED_SONAME_${_@PACKAGE_NAME@_CONFIG})
+ if (_@PACKAGE_NAME@_SONAME)
+ set_target_properties(@PACKAGE_NAME@ PROPERTIES
+ IMPORTED_SONAME_${_@PACKAGE_NAME@_CONFIG} "${_@PACKAGE_NAME@_SONAME}"
+ )
+ endif ()
+ unset (_@PACKAGE_NAME@_SONAME)
+ endforeach ()
+ unset (_@PACKAGE_NAME@_CONFIGURATIONS)
+endif ()
+
+# alias for default import target to be compatible with older CMake package configurations
+set (@PACKAGE_NAME@_LIBRARIES "${@PACKAGE_NAME@_TARGET}")
+
+# unset private variables
+unset (_INSTALL_PREFIX)
diff --git a/third_party/gflags/cmake/execute_test.cmake b/third_party/gflags/cmake/execute_test.cmake
new file mode 100644
index 0000000..df008cf
--- /dev/null
+++ b/third_party/gflags/cmake/execute_test.cmake
@@ -0,0 +1,53 @@
+# ----------------------------------------------------------------------------
+# sanitize string stored in variable for use in regular expression.
+macro (sanitize_for_regex STRVAR)
+ string (REGEX REPLACE "([.+*?^$()])" "\\\\\\1" ${STRVAR} "${${STRVAR}}")
+endmacro ()
+
+# ----------------------------------------------------------------------------
+# script arguments
+if (NOT COMMAND)
+ message (FATAL_ERROR "Test command not specified!")
+endif ()
+if (NOT DEFINED EXPECTED_RC)
+ set (EXPECTED_RC 0)
+endif ()
+if (EXPECTED_OUTPUT)
+ sanitize_for_regex(EXPECTED_OUTPUT)
+endif ()
+if (UNEXPECTED_OUTPUT)
+ sanitize_for_regex(UNEXPECTED_OUTPUT)
+endif ()
+
+# ----------------------------------------------------------------------------
+# set a few environment variables (useful for --tryfromenv)
+set (ENV{FLAGS_undefok} "foo,bar")
+set (ENV{FLAGS_weirdo} "")
+set (ENV{FLAGS_version} "true")
+set (ENV{FLAGS_help} "false")
+
+# ----------------------------------------------------------------------------
+# execute test command
+execute_process(
+ COMMAND ${COMMAND}
+ RESULT_VARIABLE RC
+ OUTPUT_VARIABLE OUTPUT
+ ERROR_VARIABLE OUTPUT
+)
+
+if (OUTPUT)
+ message ("${OUTPUT}")
+endif ()
+
+# ----------------------------------------------------------------------------
+# check test result
+if (NOT RC EQUAL EXPECTED_RC)
+ string (REPLACE ";" " " COMMAND "${COMMAND}")
+ message (FATAL_ERROR "Command:\n\t${COMMAND}\nExit status is ${RC}, expected ${EXPECTED_RC}")
+endif ()
+if (EXPECTED_OUTPUT AND NOT OUTPUT MATCHES "${EXPECTED_OUTPUT}")
+ message (FATAL_ERROR "Test output does not match expected output: ${EXPECTED_OUTPUT}")
+endif ()
+if (UNEXPECTED_OUTPUT AND OUTPUT MATCHES "${UNEXPECTED_OUTPUT}")
+ message (FATAL_ERROR "Test output matches unexpected output: ${UNEXPECTED_OUTPUT}")
+endif ()
\ No newline at end of file
diff --git a/third_party/gflags/cmake/package.cmake.in b/third_party/gflags/cmake/package.cmake.in
new file mode 100644
index 0000000..aaec792
--- /dev/null
+++ b/third_party/gflags/cmake/package.cmake.in
@@ -0,0 +1,49 @@
+# Per-generator CPack configuration file. See CPACK_PROJECT_CONFIG_FILE documented at
+# http://www.cmake.org/cmake/help/v2.8.12/cpack.html#variable:CPACK_PROJECT_CONFIG_FILE
+#
+# All common CPACK_* variables are set in CMakeLists.txt already. This file only
+# overrides some of these to provide package generator specific settings.
+
+# whether package contains all development files or only runtime files
+set (DEVEL @INSTALL_HEADERS@)
+
+# ------------------------------------------------------------------------------
+# Mac OS X package
+if (CPACK_GENERATOR MATCHES "PackageMaker|DragNDrop")
+
+ set (CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}")
+ if (DEVEL)
+ set (CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_FILE_NAME}-devel")
+ endif ()
+ set (CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_FILE_NAME}-${CPACK_PACKAGE_VERSION}")
+
+# ------------------------------------------------------------------------------
+# Debian package
+elseif (CPACK_GENERATOR MATCHES "DEB")
+
+ set (CPACK_PACKAGE_FILE_NAME "lib${CPACK_PACKAGE_NAME}")
+ if (DEVEL)
+ set (CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_FILE_NAME}-dev")
+ else ()
+ set (CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_FILE_NAME}0")
+ endif ()
+ set (CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_FILE_NAME}_${CPACK_PACKAGE_VERSION}-1_${CPACK_PACKAGE_ARCHITECTURE}")
+
+ set (CPACK_DEBIAN_PACKAGE_DEPENDS)
+ set (CPACK_DEBIAN_PACKAGE_SECTION "devel")
+ set (CPACK_DEBIAN_PACKAGE_PRIORITY "optional")
+ set (CPACK_DEBIAN_PACKAGE_HOMEPAGE "${CPACK_RPM_PACKAGE_URL}")
+ set (CPACK_DEBIAN_PACKAGE_MAINTAINER "${CPACK_PACKAGE_VENDOR}")
+ set (CPACK_DEBIAN_PACKAGE_ARCHITECTURE "${CPACK_PACKAGE_ARCHITECTURE}")
+
+# ------------------------------------------------------------------------------
+# RPM package
+elseif (CPACK_GENERATOR MATCHES "RPM")
+
+ set (CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}")
+ if (DEVEL)
+ set (CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_FILE_NAME}-devel")
+ endif ()
+ set (CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_FILE_NAME}-${CPACK_PACKAGE_VERSION}-1.${CPACK_PACKAGE_ARCHITECTURE}")
+
+endif ()
diff --git a/third_party/gflags/cmake/utils.cmake b/third_party/gflags/cmake/utils.cmake
new file mode 100644
index 0000000..9cef463
--- /dev/null
+++ b/third_party/gflags/cmake/utils.cmake
@@ -0,0 +1,96 @@
+## Utility CMake functions.
+
+# ----------------------------------------------------------------------------
+## Convert boolean value to 0 or 1
+macro (bool_to_int VAR)
+ if (${VAR})
+ set (${VAR} 1)
+ else ()
+ set (${VAR} 0)
+ endif ()
+endmacro ()
+
+# ----------------------------------------------------------------------------
+## Extract version numbers from version string.
+function (version_numbers version major minor patch)
+ if (version MATCHES "([0-9]+)(\\.[0-9]+)?(\\.[0-9]+)?(rc[1-9][0-9]*|[a-z]+)?")
+ if (CMAKE_MATCH_1)
+ set (_major ${CMAKE_MATCH_1})
+ else ()
+ set (_major 0)
+ endif ()
+ if (CMAKE_MATCH_2)
+ set (_minor ${CMAKE_MATCH_2})
+ string (REGEX REPLACE "^\\." "" _minor "${_minor}")
+ else ()
+ set (_minor 0)
+ endif ()
+ if (CMAKE_MATCH_3)
+ set (_patch ${CMAKE_MATCH_3})
+ string (REGEX REPLACE "^\\." "" _patch "${_patch}")
+ else ()
+ set (_patch 0)
+ endif ()
+ else ()
+ set (_major 0)
+ set (_minor 0)
+ set (_patch 0)
+ endif ()
+ set ("${major}" "${_major}" PARENT_SCOPE)
+ set ("${minor}" "${_minor}" PARENT_SCOPE)
+ set ("${patch}" "${_patch}" PARENT_SCOPE)
+endfunction ()
+
+# ----------------------------------------------------------------------------
+## Configure public header files
+function (configure_headers out)
+ set (tmp)
+ foreach (src IN LISTS ARGN)
+ if (IS_ABSOLUTE "${src}")
+ list (APPEND tmp "${src}")
+ elseif (EXISTS "${PROJECT_SOURCE_DIR}/src/${src}.in")
+ configure_file ("${PROJECT_SOURCE_DIR}/src/${src}.in" "${PROJECT_BINARY_DIR}/include/${GFLAGS_INCLUDE_DIR}/${src}" @ONLY)
+ list (APPEND tmp "${PROJECT_BINARY_DIR}/include/${GFLAGS_INCLUDE_DIR}/${src}")
+ else ()
+ configure_file ("${PROJECT_SOURCE_DIR}/src/${src}" "${PROJECT_BINARY_DIR}/include/${GFLAGS_INCLUDE_DIR}/${src}" COPYONLY)
+ list (APPEND tmp "${PROJECT_BINARY_DIR}/include/${GFLAGS_INCLUDE_DIR}/${src}")
+ endif ()
+ endforeach ()
+ set (${out} "${tmp}" PARENT_SCOPE)
+endfunction ()
+
+# ----------------------------------------------------------------------------
+## Configure source files with .in suffix
+function (configure_sources out)
+ set (tmp)
+ foreach (src IN LISTS ARGN)
+ if (src MATCHES ".h$" AND EXISTS "${PROJECT_SOURCE_DIR}/src/${src}.in")
+ configure_file ("${PROJECT_SOURCE_DIR}/src/${src}.in" "${PROJECT_BINARY_DIR}/include/${GFLAGS_INCLUDE_DIR}/${src}" @ONLY)
+ list (APPEND tmp "${PROJECT_BINARY_DIR}/include/${GFLAGS_INCLUDE_DIR}/${src}")
+ else ()
+ list (APPEND tmp "${PROJECT_SOURCE_DIR}/src/${src}")
+ endif ()
+ endforeach ()
+ set (${out} "${tmp}" PARENT_SCOPE)
+endfunction ()
+
+# ----------------------------------------------------------------------------
+## Add usage test
+#
+# Using PASS_REGULAR_EXPRESSION and FAIL_REGULAR_EXPRESSION would
+# do as well, but CMake/CTest does not allow us to specify an
+# expected exit status. Moreover, the execute_test.cmake script
+# sets environment variables needed by the --fromenv/--tryfromenv tests.
+macro (add_gflags_test name expected_rc expected_output unexpected_output cmd)
+ set (args "--test_tmpdir=${PROJECT_BINARY_DIR}/Testing/Temporary"
+ "--srcdir=${PROJECT_SOURCE_DIR}/test")
+ add_test (
+ NAME ${name}
+ COMMAND "${CMAKE_COMMAND}" "-DCOMMAND:STRING=$<TARGET_FILE:${cmd}>;${args};${ARGN}"
+ "-DEXPECTED_RC:STRING=${expected_rc}"
+ "-DEXPECTED_OUTPUT:STRING=${expected_output}"
+ "-DUNEXPECTED_OUTPUT:STRING=${unexpected_output}"
+ -P "${PROJECT_SOURCE_DIR}/cmake/execute_test.cmake"
+ WORKING_DIRECTORY "${GFLAGS_FLAGFILES_DIR}"
+ )
+endmacro ()
diff --git a/third_party/gflags/cmake/version.cmake.in b/third_party/gflags/cmake/version.cmake.in
new file mode 100644
index 0000000..1e1af05
--- /dev/null
+++ b/third_party/gflags/cmake/version.cmake.in
@@ -0,0 +1,21 @@
+## gflags CMake configuration version file
+
+# -----------------------------------------------------------------------------
+# library version
+set (PACKAGE_VERSION "@PACKAGE_VERSION@")
+
+# -----------------------------------------------------------------------------
+# check compatibility
+
+# Perform compatibility check here using the input CMake variables.
+# See example in http://www.cmake.org/Wiki/CMake_2.6_Notes.
+
+set (PACKAGE_VERSION_COMPATIBLE TRUE)
+set (PACKAGE_VERSION_UNSUITABLE FALSE)
+
+if ("${PACKAGE_FIND_VERSION_MAJOR}" EQUAL "@PACKAGE_VERSION_MAJOR@" AND
+ "${PACKAGE_FIND_VERSION_MINOR}" EQUAL "@PACKAGE_VERSION_MINOR@")
+ set (PACKAGE_VERSION_EXACT TRUE)
+else ()
+ set (PACKAGE_VERSION_EXACT FALSE)
+endif ()
diff --git a/third_party/gflags/doc b/third_party/gflags/doc
new file mode 160000
index 0000000..e1d15b3
--- /dev/null
+++ b/third_party/gflags/doc
@@ -0,0 +1 @@
+Subproject commit e1d15b33406ec571095e7a91dbc4b2ada1ae3bac
diff --git a/third_party/gflags/src/config.h.in b/third_party/gflags/src/config.h.in
new file mode 100644
index 0000000..a8708da
--- /dev/null
+++ b/third_party/gflags/src/config.h.in
@@ -0,0 +1,112 @@
+/* Generated from config.h.in during build configuration using CMake. */
+
+// Note: This header file is only used internally. It is not part of public interface!
+
+// ---------------------------------------------------------------------------
+// System checks
+
+// Define if you build this library for a MS Windows OS.
+#cmakedefine OS_WINDOWS
+
+// Define if you have the <stdint.h> header file.
+#cmakedefine HAVE_STDINT_H
+
+// Define if you have the <sys/types.h> header file.
+#cmakedefine HAVE_SYS_TYPES_H
+
+// Define if you have the <inttypes.h> header file.
+#cmakedefine HAVE_INTTYPES_H
+
+// Define if you have the <sys/stat.h> header file.
+#cmakedefine HAVE_SYS_STAT_H
+
+// Define if you have the <unistd.h> header file.
+#cmakedefine HAVE_UNISTD_H
+
+// Define if you have the <fnmatch.h> header file.
+#cmakedefine HAVE_FNMATCH_H
+
+// Define if you have the <shlwapi.h> header file (Windows 2000/XP).
+#cmakedefine HAVE_SHLWAPI_H
+
+// Define if you have the strtoll function.
+#cmakedefine HAVE_STRTOLL
+
+// Define if you have the strtoq function.
+#cmakedefine HAVE_STRTOQ
+
+// Define if you have the <pthread.h> header file.
+#cmakedefine HAVE_PTHREAD
+
+// Define if your pthread library defines the type pthread_rwlock_t
+#cmakedefine HAVE_RWLOCK
+
+// gcc requires this to get PRId64, etc.
+#if defined(HAVE_INTTYPES_H) && !defined(__STDC_FORMAT_MACROS)
+# define __STDC_FORMAT_MACROS 1
+#endif
+
+// ---------------------------------------------------------------------------
+// Package information
+
+// Name of package.
+#define PACKAGE @PROJECT_NAME@
+
+// Define to the full name of this package.
+#define PACKAGE_NAME @PACKAGE_NAME@
+
+// Define to the full name and version of this package.
+#define PACKAGE_STRING @PACKAGE_STRING@
+
+// Define to the one symbol short name of this package.
+#define PACKAGE_TARNAME @PACKAGE_TARNAME@
+
+// Define to the version of this package.
+#define PACKAGE_VERSION @PACKAGE_VERSION@
+
+// Version number of package.
+#define VERSION PACKAGE_VERSION
+
+// Define to the address where bug reports for this package should be sent.
+#define PACKAGE_BUGREPORT @PACKAGE_BUGREPORT@
+
+// ---------------------------------------------------------------------------
+// Path separator
+#ifndef PATH_SEPARATOR
+# ifdef OS_WINDOWS
+# define PATH_SEPARATOR '\\'
+# else
+# define PATH_SEPARATOR '/'
+# endif
+#endif
+
+// ---------------------------------------------------------------------------
+// Windows
+
+// Whether gflags library is a DLL.
+#ifndef GFLAGS_IS_A_DLL
+# define GFLAGS_IS_A_DLL 0
+#endif
+
+// Always export symbols when compiling a shared library as this file is only
+// included by internal modules when building the gflags library itself.
+// The gflags_declare.h header file will set it to import these symbols otherwise.
+#ifndef GFLAGS_DLL_DECL
+# if GFLAGS_IS_A_DLL && defined(_MSC_VER)
+# define GFLAGS_DLL_DECL __declspec(dllexport)
+# else
+# define GFLAGS_DLL_DECL
+# endif
+#endif
+// Flags defined by the gflags library itself must be exported
+#ifndef GFLAGS_DLL_DEFINE_FLAG
+# define GFLAGS_DLL_DEFINE_FLAG GFLAGS_DLL_DECL
+#endif
+
+#ifdef OS_WINDOWS
+// The unittests import the symbols of the shared gflags library
+# if GFLAGS_IS_A_DLL && defined(_MSC_VER)
+# define GFLAGS_DLL_DECL_FOR_UNITTESTS __declspec(dllimport)
+# endif
+# include "windows_port.h"
+#endif
diff --git a/third_party/gflags/src/gflags.cc b/third_party/gflags/src/gflags.cc
new file mode 100644
index 0000000..285050f
--- /dev/null
+++ b/third_party/gflags/src/gflags.cc
@@ -0,0 +1,1957 @@
+// Copyright (c) 1999, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// ---
+// Revamped and reorganized by Craig Silverstein
+//
+// This file contains the implementation of all our command line flags
+// stuff. Here's how everything fits together
+//
+// * FlagRegistry owns CommandLineFlags owns FlagValue.
+// * FlagSaver holds a FlagRegistry (saves it at construct time,
+// restores it at destroy time).
+// * CommandLineFlagParser lives outside that hierarchy, but works on
+// CommandLineFlags (modifying the FlagValues).
+// * Free functions like SetCommandLineOption() work via one of the
+// above (such as CommandLineFlagParser).
+//
+// In more detail:
+//
+// -- The main classes that hold flag data:
+//
+// FlagValue holds the current value of a flag. It's
+// pseudo-templatized: every operation on a FlagValue is typed. It
+// also deals with storage-lifetime issues (so flag values don't go
+// away in a destructor), which is why we need a whole class to hold a
+// variable's value.
+//
+// CommandLineFlag is all the information about a single command-line
+// flag. It has a FlagValue for the flag's current value, but also
+// the flag's name, type, etc.
+//
+// FlagRegistry is a collection of CommandLineFlags. There's the
+// global registry, which is where flags defined via DEFINE_foo()
+// live. But it's possible to define your own flag, manually, in a
+// different registry you create. (In practice, multiple registries
+// are used only by FlagSaver).
+//
+// A given FlagValue is owned by exactly one CommandLineFlag. A given
+// CommandLineFlag is owned by exactly one FlagRegistry. FlagRegistry
+// has a lock; any operation that writes to a FlagValue or
+// CommandLineFlag owned by that registry must acquire the
+// FlagRegistry lock before doing so.
+//
+// --- Some other classes and free functions:
+//
+// CommandLineFlagInfo is a client-exposed version of CommandLineFlag.
+// Once it's instantiated, it has no dependencies or relationships
+// with any other part of this file.
+//
+// FlagRegisterer is the helper class used by the DEFINE_* macros to
+// allow work to be done at global initialization time.
+//
+// CommandLineFlagParser is the class that reads from the commandline
+// and instantiates flag values based on that. It needs to poke into
+// the innards of the FlagValue->CommandLineFlag->FlagRegistry class
+// hierarchy to do that. It's careful to acquire the FlagRegistry
+// lock before doing any writing or other non-const actions.
+//
+// GetCommandLineOption is just a hook into registry routines to
+// retrieve a flag based on its name. SetCommandLineOption, on the
+// other hand, hooks into CommandLineFlagParser. Other API functions
+// are, similarly, mostly hooks into the functionality described above.
+
+#include "config.h"
+#include "gflags.h"
+
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#if defined(HAVE_FNMATCH_H)
+# include <fnmatch.h>
+#elif defined(HAVE_SHLWAPI_H)
+# include <shlwapi.h>
+#endif
+#include <stdarg.h> // For va_list and related operations
+#include <stdio.h>
+#include <string.h>
+
+#include <algorithm>
+#include <map>
+#include <string>
+#include <utility> // for pair<>
+#include <vector>
+
+#include "mutex.h"
+#include "util.h"
+
+// Special flags, type 1: the 'recursive' flags. They set another flag's val.
+DEFINE_string(flagfile, "", "load flags from file");
+DEFINE_string(fromenv, "", "set flags from the environment"
+ " [use 'export FLAGS_flag1=value']");
+DEFINE_string(tryfromenv, "", "set flags from the environment if present");
+
+// Special flags, type 2: the 'parsing' flags. They modify how we parse.
+DEFINE_string(undefok, "", "comma-separated list of flag names that it is okay to specify "
+ "on the command line even if the program does not define a flag "
+ "with that name. IMPORTANT: flags in this list that have "
+ "arguments MUST use the flag=value format");
+
+namespace GFLAGS_NAMESPACE {
+
+using std::map;
+using std::pair;
+using std::sort;
+using std::string;
+using std::vector;
+
+// This is used by the unittest to test error-exit code
+void GFLAGS_DLL_DECL (*gflags_exitfunc)(int) = &exit; // from stdlib.h
+
+
+// The help message indicating that the commandline flag has been
+// 'stripped'. It will not show up when doing "-help" and its
+// variants. The flag is stripped if STRIP_FLAG_HELP is set to 1
+// before including base/gflags.h
+
+// This is used by this file, and also in gflags_reporting.cc
+const char kStrippedFlagHelp[] = "\001\002\003\004 (unknown) \004\003\002\001";
+
+namespace {
+
+// There are also 'reporting' flags, in gflags_reporting.cc.
+
+static const char kError[] = "ERROR: ";
+
+// Indicates that undefined options are to be ignored.
+// Enables deferred processing of flags in dynamically loaded libraries.
+static bool allow_command_line_reparsing = false;
+
+static bool logging_is_probably_set_up = false;
+
+// This is a 'prototype' validate-function. 'Real' validate
+// functions, take a flag-value as an argument: ValidateFn(bool) or
+// ValidateFn(uint64). However, for easier storage, we strip off this
+// argument and then restore it when actually calling the function on
+// a flag value.
+typedef bool (*ValidateFnProto)();
+
+// Whether we should die when reporting an error.
+enum DieWhenReporting { DIE, DO_NOT_DIE };
+
+// Report Error and exit if requested.
+static void ReportError(DieWhenReporting should_die, const char* format, ...) {
+ char error_message[255];
+ va_list ap;
+ va_start(ap, format);
+ vsnprintf(error_message, sizeof(error_message), format, ap);
+ va_end(ap);
+ fprintf(stderr, "%s", error_message);
+ fflush(stderr); // should be unnecessary, but cygwin's rxvt buffers stderr
+ if (should_die == DIE) gflags_exitfunc(1);
+}
+
+
+// --------------------------------------------------------------------
+// FlagValue
+// This represent the value a single flag might have. The major
+// functionality is to convert from a string to an object of a
+// given type, and back. Thread-compatible.
+// --------------------------------------------------------------------
+
+class CommandLineFlag;
+class FlagValue {
+ public:
+ FlagValue(void* valbuf, const char* type, bool transfer_ownership_of_value);
+ ~FlagValue();
+
+ bool ParseFrom(const char* spec);
+ string ToString() const;
+
+ private:
+ friend class CommandLineFlag; // for many things, including Validate()
+ friend class GFLAGS_NAMESPACE::FlagSaverImpl; // calls New()
+ friend class FlagRegistry; // checks value_buffer_ for flags_by_ptr_ map
+ template <typename T> friend T GetFromEnv(const char*, const char*, T);
+ friend bool TryParseLocked(const CommandLineFlag*, FlagValue*,
+ const char*, string*); // for New(), CopyFrom()
+
+ enum ValueType {
+ FV_BOOL = 0,
+ FV_INT32 = 1,
+ FV_INT64 = 2,
+ FV_UINT64 = 3,
+ FV_DOUBLE = 4,
+ FV_STRING = 5,
+ FV_MAX_INDEX = 5,
+ };
+ const char* TypeName() const;
+ bool Equal(const FlagValue& x) const;
+ FlagValue* New() const; // creates a new one with default value
+ void CopyFrom(const FlagValue& x);
+ int ValueSize() const;
+
+ // Calls the given validate-fn on value_buffer_, and returns
+ // whatever it returns. But first casts validate_fn_proto to a
+ // function that takes our value as an argument (eg void
+ // (*validate_fn)(bool) for a bool flag).
+ bool Validate(const char* flagname, ValidateFnProto validate_fn_proto) const;
+
+ void* value_buffer_; // points to the buffer holding our data
+ int8 type_; // how to interpret value_
+ bool owns_value_; // whether to free value on destruct
+
+ FlagValue(const FlagValue&); // no copying!
+ void operator=(const FlagValue&);
+};
+
+
+// This could be a templated method of FlagValue, but doing so adds to the
+// size of the .o. Since there's no type-safety here anyway, macro is ok.
+#define VALUE_AS(type) *reinterpret_cast<type*>(value_buffer_)
+#define OTHER_VALUE_AS(fv, type) *reinterpret_cast<type*>(fv.value_buffer_)
+#define SET_VALUE_AS(type, value) VALUE_AS(type) = (value)
+
+FlagValue::FlagValue(void* valbuf, const char* type,
+ bool transfer_ownership_of_value)
+ : value_buffer_(valbuf),
+ owns_value_(transfer_ownership_of_value) {
+ for (type_ = 0; type_ <= FV_MAX_INDEX; ++type_) {
+ if (!strcmp(type, TypeName())) {
+ break;
+ }
+ }
+ assert(type_ <= FV_MAX_INDEX); // Unknown typename
+}
+
+FlagValue::~FlagValue() {
+ if (!owns_value_) {
+ return;
+ }
+ switch (type_) {
+ case FV_BOOL: delete reinterpret_cast<bool*>(value_buffer_); break;
+ case FV_INT32: delete reinterpret_cast<int32*>(value_buffer_); break;
+ case FV_INT64: delete reinterpret_cast<int64*>(value_buffer_); break;
+ case FV_UINT64: delete reinterpret_cast<uint64*>(value_buffer_); break;
+ case FV_DOUBLE: delete reinterpret_cast<double*>(value_buffer_); break;
+ case FV_STRING: delete reinterpret_cast<string*>(value_buffer_); break;
+ }
+}
+
+bool FlagValue::ParseFrom(const char* value) {
+ if (type_ == FV_BOOL) {
+ const char* kTrue[] = { "1", "t", "true", "y", "yes" };
+ const char* kFalse[] = { "0", "f", "false", "n", "no" };
+ COMPILE_ASSERT(sizeof(kTrue) == sizeof(kFalse), true_false_equal);
+ for (size_t i = 0; i < sizeof(kTrue)/sizeof(*kTrue); ++i) {
+ if (strcasecmp(value, kTrue[i]) == 0) {
+ SET_VALUE_AS(bool, true);
+ return true;
+ } else if (strcasecmp(value, kFalse[i]) == 0) {
+ SET_VALUE_AS(bool, false);
+ return true;
+ }
+ }
+ return false; // didn't match a legal input
+
+ } else if (type_ == FV_STRING) {
+ SET_VALUE_AS(string, value);
+ return true;
+ }
+
+ // OK, it's likely to be numeric, and we'll be using a strtoXXX method.
+ if (value[0] == '\0') // empty-string is only allowed for string type.
+ return false;
+ char* end;
+ // Leading 0x puts us in base 16. But leading 0 does not put us in base 8!
+ // It caused too many bugs when we had that behavior.
+ int base = 10; // by default
+ if (value[0] == '0' && (value[1] == 'x' || value[1] == 'X'))
+ base = 16;
+ errno = 0;
+
+ switch (type_) {
+ case FV_INT32: {
+ const int64 r = strto64(value, &end, base);
+ if (errno || end != value + strlen(value)) return false; // bad parse
+ if (static_cast<int32>(r) != r) // worked, but number out of range
+ return false;
+ SET_VALUE_AS(int32, static_cast<int32>(r));
+ return true;
+ }
+ case FV_INT64: {
+ const int64 r = strto64(value, &end, base);
+ if (errno || end != value + strlen(value)) return false; // bad parse
+ SET_VALUE_AS(int64, r);
+ return true;
+ }
+ case FV_UINT64: {
+ while (*value == ' ') value++;
+ if (*value == '-') return false; // negative number
+ const uint64 r = strtou64(value, &end, base);
+ if (errno || end != value + strlen(value)) return false; // bad parse
+ SET_VALUE_AS(uint64, r);
+ return true;
+ }
+ case FV_DOUBLE: {
+ const double r = strtod(value, &end);
+ if (errno || end != value + strlen(value)) return false; // bad parse
+ SET_VALUE_AS(double, r);
+ return true;
+ }
+ default: {
+ assert(false); // unknown type
+ return false;
+ }
+ }
+}
+
+string FlagValue::ToString() const {
+ char intbuf[64]; // enough to hold even the biggest number
+ switch (type_) {
+ case FV_BOOL:
+ return VALUE_AS(bool) ? "true" : "false";
+ case FV_INT32:
+ snprintf(intbuf, sizeof(intbuf), "%" PRId32, VALUE_AS(int32));
+ return intbuf;
+ case FV_INT64:
+ snprintf(intbuf, sizeof(intbuf), "%" PRId64, VALUE_AS(int64));
+ return intbuf;
+ case FV_UINT64:
+ snprintf(intbuf, sizeof(intbuf), "%" PRIu64, VALUE_AS(uint64));
+ return intbuf;
+ case FV_DOUBLE:
+ snprintf(intbuf, sizeof(intbuf), "%.17g", VALUE_AS(double));
+ return intbuf;
+ case FV_STRING:
+ return VALUE_AS(string);
+ default:
+ assert(false);
+ return ""; // unknown type
+ }
+}
+
+bool FlagValue::Validate(const char* flagname,
+ ValidateFnProto validate_fn_proto) const {
+ switch (type_) {
+ case FV_BOOL:
+ return reinterpret_cast<bool (*)(const char*, bool)>(
+ validate_fn_proto)(flagname, VALUE_AS(bool));
+ case FV_INT32:
+ return reinterpret_cast<bool (*)(const char*, int32)>(
+ validate_fn_proto)(flagname, VALUE_AS(int32));
+ case FV_INT64:
+ return reinterpret_cast<bool (*)(const char*, int64)>(
+ validate_fn_proto)(flagname, VALUE_AS(int64));
+ case FV_UINT64:
+ return reinterpret_cast<bool (*)(const char*, uint64)>(
+ validate_fn_proto)(flagname, VALUE_AS(uint64));
+ case FV_DOUBLE:
+ return reinterpret_cast<bool (*)(const char*, double)>(
+ validate_fn_proto)(flagname, VALUE_AS(double));
+ case FV_STRING:
+ return reinterpret_cast<bool (*)(const char*, const string&)>(
+ validate_fn_proto)(flagname, VALUE_AS(string));
+ default:
+ assert(false); // unknown type
+ return false;
+ }
+}
+
+const char* FlagValue::TypeName() const {
+ static const char types[] =
+ "bool\0xx"
+ "int32\0x"
+ "int64\0x"
+ "uint64\0"
+ "double\0"
+ "string";
+ if (type_ > FV_MAX_INDEX) {
+ assert(false);
+ return "";
+ }
+ // Directly indexing the strings in the 'types' string, each of them is 7 bytes long.
+ return &types[type_ * 7];
+}
+
+bool FlagValue::Equal(const FlagValue& x) const {
+ if (type_ != x.type_)
+ return false;
+ switch (type_) {
+ case FV_BOOL: return VALUE_AS(bool) == OTHER_VALUE_AS(x, bool);
+ case FV_INT32: return VALUE_AS(int32) == OTHER_VALUE_AS(x, int32);
+ case FV_INT64: return VALUE_AS(int64) == OTHER_VALUE_AS(x, int64);
+ case FV_UINT64: return VALUE_AS(uint64) == OTHER_VALUE_AS(x, uint64);
+ case FV_DOUBLE: return VALUE_AS(double) == OTHER_VALUE_AS(x, double);
+ case FV_STRING: return VALUE_AS(string) == OTHER_VALUE_AS(x, string);
+ default: assert(false); return false; // unknown type
+ }
+}
+
+FlagValue* FlagValue::New() const {
+ const char *type = TypeName();
+ switch (type_) {
+ case FV_BOOL: return new FlagValue(new bool(false), type, true);
+ case FV_INT32: return new FlagValue(new int32(0), type, true);
+ case FV_INT64: return new FlagValue(new int64(0), type, true);
+ case FV_UINT64: return new FlagValue(new uint64(0), type, true);
+ case FV_DOUBLE: return new FlagValue(new double(0.0), type, true);
+ case FV_STRING: return new FlagValue(new string, type, true);
+ default: assert(false); return NULL; // unknown type
+ }
+}
+
+void FlagValue::CopyFrom(const FlagValue& x) {
+ assert(type_ == x.type_);
+ switch (type_) {
+ case FV_BOOL: SET_VALUE_AS(bool, OTHER_VALUE_AS(x, bool)); break;
+ case FV_INT32: SET_VALUE_AS(int32, OTHER_VALUE_AS(x, int32)); break;
+ case FV_INT64: SET_VALUE_AS(int64, OTHER_VALUE_AS(x, int64)); break;
+ case FV_UINT64: SET_VALUE_AS(uint64, OTHER_VALUE_AS(x, uint64)); break;
+ case FV_DOUBLE: SET_VALUE_AS(double, OTHER_VALUE_AS(x, double)); break;
+ case FV_STRING: SET_VALUE_AS(string, OTHER_VALUE_AS(x, string)); break;
+ default: assert(false); // unknown type
+ }
+}
+
+int FlagValue::ValueSize() const {
+ if (type_ > FV_MAX_INDEX) {
+ assert(false); // unknown type
+ return 0;
+ }
+ static const uint8 valuesize[] = {
+ sizeof(bool),
+ sizeof(int32),
+ sizeof(int64),
+ sizeof(uint64),
+ sizeof(double),
+ sizeof(string),
+ };
+ return valuesize[type_];
+}
+
+// --------------------------------------------------------------------
+// CommandLineFlag
+// This represents a single flag, including its name, description,
+// default value, and current value. Mostly this serves as a
+// struct, though it also knows how to register itself.
+// All CommandLineFlags are owned by a (exactly one)
+// FlagRegistry. If you wish to modify fields in this class, you
+// should acquire the FlagRegistry lock for the registry that owns
+// this flag.
+// --------------------------------------------------------------------
+
+class CommandLineFlag {
+ public:
+ // Note: we take over memory-ownership of current_val and default_val.
+ CommandLineFlag(const char* name, const char* help, const char* filename,
+ FlagValue* current_val, FlagValue* default_val);
+ ~CommandLineFlag();
+
+ const char* name() const { return name_; }
+ const char* help() const { return help_; }
+ const char* filename() const { return file_; }
+ const char* CleanFileName() const; // nixes irrelevant prefix such as homedir
+ string current_value() const { return current_->ToString(); }
+ string default_value() const { return defvalue_->ToString(); }
+ const char* type_name() const { return defvalue_->TypeName(); }
+ ValidateFnProto validate_function() const { return validate_fn_proto_; }
+ const void* flag_ptr() const { return current_->value_buffer_; }
+
+ void FillCommandLineFlagInfo(struct CommandLineFlagInfo* result);
+
+ // If validate_fn_proto_ is non-NULL, calls it on value, returns result.
+ bool Validate(const FlagValue& value) const;
+ bool ValidateCurrent() const { return Validate(*current_); }
+
+ private:
+ // for SetFlagLocked() and setting flags_by_ptr_
+ friend class FlagRegistry;
+ friend class GFLAGS_NAMESPACE::FlagSaverImpl; // for cloning the values
+ // set validate_fn
+ friend bool AddFlagValidator(const void*, ValidateFnProto);
+
+ // This copies all the non-const members: modified, processed, defvalue, etc.
+ void CopyFrom(const CommandLineFlag& src);
+
+ void UpdateModifiedBit();
+
+ const char* const name_; // Flag name
+ const char* const help_; // Help message
+ const char* const file_; // Which file did this come from?
+ bool modified_; // Set after default assignment?
+ FlagValue* defvalue_; // Default value for flag
+ FlagValue* current_; // Current value for flag
+ // This is a casted, 'generic' version of validate_fn, which actually
+ // takes a flag-value as an arg (void (*validate_fn)(bool), say).
+ // When we pass this to current_->Validate(), it will cast it back to
+ // the proper type. This may be NULL to mean we have no validate_fn.
+ ValidateFnProto validate_fn_proto_;
+
+ CommandLineFlag(const CommandLineFlag&); // no copying!
+ void operator=(const CommandLineFlag&);
+};
+
+CommandLineFlag::CommandLineFlag(const char* name, const char* help,
+ const char* filename,
+ FlagValue* current_val, FlagValue* default_val)
+ : name_(name), help_(help), file_(filename), modified_(false),
+ defvalue_(default_val), current_(current_val), validate_fn_proto_(NULL) {
+}
+
+CommandLineFlag::~CommandLineFlag() {
+ delete current_;
+ delete defvalue_;
+}
+
+const char* CommandLineFlag::CleanFileName() const {
+ // Compute top-level directory & file that this appears in
+ // search full path backwards.
+ // Stop going backwards at kRootDir; and skip by the first slash.
+ static const char kRootDir[] = ""; // can set this to root directory,
+
+ if (sizeof(kRootDir)-1 == 0) // no prefix to strip
+ return filename();
+
+ const char* clean_name = filename() + strlen(filename()) - 1;
+ while ( clean_name > filename() ) {
+ if (*clean_name == PATH_SEPARATOR) {
+ if (strncmp(clean_name, kRootDir, sizeof(kRootDir)-1) == 0) {
+ clean_name += sizeof(kRootDir)-1; // past root-dir
+ break;
+ }
+ }
+ --clean_name;
+ }
+ while ( *clean_name == PATH_SEPARATOR ) ++clean_name; // Skip any slashes
+ return clean_name;
+}
+
+void CommandLineFlag::FillCommandLineFlagInfo(
+ CommandLineFlagInfo* result) {
+ result->name = name();
+ result->type = type_name();
+ result->description = help();
+ result->current_value = current_value();
+ result->default_value = default_value();
+ result->filename = CleanFileName();
+ UpdateModifiedBit();
+ result->is_default = !modified_;
+ result->has_validator_fn = validate_function() != NULL;
+ result->flag_ptr = flag_ptr();
+}
+
+void CommandLineFlag::UpdateModifiedBit() {
+ // Update the "modified" bit in case somebody bypassed the
+ // Flags API and wrote directly through the FLAGS_name variable.
+ if (!modified_ && !current_->Equal(*defvalue_)) {
+ modified_ = true;
+ }
+}
+
+void CommandLineFlag::CopyFrom(const CommandLineFlag& src) {
+ // Note we only copy the non-const members; others are fixed at construct time
+ if (modified_ != src.modified_) modified_ = src.modified_;
+ if (!current_->Equal(*src.current_)) current_->CopyFrom(*src.current_);
+ if (!defvalue_->Equal(*src.defvalue_)) defvalue_->CopyFrom(*src.defvalue_);
+ if (validate_fn_proto_ != src.validate_fn_proto_)
+ validate_fn_proto_ = src.validate_fn_proto_;
+}
+
+bool CommandLineFlag::Validate(const FlagValue& value) const {
+
+ if (validate_function() == NULL)
+ return true;
+ else
+ return value.Validate(name(), validate_function());
+}
+
+
+// --------------------------------------------------------------------
+// FlagRegistry
+// A FlagRegistry singleton object holds all flag objects indexed
+// by their names so that if you know a flag's name (as a C
+// string), you can access or set it. If the function is named
+// FooLocked(), you must own the registry lock before calling
+// the function; otherwise, you should *not* hold the lock, and
+// the function will acquire it itself if needed.
+// --------------------------------------------------------------------
+
+struct StringCmp { // Used by the FlagRegistry map class to compare char*'s
+ bool operator() (const char* s1, const char* s2) const {
+ return (strcmp(s1, s2) < 0);
+ }
+};
+
+
+class FlagRegistry {
+ public:
+ FlagRegistry() {
+ }
+ ~FlagRegistry() {
+ // Not using STLDeleteElements as that resides in util and this
+ // class is base.
+ for (FlagMap::iterator p = flags_.begin(), e = flags_.end(); p != e; ++p) {
+ CommandLineFlag* flag = p->second;
+ delete flag;
+ }
+ }
+
+ static void DeleteGlobalRegistry() {
+ delete global_registry_;
+ global_registry_ = NULL;
+ }
+
+ // Store a flag in this registry. Takes ownership of the given pointer.
+ void RegisterFlag(CommandLineFlag* flag);
+
+ void Lock() { lock_.Lock(); }
+ void Unlock() { lock_.Unlock(); }
+
+ // Returns the flag object for the specified name, or NULL if not found.
+ CommandLineFlag* FindFlagLocked(const char* name);
+
+ // Returns the flag object whose current-value is stored at flag_ptr.
+ // That is, for whom current_->value_buffer_ == flag_ptr
+ CommandLineFlag* FindFlagViaPtrLocked(const void* flag_ptr);
+
+ // A fancier form of FindFlag that works correctly if name is of the
+ // form flag=value. In that case, we set key to point to flag, and
+ // modify v to point to the value (if present), and return the flag
+ // with the given name. If the flag does not exist, returns NULL
+ // and sets error_message.
+ CommandLineFlag* SplitArgumentLocked(const char* argument,
+ string* key, const char** v,
+ string* error_message);
+
+ // Set the value of a flag. If the flag was successfully set to
+ // value, set msg to indicate the new flag-value, and return true.
+ // Otherwise, set msg to indicate the error, leave flag unchanged,
+ // and return false. msg can be NULL.
+ bool SetFlagLocked(CommandLineFlag* flag, const char* value,
+ FlagSettingMode set_mode, string* msg);
+
+ static FlagRegistry* GlobalRegistry(); // returns a singleton registry
+
+ private:
+ friend class GFLAGS_NAMESPACE::FlagSaverImpl; // reads all the flags in order to copy them
+ friend class CommandLineFlagParser; // for ValidateAllFlags
+ friend void GFLAGS_NAMESPACE::GetAllFlags(vector<CommandLineFlagInfo>*);
+
+ // The map from name to flag, for FindFlagLocked().
+ typedef map<const char*, CommandLineFlag*, StringCmp> FlagMap;
+ typedef FlagMap::iterator FlagIterator;
+ typedef FlagMap::const_iterator FlagConstIterator;
+ FlagMap flags_;
+
+ // The map from current-value pointer to flag, fo FindFlagViaPtrLocked().
+ typedef map<const void*, CommandLineFlag*> FlagPtrMap;
+ FlagPtrMap flags_by_ptr_;
+
+ static FlagRegistry* global_registry_; // a singleton registry
+
+ Mutex lock_;
+ static Mutex global_registry_lock_;
+
+ static void InitGlobalRegistry();
+
+ // Disallow
+ FlagRegistry(const FlagRegistry&);
+ FlagRegistry& operator=(const FlagRegistry&);
+};
+
+class FlagRegistryLock {
+ public:
+ explicit FlagRegistryLock(FlagRegistry* fr) : fr_(fr) { fr_->Lock(); }
+ ~FlagRegistryLock() { fr_->Unlock(); }
+ private:
+ FlagRegistry *const fr_;
+};
+
+
+void FlagRegistry::RegisterFlag(CommandLineFlag* flag) {
+ Lock();
+ pair<FlagIterator, bool> ins =
+ flags_.insert(pair<const char*, CommandLineFlag*>(flag->name(), flag));
+ if (ins.second == false) { // means the name was already in the map
+ if (strcmp(ins.first->second->filename(), flag->filename()) != 0) {
+ ReportError(DIE, "ERROR: flag '%s' was defined more than once "
+ "(in files '%s' and '%s').\n",
+ flag->name(),
+ ins.first->second->filename(),
+ flag->filename());
+ } else {
+ ReportError(DIE, "ERROR: something wrong with flag '%s' in file '%s'. "
+ "One possibility: file '%s' is being linked both statically "
+ "and dynamically into this executable.\n",
+ flag->name(),
+ flag->filename(), flag->filename());
+ }
+ }
+ // Also add to the flags_by_ptr_ map.
+ flags_by_ptr_[flag->current_->value_buffer_] = flag;
+ Unlock();
+}
+
+CommandLineFlag* FlagRegistry::FindFlagLocked(const char* name) {
+ FlagConstIterator i = flags_.find(name);
+ if (i == flags_.end()) {
+ return NULL;
+ } else {
+ return i->second;
+ }
+}
+
+CommandLineFlag* FlagRegistry::FindFlagViaPtrLocked(const void* flag_ptr) {
+ FlagPtrMap::const_iterator i = flags_by_ptr_.find(flag_ptr);
+ if (i == flags_by_ptr_.end()) {
+ return NULL;
+ } else {
+ return i->second;
+ }
+}
+
+CommandLineFlag* FlagRegistry::SplitArgumentLocked(const char* arg,
+ string* key,
+ const char** v,
+ string* error_message) {
+ // Find the flag object for this option
+ const char* flag_name;
+ const char* value = strchr(arg, '=');
+ if (value == NULL) {
+ key->assign(arg);
+ *v = NULL;
+ } else {
+ // Strip out the "=value" portion from arg
+ key->assign(arg, value-arg);
+ *v = ++value; // advance past the '='
+ }
+ flag_name = key->c_str();
+
+ CommandLineFlag* flag = FindFlagLocked(flag_name);
+
+ if (flag == NULL) {
+ // If we can't find the flag-name, then we should return an error.
+ // The one exception is if 1) the flag-name is 'nox', 2) there
+ // exists a flag named 'x', and 3) 'x' is a boolean flag.
+ // In that case, we want to return flag 'x'.
+ if (!(flag_name[0] == 'n' && flag_name[1] == 'o')) {
+ // flag-name is not 'nox', so we're not in the exception case.
+ *error_message = StringPrintf("%sunknown command line flag '%s'\n",
+ kError, key->c_str());
+ return NULL;
+ }
+ flag = FindFlagLocked(flag_name+2);
+ if (flag == NULL) {
+ // No flag named 'x' exists, so we're not in the exception case.
+ *error_message = StringPrintf("%sunknown command line flag '%s'\n",
+ kError, key->c_str());
+ return NULL;
+ }
+ if (strcmp(flag->type_name(), "bool") != 0) {
+ // 'x' exists but is not boolean, so we're not in the exception case.
+ *error_message = StringPrintf(
+ "%sboolean value (%s) specified for %s command line flag\n",
+ kError, key->c_str(), flag->type_name());
+ return NULL;
+ }
+ // We're in the exception case!
+ // Make up a fake value to replace the "no" we stripped out
+ key->assign(flag_name+2); // the name without the "no"
+ *v = "0";
+ }
+
+ // Assign a value if this is a boolean flag
+ if (*v == NULL && strcmp(flag->type_name(), "bool") == 0) {
+ *v = "1"; // the --nox case was already handled, so this is the --x case
+ }
+
+ return flag;
+}
+
+bool TryParseLocked(const CommandLineFlag* flag, FlagValue* flag_value,
+ const char* value, string* msg) {
+ // Use tenative_value, not flag_value, until we know value is valid.
+ FlagValue* tentative_value = flag_value->New();
+ if (!tentative_value->ParseFrom(value)) {
+ if (msg) {
+ StringAppendF(msg,
+ "%sillegal value '%s' specified for %s flag '%s'\n",
+ kError, value,
+ flag->type_name(), flag->name());
+ }
+ delete tentative_value;
+ return false;
+ } else if (!flag->Validate(*tentative_value)) {
+ if (msg) {
+ StringAppendF(msg,
+ "%sfailed validation of new value '%s' for flag '%s'\n",
+ kError, tentative_value->ToString().c_str(),
+ flag->name());
+ }
+ delete tentative_value;
+ return false;
+ } else {
+ flag_value->CopyFrom(*tentative_value);
+ if (msg) {
+ StringAppendF(msg, "%s set to %s\n",
+ flag->name(), flag_value->ToString().c_str());
+ }
+ delete tentative_value;
+ return true;
+ }
+}
+
+bool FlagRegistry::SetFlagLocked(CommandLineFlag* flag,
+ const char* value,
+ FlagSettingMode set_mode,
+ string* msg) {
+ flag->UpdateModifiedBit();
+ switch (set_mode) {
+ case SET_FLAGS_VALUE: {
+ // set or modify the flag's value
+ if (!TryParseLocked(flag, flag->current_, value, msg))
+ return false;
+ flag->modified_ = true;
+ break;
+ }
+ case SET_FLAG_IF_DEFAULT: {
+ // set the flag's value, but only if it hasn't been set by someone else
+ if (!flag->modified_) {
+ if (!TryParseLocked(flag, flag->current_, value, msg))
+ return false;
+ flag->modified_ = true;
+ } else {
+ *msg = StringPrintf("%s set to %s",
+ flag->name(), flag->current_value().c_str());
+ }
+ break;
+ }
+ case SET_FLAGS_DEFAULT: {
+ // modify the flag's default-value
+ if (!TryParseLocked(flag, flag->defvalue_, value, msg))
+ return false;
+ if (!flag->modified_) {
+ // Need to set both defvalue *and* current, in this case
+ TryParseLocked(flag, flag->current_, value, NULL);
+ }
+ break;
+ }
+ default: {
+ // unknown set_mode
+ assert(false);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+// Get the singleton FlagRegistry object
+FlagRegistry* FlagRegistry::global_registry_ = NULL;
+Mutex FlagRegistry::global_registry_lock_(Mutex::LINKER_INITIALIZED);
+
+FlagRegistry* FlagRegistry::GlobalRegistry() {
+ MutexLock acquire_lock(&global_registry_lock_);
+ if (!global_registry_) {
+ global_registry_ = new FlagRegistry;
+ }
+ return global_registry_;
+}
+
+// --------------------------------------------------------------------
+// CommandLineFlagParser
+// Parsing is done in two stages. In the first, we go through
+// argv. For every flag-like arg we can make sense of, we parse
+// it and set the appropriate FLAGS_* variable. For every flag-
+// like arg we can't make sense of, we store it in a vector,
+// along with an explanation of the trouble. In stage 2, we
+// handle the 'reporting' flags like --help and --mpm_version.
+// (This is via a call to HandleCommandLineHelpFlags(), in
+// gflags_reporting.cc.)
+// An optional stage 3 prints out the error messages.
+// This is a bit of a simplification. For instance, --flagfile
+// is handled as soon as it's seen in stage 1, not in stage 2.
+// --------------------------------------------------------------------
+
+class CommandLineFlagParser {
+ public:
+ // The argument is the flag-registry to register the parsed flags in
+ explicit CommandLineFlagParser(FlagRegistry* reg) : registry_(reg) {}
+ ~CommandLineFlagParser() {}
+
+ // Stage 1: Every time this is called, it reads all flags in argv.
+ // However, it ignores all flags that have been successfully set
+ // before. Typically this is only called once, so this 'reparsing'
+ // behavior isn't important. It can be useful when trying to
+ // reparse after loading a dll, though.
+ uint32 ParseNewCommandLineFlags(int* argc, char*** argv, bool remove_flags);
+
+ // Stage 2: print reporting info and exit, if requested.
+ // In gflags_reporting.cc:HandleCommandLineHelpFlags().
+
+ // Stage 3: validate all the commandline flags that have validators
+ // registered.
+ void ValidateAllFlags();
+
+ // Stage 4: report any errors and return true if any were found.
+ bool ReportErrors();
+
+ // Set a particular command line option. "newval" is a string
+ // describing the new value that the option has been set to. If
+ // option_name does not specify a valid option name, or value is not
+ // a valid value for option_name, newval is empty. Does recursive
+ // processing for --flagfile and --fromenv. Returns the new value
+ // if everything went ok, or empty-string if not. (Actually, the
+ // return-string could hold many flag/value pairs due to --flagfile.)
+ // NB: Must have called registry_->Lock() before calling this function.
+ string ProcessSingleOptionLocked(CommandLineFlag* flag,
+ const char* value,
+ FlagSettingMode set_mode);
+
+ // Set a whole batch of command line options as specified by contentdata,
+ // which is in flagfile format (and probably has been read from a flagfile).
+ // Returns the new value if everything went ok, or empty-string if
+ // not. (Actually, the return-string could hold many flag/value
+ // pairs due to --flagfile.)
+ // NB: Must have called registry_->Lock() before calling this function.
+ string ProcessOptionsFromStringLocked(const string& contentdata,
+ FlagSettingMode set_mode);
+
+ // These are the 'recursive' flags, defined at the top of this file.
+ // Whenever we see these flags on the commandline, we must take action.
+ // These are called by ProcessSingleOptionLocked and, similarly, return
+ // new values if everything went ok, or the empty-string if not.
+ string ProcessFlagfileLocked(const string& flagval, FlagSettingMode set_mode);
+ // diff fromenv/tryfromenv
+ string ProcessFromenvLocked(const string& flagval, FlagSettingMode set_mode,
+ bool errors_are_fatal);
+
+ private:
+ FlagRegistry* const registry_;
+ map<string, string> error_flags_; // map from name to error message
+ // This could be a set<string>, but we reuse the map to minimize the .o size
+ map<string, string> undefined_names_; // --[flag] name was not registered
+};
+
+
+// Parse a list of (comma-separated) flags.
+static void ParseFlagList(const char* value, vector<string>* flags) {
+ for (const char *p = value; p && *p; value = p) {
+ p = strchr(value, ',');
+ size_t len;
+ if (p) {
+ len = p - value;
+ p++;
+ } else {
+ len = strlen(value);
+ }
+
+ if (len == 0)
+ ReportError(DIE, "ERROR: empty flaglist entry\n");
+ if (value[0] == '-')
+ ReportError(DIE, "ERROR: flag \"%*s\" begins with '-'\n", len, value);
+
+ flags->push_back(string(value, len));
+ }
+}
+
+// Snarf an entire file into a C++ string. This is just so that we
+// can do all the I/O in one place and not worry about it everywhere.
+// Plus, it's convenient to have the whole file contents at hand.
+// Adds a newline at the end of the file.
+#define PFATAL(s) do { perror(s); gflags_exitfunc(1); } while (0)
+
+static string ReadFileIntoString(const char* filename) {
+ const int kBufSize = 8092;
+ char buffer[kBufSize];
+ string s;
+ FILE* fp;
+ if ((errno = SafeFOpen(&fp, filename, "r")) != 0) PFATAL(filename);
+ size_t n;
+ while ( (n=fread(buffer, 1, kBufSize, fp)) > 0 ) {
+ if (ferror(fp)) PFATAL(filename);
+ s.append(buffer, n);
+ }
+ fclose(fp);
+ return s;
+}
+
+uint32 CommandLineFlagParser::ParseNewCommandLineFlags(int* argc, char*** argv,
+ bool remove_flags) {
+ const char *program_name = strrchr((*argv)[0], PATH_SEPARATOR); // nix path
+ program_name = (program_name == NULL ? (*argv)[0] : program_name+1);
+
+ int first_nonopt = *argc; // for non-options moved to the end
+
+ registry_->Lock();
+ for (int i = 1; i < first_nonopt; i++) {
+ char* arg = (*argv)[i];
+
+ // Like getopt(), we permute non-option flags to be at the end.
+ if (arg[0] != '-' || // must be a program argument
+ (arg[0] == '-' && arg[1] == '\0')) { // "-" is an argument, not a flag
+ memmove((*argv) + i, (*argv) + i+1, (*argc - (i+1)) * sizeof((*argv)[i]));
+ (*argv)[*argc-1] = arg; // we go last
+ first_nonopt--; // we've been pushed onto the stack
+ i--; // to undo the i++ in the loop
+ continue;
+ }
+
+ if (arg[0] == '-') arg++; // allow leading '-'
+ if (arg[0] == '-') arg++; // or leading '--'
+
+ // -- alone means what it does for GNU: stop options parsing
+ if (*arg == '\0') {
+ first_nonopt = i+1;
+ break;
+ }
+
+ // Find the flag object for this option
+ string key;
+ const char* value;
+ string error_message;
+ CommandLineFlag* flag = registry_->SplitArgumentLocked(arg, &key, &value,
+ &error_message);
+ if (flag == NULL) {
+ undefined_names_[key] = ""; // value isn't actually used
+ error_flags_[key] = error_message;
+ continue;
+ }
+
+ if (value == NULL) {
+ // Boolean options are always assigned a value by SplitArgumentLocked()
+ assert(strcmp(flag->type_name(), "bool") != 0);
+ if (i+1 >= first_nonopt) {
+ // This flag needs a value, but there is nothing available
+ error_flags_[key] = (string(kError) + "flag '" + (*argv)[i] + "'"
+ + " is missing its argument");
+ if (flag->help() && flag->help()[0] > '\001') {
+ // Be useful in case we have a non-stripped description.
+ error_flags_[key] += string("; flag description: ") + flag->help();
+ }
+ error_flags_[key] += "\n";
+ break; // we treat this as an unrecoverable error
+ } else {
+ value = (*argv)[++i]; // read next arg for value
+
+ // Heuristic to detect the case where someone treats a string arg
+ // like a bool:
+ // --my_string_var --foo=bar
+ // We look for a flag of string type, whose value begins with a
+ // dash, and where the flag-name and value are separated by a
+ // space rather than an '='.
+ // To avoid false positives, we also require the word "true"
+ // or "false" in the help string. Without this, a valid usage
+ // "-lat -30.5" would trigger the warning. The common cases we
+ // want to solve talk about true and false as values.
+ if (value[0] == '-'
+ && strcmp(flag->type_name(), "string") == 0
+ && (strstr(flag->help(), "true")
+ || strstr(flag->help(), "false"))) {
+ LOG(WARNING) << "Did you really mean to set flag '"
+ << flag->name() << "' to the value '"
+ << value << "'?";
+ }
+ }
+ }
+
+ // TODO(csilvers): only set a flag if we hadn't set it before here
+ ProcessSingleOptionLocked(flag, value, SET_FLAGS_VALUE);
+ }
+ registry_->Unlock();
+
+ if (remove_flags) { // Fix up argc and argv by removing command line flags
+ (*argv)[first_nonopt-1] = (*argv)[0];
+ (*argv) += (first_nonopt-1);
+ (*argc) -= (first_nonopt-1);
+ first_nonopt = 1; // because we still don't count argv[0]
+ }
+
+ logging_is_probably_set_up = true; // because we've parsed --logdir, etc.
+
+ return first_nonopt;
+}
+
+string CommandLineFlagParser::ProcessFlagfileLocked(const string& flagval,
+ FlagSettingMode set_mode) {
+ if (flagval.empty())
+ return "";
+
+ string msg;
+ vector<string> filename_list;
+ ParseFlagList(flagval.c_str(), &filename_list); // take a list of filenames
+ for (size_t i = 0; i < filename_list.size(); ++i) {
+ const char* file = filename_list[i].c_str();
+ msg += ProcessOptionsFromStringLocked(ReadFileIntoString(file), set_mode);
+ }
+ return msg;
+}
+
+string CommandLineFlagParser::ProcessFromenvLocked(const string& flagval,
+ FlagSettingMode set_mode,
+ bool errors_are_fatal) {
+ if (flagval.empty())
+ return "";
+
+ string msg;
+ vector<string> flaglist;
+ ParseFlagList(flagval.c_str(), &flaglist);
+
+ for (size_t i = 0; i < flaglist.size(); ++i) {
+ const char* flagname = flaglist[i].c_str();
+ CommandLineFlag* flag = registry_->FindFlagLocked(flagname);
+ if (flag == NULL) {
+ error_flags_[flagname] =
+ StringPrintf("%sunknown command line flag '%s' "
+ "(via --fromenv or --tryfromenv)\n",
+ kError, flagname);
+ undefined_names_[flagname] = "";
+ continue;
+ }
+
+ const string envname = string("FLAGS_") + string(flagname);
+ string envval;
+ if (!SafeGetEnv(envname.c_str(), envval)) {
+ if (errors_are_fatal) {
+ error_flags_[flagname] = (string(kError) + envname +
+ " not found in environment\n");
+ }
+ continue;
+ }
+
+ // Avoid infinite recursion.
+ if (envval == "fromenv" || envval == "tryfromenv") {
+ error_flags_[flagname] =
+ StringPrintf("%sinfinite recursion on environment flag '%s'\n",
+ kError, envval.c_str());
+ continue;
+ }
+
+ msg += ProcessSingleOptionLocked(flag, envval.c_str(), set_mode);
+ }
+ return msg;
+}
+
+string CommandLineFlagParser::ProcessSingleOptionLocked(
+ CommandLineFlag* flag, const char* value, FlagSettingMode set_mode) {
+ string msg;
+ if (value && !registry_->SetFlagLocked(flag, value, set_mode, &msg)) {
+ error_flags_[flag->name()] = msg;
+ return "";
+ }
+
+ // The recursive flags, --flagfile and --fromenv and --tryfromenv,
+ // must be dealt with as soon as they're seen. They will emit
+ // messages of their own.
+ if (strcmp(flag->name(), "flagfile") == 0) {
+ msg += ProcessFlagfileLocked(FLAGS_flagfile, set_mode);
+
+ } else if (strcmp(flag->name(), "fromenv") == 0) {
+ // last arg indicates envval-not-found is fatal (unlike in --tryfromenv)
+ msg += ProcessFromenvLocked(FLAGS_fromenv, set_mode, true);
+
+ } else if (strcmp(flag->name(), "tryfromenv") == 0) {
+ msg += ProcessFromenvLocked(FLAGS_tryfromenv, set_mode, false);
+ }
+
+ return msg;
+}
+
+void CommandLineFlagParser::ValidateAllFlags() {
+ FlagRegistryLock frl(registry_);
+ for (FlagRegistry::FlagConstIterator i = registry_->flags_.begin();
+ i != registry_->flags_.end(); ++i) {
+ if (!i->second->ValidateCurrent()) {
+ // only set a message if one isn't already there. (If there's
+ // an error message, our job is done, even if it's not exactly
+ // the same error.)
+ if (error_flags_[i->second->name()].empty())
+ error_flags_[i->second->name()] =
+ string(kError) + "--" + i->second->name() +
+ " must be set on the commandline"
+ " (default value fails validation)\n";
+ }
+ }
+}
+
+bool CommandLineFlagParser::ReportErrors() {
+ // error_flags_ indicates errors we saw while parsing.
+ // But we ignore undefined-names if ok'ed by --undef_ok
+ if (!FLAGS_undefok.empty()) {
+ vector<string> flaglist;
+ ParseFlagList(FLAGS_undefok.c_str(), &flaglist);
+ for (size_t i = 0; i < flaglist.size(); ++i) {
+ // We also deal with --no<flag>, in case the flagname was boolean
+ const string no_version = string("no") + flaglist[i];
+ if (undefined_names_.find(flaglist[i]) != undefined_names_.end()) {
+ error_flags_[flaglist[i]] = ""; // clear the error message
+ } else if (undefined_names_.find(no_version) != undefined_names_.end()) {
+ error_flags_[no_version] = "";
+ }
+ }
+ }
+ // Likewise, if they decided to allow reparsing, all undefined-names
+ // are ok; we just silently ignore them now, and hope that a future
+ // parse will pick them up somehow.
+ if (allow_command_line_reparsing) {
+ for (map<string, string>::const_iterator it = undefined_names_.begin();
+ it != undefined_names_.end(); ++it)
+ error_flags_[it->first] = ""; // clear the error message
+ }
+
+ bool found_error = false;
+ string error_message;
+ for (map<string, string>::const_iterator it = error_flags_.begin();
+ it != error_flags_.end(); ++it) {
+ if (!it->second.empty()) {
+ error_message.append(it->second.data(), it->second.size());
+ found_error = true;
+ }
+ }
+ if (found_error)
+ ReportError(DO_NOT_DIE, "%s", error_message.c_str());
+ return found_error;
+}
+
+string CommandLineFlagParser::ProcessOptionsFromStringLocked(
+ const string& contentdata, FlagSettingMode set_mode) {
+ string retval;
+ const char* flagfile_contents = contentdata.c_str();
+ bool flags_are_relevant = true; // set to false when filenames don't match
+ bool in_filename_section = false;
+
+ const char* line_end = flagfile_contents;
+ // We read this file a line at a time.
+ for (; line_end; flagfile_contents = line_end + 1) {
+ while (*flagfile_contents && isspace(*flagfile_contents))
+ ++flagfile_contents;
+ line_end = strchr(flagfile_contents, '\n');
+ size_t len = line_end ? line_end - flagfile_contents
+ : strlen(flagfile_contents);
+ string line(flagfile_contents, len);
+
+ // Each line can be one of four things:
+ // 1) A comment line -- we skip it
+ // 2) An empty line -- we skip it
+ // 3) A list of filenames -- starts a new filenames+flags section
+ // 4) A --flag=value line -- apply if previous filenames match
+ if (line.empty() || line[0] == '#') {
+ // comment or empty line; just ignore
+
+ } else if (line[0] == '-') { // flag
+ in_filename_section = false; // instead, it was a flag-line
+ if (!flags_are_relevant) // skip this flag; applies to someone else
+ continue;
+
+ const char* name_and_val = line.c_str() + 1; // skip the leading -
+ if (*name_and_val == '-')
+ name_and_val++; // skip second - too
+ string key;
+ const char* value;
+ string error_message;
+ CommandLineFlag* flag = registry_->SplitArgumentLocked(name_and_val,
+ &key, &value,
+ &error_message);
+ // By API, errors parsing flagfile lines are silently ignored.
+ if (flag == NULL) {
+ // "WARNING: flagname '" + key + "' not found\n"
+ } else if (value == NULL) {
+ // "WARNING: flagname '" + key + "' missing a value\n"
+ } else {
+ retval += ProcessSingleOptionLocked(flag, value, set_mode);
+ }
+
+ } else { // a filename!
+ if (!in_filename_section) { // start over: assume filenames don't match
+ in_filename_section = true;
+ flags_are_relevant = false;
+ }
+
+ // Split the line up at spaces into glob-patterns
+ const char* space = line.c_str(); // just has to be non-NULL
+ for (const char* word = line.c_str(); *space; word = space+1) {
+ if (flags_are_relevant) // we can stop as soon as we match
+ break;
+ space = strchr(word, ' ');
+ if (space == NULL)
+ space = word + strlen(word);
+ const string glob(word, space - word);
+ // We try matching both against the full argv0 and basename(argv0)
+ if (glob == ProgramInvocationName() // small optimization
+ || glob == ProgramInvocationShortName()
+#if defined(HAVE_FNMATCH_H)
+ || fnmatch(glob.c_str(), ProgramInvocationName(), FNM_PATHNAME) == 0
+ || fnmatch(glob.c_str(), ProgramInvocationShortName(), FNM_PATHNAME) == 0
+#elif defined(HAVE_SHLWAPI_H)
+ || PathMatchSpec(glob.c_str(), ProgramInvocationName())
+ || PathMatchSpec(glob.c_str(), ProgramInvocationShortName())
+#endif
+ ) {
+ flags_are_relevant = true;
+ }
+ }
+ }
+ }
+ return retval;
+}
+
+// --------------------------------------------------------------------
+// GetFromEnv()
+// AddFlagValidator()
+// These are helper functions for routines like BoolFromEnv() and
+// RegisterFlagValidator, defined below. They're defined here so
+// they can live in the unnamed namespace (which makes friendship
+// declarations for these classes possible).
+// --------------------------------------------------------------------
+
+template<typename T>
+T GetFromEnv(const char *varname, const char* type, T dflt) {
+ std::string valstr;
+ if (SafeGetEnv(varname, valstr)) {
+ FlagValue ifv(new T, type, true);
+ if (!ifv.ParseFrom(valstr.c_str())) {
+ ReportError(DIE, "ERROR: error parsing env variable '%s' with value '%s'\n",
+ varname, valstr.c_str());
+ }
+ return OTHER_VALUE_AS(ifv, T);
+ } else return dflt;
+}
+
+bool AddFlagValidator(const void* flag_ptr, ValidateFnProto validate_fn_proto) {
+ // We want a lock around this routine, in case two threads try to
+ // add a validator (hopefully the same one!) at once. We could use
+ // our own thread, but we need to loook at the registry anyway, so
+ // we just steal that one.
+ FlagRegistry* const registry = FlagRegistry::GlobalRegistry();
+ FlagRegistryLock frl(registry);
+ // First, find the flag whose current-flag storage is 'flag'.
+ // This is the CommandLineFlag whose current_->value_buffer_ == flag
+ CommandLineFlag* flag = registry->FindFlagViaPtrLocked(flag_ptr);
+ if (!flag) {
+ LOG(WARNING) << "Ignoring RegisterValidateFunction() for flag pointer "
+ << flag_ptr << ": no flag found at that address";
+ return false;
+ } else if (validate_fn_proto == flag->validate_function()) {
+ return true; // ok to register the same function over and over again
+ } else if (validate_fn_proto != NULL && flag->validate_function() != NULL) {
+ LOG(WARNING) << "Ignoring RegisterValidateFunction() for flag '"
+ << flag->name() << "': validate-fn already registered";
+ return false;
+ } else {
+ flag->validate_fn_proto_ = validate_fn_proto;
+ return true;
+ }
+}
+
+} // end unnamed namespaces
+
+
+// Now define the functions that are exported via the .h file
+
+// --------------------------------------------------------------------
+// FlagRegisterer
+// This class exists merely to have a global constructor (the
+// kind that runs before main(), that goes an initializes each
+// flag that's been declared. Note that it's very important we
+// don't have a destructor that deletes flag_, because that would
+// cause us to delete current_storage/defvalue_storage as well,
+// which can cause a crash if anything tries to access the flag
+// values in a global destructor.
+// --------------------------------------------------------------------
+
+FlagRegisterer::FlagRegisterer(const char* name, const char* type,
+ const char* help, const char* filename,
+ void* current_storage, void* defvalue_storage) {
+ if (help == NULL)
+ help = "";
+ // FlagValue expects the type-name to not include any namespace
+ // components, so we get rid of those, if any.
+ if (strchr(type, ':'))
+ type = strrchr(type, ':') + 1;
+ FlagValue* current = new FlagValue(current_storage, type, false);
+ FlagValue* defvalue = new FlagValue(defvalue_storage, type, false);
+ // Importantly, flag_ will never be deleted, so storage is always good.
+ CommandLineFlag* flag = new CommandLineFlag(name, help, filename,
+ current, defvalue);
+ FlagRegistry::GlobalRegistry()->RegisterFlag(flag); // default registry
+}
+
+// --------------------------------------------------------------------
+// GetAllFlags()
+// The main way the FlagRegistry class exposes its data. This
+// returns, as strings, all the info about all the flags in
+// the main registry, sorted first by filename they are defined
+// in, and then by flagname.
+// --------------------------------------------------------------------
+
+struct FilenameFlagnameCmp {
+ bool operator()(const CommandLineFlagInfo& a,
+ const CommandLineFlagInfo& b) const {
+ int cmp = strcmp(a.filename.c_str(), b.filename.c_str());
+ if (cmp == 0)
+ cmp = strcmp(a.name.c_str(), b.name.c_str()); // secondary sort key
+ return cmp < 0;
+ }
+};
+
+void GetAllFlags(vector<CommandLineFlagInfo>* OUTPUT) {
+ FlagRegistry* const registry = FlagRegistry::GlobalRegistry();
+ registry->Lock();
+ for (FlagRegistry::FlagConstIterator i = registry->flags_.begin();
+ i != registry->flags_.end(); ++i) {
+ CommandLineFlagInfo fi;
+ i->second->FillCommandLineFlagInfo(&fi);
+ OUTPUT->push_back(fi);
+ }
+ registry->Unlock();
+ // Now sort the flags, first by filename they occur in, then alphabetically
+ sort(OUTPUT->begin(), OUTPUT->end(), FilenameFlagnameCmp());
+}
+
+// --------------------------------------------------------------------
+// SetArgv()
+// GetArgvs()
+// GetArgv()
+// GetArgv0()
+// ProgramInvocationName()
+// ProgramInvocationShortName()
+// SetUsageMessage()
+// ProgramUsage()
+// Functions to set and get argv. Typically the setter is called
+// by ParseCommandLineFlags. Also can get the ProgramUsage string,
+// set by SetUsageMessage.
+// --------------------------------------------------------------------
+
+// These values are not protected by a Mutex because they are normally
+// set only once during program startup.
+static const char* argv0 = "UNKNOWN"; // just the program name
+static const char* cmdline = ""; // the entire command-line
+static vector<string> argvs;
+static uint32 argv_sum = 0;
+static const char* program_usage = NULL;
+
+void SetArgv(int argc, const char** argv) {
+ static bool called_set_argv = false;
+ if (called_set_argv) // we already have an argv for you
+ return;
+
+ called_set_argv = true;
+
+ assert(argc > 0); // every program has at least a progname
+ argv0 = strdup(argv[0]); // small memory leak, but fn only called once
+ assert(argv0);
+
+ string cmdline_string; // easier than doing strcats
+ for (int i = 0; i < argc; i++) {
+ if (i != 0) {
+ cmdline_string += " ";
+ }
+ cmdline_string += argv[i];
+ argvs.push_back(argv[i]);
+ }
+ cmdline = strdup(cmdline_string.c_str()); // another small memory leak
+ assert(cmdline);
+
+ // Compute a simple sum of all the chars in argv
+ for (const char* c = cmdline; *c; c++)
+ argv_sum += *c;
+}
+
+const vector<string>& GetArgvs() { return argvs; }
+const char* GetArgv() { return cmdline; }
+const char* GetArgv0() { return argv0; }
+uint32 GetArgvSum() { return argv_sum; }
+const char* ProgramInvocationName() { // like the GNU libc fn
+ return GetArgv0();
+}
+const char* ProgramInvocationShortName() { // like the GNU libc fn
+ const char* slash = strrchr(argv0, '/');
+#ifdef OS_WINDOWS
+ if (!slash) slash = strrchr(argv0, '\\');
+#endif
+ return slash ? slash + 1 : argv0;
+}
+
+void SetUsageMessage(const string& usage) {
+ if (program_usage != NULL)
+ ReportError(DIE, "ERROR: SetUsageMessage() called twice\n");
+ program_usage = strdup(usage.c_str()); // small memory leak
+}
+
+const char* ProgramUsage() {
+ if (program_usage) {
+ return program_usage;
+ }
+ return "Warning: SetUsageMessage() never called";
+}
+
+// --------------------------------------------------------------------
+// SetVersionString()
+// VersionString()
+// --------------------------------------------------------------------
+
+static const char* version_string = NULL;
+
+void SetVersionString(const string& version) {
+ if (version_string != NULL)
+ ReportError(DIE, "ERROR: SetVersionString() called twice\n");
+ version_string = strdup(version.c_str()); // small memory leak
+}
+
+const char* VersionString() {
+ return version_string ? version_string : "";
+}
+
+
+// --------------------------------------------------------------------
+// GetCommandLineOption()
+// GetCommandLineFlagInfo()
+// GetCommandLineFlagInfoOrDie()
+// SetCommandLineOption()
+// SetCommandLineOptionWithMode()
+// The programmatic way to set a flag's value, using a string
+// for its name rather than the variable itself (that is,
+// SetCommandLineOption("foo", x) rather than FLAGS_foo = x).
+// There's also a bit more flexibility here due to the various
+// set-modes, but typically these are used when you only have
+// that flag's name as a string, perhaps at runtime.
+// All of these work on the default, global registry.
+// For GetCommandLineOption, return false if no such flag
+// is known, true otherwise. We clear "value" if a suitable
+// flag is found.
+// --------------------------------------------------------------------
+
+
+bool GetCommandLineOption(const char* name, string* value) {
+ if (NULL == name)
+ return false;
+ assert(value);
+
+ FlagRegistry* const registry = FlagRegistry::GlobalRegistry();
+ FlagRegistryLock frl(registry);
+ CommandLineFlag* flag = registry->FindFlagLocked(name);
+ if (flag == NULL) {
+ return false;
+ } else {
+ *value = flag->current_value();
+ return true;
+ }
+}
+
+bool GetCommandLineFlagInfo(const char* name, CommandLineFlagInfo* OUTPUT) {
+ if (NULL == name) return false;
+ FlagRegistry* const registry = FlagRegistry::GlobalRegistry();
+ FlagRegistryLock frl(registry);
+ CommandLineFlag* flag = registry->FindFlagLocked(name);
+ if (flag == NULL) {
+ return false;
+ } else {
+ assert(OUTPUT);
+ flag->FillCommandLineFlagInfo(OUTPUT);
+ return true;
+ }
+}
+
+CommandLineFlagInfo GetCommandLineFlagInfoOrDie(const char* name) {
+ CommandLineFlagInfo info;
+ if (!GetCommandLineFlagInfo(name, &info)) {
+ fprintf(stderr, "FATAL ERROR: flag name '%s' doesn't exist\n", name);
+ gflags_exitfunc(1); // almost certainly gflags_exitfunc()
+ }
+ return info;
+}
+
+string SetCommandLineOptionWithMode(const char* name, const char* value,
+ FlagSettingMode set_mode) {
+ string result;
+ FlagRegistry* const registry = FlagRegistry::GlobalRegistry();
+ FlagRegistryLock frl(registry);
+ CommandLineFlag* flag = registry->FindFlagLocked(name);
+ if (flag) {
+ CommandLineFlagParser parser(registry);
+ result = parser.ProcessSingleOptionLocked(flag, value, set_mode);
+ if (!result.empty()) { // in the error case, we've already logged
+ // Could consider logging this change
+ }
+ }
+ // The API of this function is that we return empty string on error
+ return result;
+}
+
+string SetCommandLineOption(const char* name, const char* value) {
+ return SetCommandLineOptionWithMode(name, value, SET_FLAGS_VALUE);
+}
+
+// --------------------------------------------------------------------
+// FlagSaver
+// FlagSaverImpl
+// This class stores the states of all flags at construct time,
+// and restores all flags to that state at destruct time.
+// Its major implementation challenge is that it never modifies
+// pointers in the 'main' registry, so global FLAG_* vars always
+// point to the right place.
+// --------------------------------------------------------------------
+
+class FlagSaverImpl {
+ public:
+ // Constructs an empty FlagSaverImpl object.
+ explicit FlagSaverImpl(FlagRegistry* main_registry)
+ : main_registry_(main_registry) { }
+ ~FlagSaverImpl() {
+ // reclaim memory from each of our CommandLineFlags
+ vector<CommandLineFlag*>::const_iterator it;
+ for (it = backup_registry_.begin(); it != backup_registry_.end(); ++it)
+ delete *it;
+ }
+
+ // Saves the flag states from the flag registry into this object.
+ // It's an error to call this more than once.
+ // Must be called when the registry mutex is not held.
+ void SaveFromRegistry() {
+ FlagRegistryLock frl(main_registry_);
+ assert(backup_registry_.empty()); // call only once!
+ for (FlagRegistry::FlagConstIterator it = main_registry_->flags_.begin();
+ it != main_registry_->flags_.end();
+ ++it) {
+ const CommandLineFlag* main = it->second;
+ // Sets up all the const variables in backup correctly
+ CommandLineFlag* backup = new CommandLineFlag(
+ main->name(), main->help(), main->filename(),
+ main->current_->New(), main->defvalue_->New());
+ // Sets up all the non-const variables in backup correctly
+ backup->CopyFrom(*main);
+ backup_registry_.push_back(backup); // add it to a convenient list
+ }
+ }
+
+ // Restores the saved flag states into the flag registry. We
+ // assume no flags were added or deleted from the registry since
+ // the SaveFromRegistry; if they were, that's trouble! Must be
+ // called when the registry mutex is not held.
+ void RestoreToRegistry() {
+ FlagRegistryLock frl(main_registry_);
+ vector<CommandLineFlag*>::const_iterator it;
+ for (it = backup_registry_.begin(); it != backup_registry_.end(); ++it) {
+ CommandLineFlag* main = main_registry_->FindFlagLocked((*it)->name());
+ if (main != NULL) { // if NULL, flag got deleted from registry(!)
+ main->CopyFrom(**it);
+ }
+ }
+ }
+
+ private:
+ FlagRegistry* const main_registry_;
+ vector<CommandLineFlag*> backup_registry_;
+
+ FlagSaverImpl(const FlagSaverImpl&); // no copying!
+ void operator=(const FlagSaverImpl&);
+};
+
+FlagSaver::FlagSaver()
+ : impl_(new FlagSaverImpl(FlagRegistry::GlobalRegistry())) {
+ impl_->SaveFromRegistry();
+}
+
+FlagSaver::~FlagSaver() {
+ impl_->RestoreToRegistry();
+ delete impl_;
+}
+
+
+// --------------------------------------------------------------------
+// CommandlineFlagsIntoString()
+// ReadFlagsFromString()
+// AppendFlagsIntoFile()
+// ReadFromFlagsFile()
+// These are mostly-deprecated routines that stick the
+// commandline flags into a file/string and read them back
+// out again. I can see a use for CommandlineFlagsIntoString,
+// for creating a flagfile, but the rest don't seem that useful
+// -- some, I think, are a poor-man's attempt at FlagSaver --
+// and are included only until we can delete them from callers.
+// Note they don't save --flagfile flags (though they do save
+// the result of having called the flagfile, of course).
+// --------------------------------------------------------------------
+
+static string TheseCommandlineFlagsIntoString(
+ const vector<CommandLineFlagInfo>& flags) {
+ vector<CommandLineFlagInfo>::const_iterator i;
+
+ size_t retval_space = 0;
+ for (i = flags.begin(); i != flags.end(); ++i) {
+ // An (over)estimate of how much space it will take to print this flag
+ retval_space += i->name.length() + i->current_value.length() + 5;
+ }
+
+ string retval;
+ retval.reserve(retval_space);
+ for (i = flags.begin(); i != flags.end(); ++i) {
+ retval += "--";
+ retval += i->name;
+ retval += "=";
+ retval += i->current_value;
+ retval += "\n";
+ }
+ return retval;
+}
+
+string CommandlineFlagsIntoString() {
+ vector<CommandLineFlagInfo> sorted_flags;
+ GetAllFlags(&sorted_flags);
+ return TheseCommandlineFlagsIntoString(sorted_flags);
+}
+
+bool ReadFlagsFromString(const string& flagfilecontents,
+ const char* /*prog_name*/, // TODO(csilvers): nix this
+ bool errors_are_fatal) {
+ FlagRegistry* const registry = FlagRegistry::GlobalRegistry();
+ FlagSaverImpl saved_states(registry);
+ saved_states.SaveFromRegistry();
+
+ CommandLineFlagParser parser(registry);
+ registry->Lock();
+ parser.ProcessOptionsFromStringLocked(flagfilecontents, SET_FLAGS_VALUE);
+ registry->Unlock();
+ // Should we handle --help and such when reading flags from a string? Sure.
+ HandleCommandLineHelpFlags();
+ if (parser.ReportErrors()) {
+ // Error. Restore all global flags to their previous values.
+ if (errors_are_fatal)
+ gflags_exitfunc(1);
+ saved_states.RestoreToRegistry();
+ return false;
+ }
+ return true;
+}
+
+// TODO(csilvers): nix prog_name in favor of ProgramInvocationShortName()
+bool AppendFlagsIntoFile(const string& filename, const char *prog_name) {
+ FILE *fp;
+ if (SafeFOpen(&fp, filename.c_str(), "a") != 0) {
+ return false;
+ }
+
+ if (prog_name)
+ fprintf(fp, "%s\n", prog_name);
+
+ vector<CommandLineFlagInfo> flags;
+ GetAllFlags(&flags);
+ // But we don't want --flagfile, which leads to weird recursion issues
+ vector<CommandLineFlagInfo>::iterator i;
+ for (i = flags.begin(); i != flags.end(); ++i) {
+ if (strcmp(i->name.c_str(), "flagfile") == 0) {
+ flags.erase(i);
+ break;
+ }
+ }
+ fprintf(fp, "%s", TheseCommandlineFlagsIntoString(flags).c_str());
+
+ fclose(fp);
+ return true;
+}
+
+bool ReadFromFlagsFile(const string& filename, const char* prog_name,
+ bool errors_are_fatal) {
+ return ReadFlagsFromString(ReadFileIntoString(filename.c_str()),
+ prog_name, errors_are_fatal);
+}
+
+
+// --------------------------------------------------------------------
+// BoolFromEnv()
+// Int32FromEnv()
+// Int64FromEnv()
+// Uint64FromEnv()
+// DoubleFromEnv()
+// StringFromEnv()
+// Reads the value from the environment and returns it.
+// We use an FlagValue to make the parsing easy.
+// Example usage:
+// DEFINE_bool(myflag, BoolFromEnv("MYFLAG_DEFAULT", false), "whatever");
+// --------------------------------------------------------------------
+
+bool BoolFromEnv(const char *v, bool dflt) {
+ return GetFromEnv(v, "bool", dflt);
+}
+int32 Int32FromEnv(const char *v, int32 dflt) {
+ return GetFromEnv(v, "int32", dflt);
+}
+int64 Int64FromEnv(const char *v, int64 dflt) {
+ return GetFromEnv(v, "int64", dflt);
+}
+uint64 Uint64FromEnv(const char *v, uint64 dflt) {
+ return GetFromEnv(v, "uint64", dflt);
+}
+double DoubleFromEnv(const char *v, double dflt) {
+ return GetFromEnv(v, "double", dflt);
+}
+
+#ifdef _MSC_VER
+# pragma warning(push)
+# pragma warning(disable: 4996) // ignore getenv security warning
+#endif
+const char *StringFromEnv(const char *varname, const char *dflt) {
+ const char* const val = getenv(varname);
+ return val ? val : dflt;
+}
+#ifdef _MSC_VER
+# pragma warning(pop)
+#endif
+
+
+// --------------------------------------------------------------------
+// RegisterFlagValidator()
+// RegisterFlagValidator() is the function that clients use to
+// 'decorate' a flag with a validation function. Once this is
+// done, every time the flag is set (including when the flag
+// is parsed from argv), the validator-function is called.
+// These functions return true if the validator was added
+// successfully, or false if not: the flag already has a validator,
+// (only one allowed per flag), the 1st arg isn't a flag, etc.
+// This function is not thread-safe.
+// --------------------------------------------------------------------
+
+bool RegisterFlagValidator(const bool* flag,
+ bool (*validate_fn)(const char*, bool)) {
+ return AddFlagValidator(flag, reinterpret_cast<ValidateFnProto>(validate_fn));
+}
+bool RegisterFlagValidator(const int32* flag,
+ bool (*validate_fn)(const char*, int32)) {
+ return AddFlagValidator(flag, reinterpret_cast<ValidateFnProto>(validate_fn));
+}
+bool RegisterFlagValidator(const int64* flag,
+ bool (*validate_fn)(const char*, int64)) {
+ return AddFlagValidator(flag, reinterpret_cast<ValidateFnProto>(validate_fn));
+}
+bool RegisterFlagValidator(const uint64* flag,
+ bool (*validate_fn)(const char*, uint64)) {
+ return AddFlagValidator(flag, reinterpret_cast<ValidateFnProto>(validate_fn));
+}
+bool RegisterFlagValidator(const double* flag,
+ bool (*validate_fn)(const char*, double)) {
+ return AddFlagValidator(flag, reinterpret_cast<ValidateFnProto>(validate_fn));
+}
+bool RegisterFlagValidator(const string* flag,
+ bool (*validate_fn)(const char*, const string&)) {
+ return AddFlagValidator(flag, reinterpret_cast<ValidateFnProto>(validate_fn));
+}
+
+
+// --------------------------------------------------------------------
+// ParseCommandLineFlags()
+// ParseCommandLineNonHelpFlags()
+// HandleCommandLineHelpFlags()
+// This is the main function called from main(), to actually
+// parse the commandline. It modifies argc and argv as described
+// at the top of gflags.h. You can also divide this
+// function into two parts, if you want to do work between
+// the parsing of the flags and the printing of any help output.
+// --------------------------------------------------------------------
+
+static uint32 ParseCommandLineFlagsInternal(int* argc, char*** argv,
+ bool remove_flags, bool do_report) {
+ SetArgv(*argc, const_cast<const char**>(*argv)); // save it for later
+
+ FlagRegistry* const registry = FlagRegistry::GlobalRegistry();
+ CommandLineFlagParser parser(registry);
+
+ // When we parse the commandline flags, we'll handle --flagfile,
+ // --tryfromenv, etc. as we see them (since flag-evaluation order
+ // may be important). But sometimes apps set FLAGS_tryfromenv/etc.
+ // manually before calling ParseCommandLineFlags. We want to evaluate
+ // those too, as if they were the first flags on the commandline.
+ registry->Lock();
+ parser.ProcessFlagfileLocked(FLAGS_flagfile, SET_FLAGS_VALUE);
+ // Last arg here indicates whether flag-not-found is a fatal error or not
+ parser.ProcessFromenvLocked(FLAGS_fromenv, SET_FLAGS_VALUE, true);
+ parser.ProcessFromenvLocked(FLAGS_tryfromenv, SET_FLAGS_VALUE, false);
+ registry->Unlock();
+
+ // Now get the flags specified on the commandline
+ const int r = parser.ParseNewCommandLineFlags(argc, argv, remove_flags);
+
+ if (do_report)
+ HandleCommandLineHelpFlags(); // may cause us to exit on --help, etc.
+
+ // See if any of the unset flags fail their validation checks
+ parser.ValidateAllFlags();
+
+ if (parser.ReportErrors()) // may cause us to exit on illegal flags
+ gflags_exitfunc(1);
+ return r;
+}
+
+uint32 ParseCommandLineFlags(int* argc, char*** argv, bool remove_flags) {
+ return ParseCommandLineFlagsInternal(argc, argv, remove_flags, true);
+}
+
+uint32 ParseCommandLineNonHelpFlags(int* argc, char*** argv,
+ bool remove_flags) {
+ return ParseCommandLineFlagsInternal(argc, argv, remove_flags, false);
+}
+
+// --------------------------------------------------------------------
+// AllowCommandLineReparsing()
+// ReparseCommandLineNonHelpFlags()
+// This is most useful for shared libraries. The idea is if
+// a flag is defined in a shared library that is dlopen'ed
+// sometime after main(), you can ParseCommandLineFlags before
+// the dlopen, then ReparseCommandLineNonHelpFlags() after the
+// dlopen, to get the new flags. But you have to explicitly
+// Allow() it; otherwise, you get the normal default behavior
+// of unrecognized flags calling a fatal error.
+// TODO(csilvers): this isn't used. Just delete it?
+// --------------------------------------------------------------------
+
+void AllowCommandLineReparsing() {
+ allow_command_line_reparsing = true;
+}
+
+void ReparseCommandLineNonHelpFlags() {
+ // We make a copy of argc and argv to pass in
+ const vector<string>& argvs = GetArgvs();
+ int tmp_argc = static_cast<int>(argvs.size());
+ char** tmp_argv = new char* [tmp_argc + 1];
+ for (int i = 0; i < tmp_argc; ++i)
+ tmp_argv[i] = strdup(argvs[i].c_str()); // TODO(csilvers): don't dup
+
+ ParseCommandLineNonHelpFlags(&tmp_argc, &tmp_argv, false);
+
+ for (int i = 0; i < tmp_argc; ++i)
+ free(tmp_argv[i]);
+ delete[] tmp_argv;
+}
+
+void ShutDownCommandLineFlags() {
+ FlagRegistry::DeleteGlobalRegistry();
+}
+
+
+} // namespace GFLAGS_NAMESPACE
diff --git a/third_party/gflags/src/gflags.h.in b/third_party/gflags/src/gflags.h.in
new file mode 100644
index 0000000..0324d39
--- /dev/null
+++ b/third_party/gflags/src/gflags.h.in
@@ -0,0 +1,572 @@
+// Copyright (c) 2006, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// ---
+// Revamped and reorganized by Craig Silverstein
+//
+// This is the file that should be included by any file which declares
+// or defines a command line flag or wants to parse command line flags
+// or print a program usage message (which will include information about
+// flags). Executive summary, in the form of an example foo.cc file:
+//
+// #include "foo.h" // foo.h has a line "DECLARE_int32(start);"
+// #include "validators.h" // hypothetical file defining ValidateIsFile()
+//
+// DEFINE_int32(end, 1000, "The last record to read");
+//
+// DEFINE_string(filename, "my_file.txt", "The file to read");
+// // Crash if the specified file does not exist.
+// static bool dummy = RegisterFlagValidator(&FLAGS_filename,
+// &ValidateIsFile);
+//
+// DECLARE_bool(verbose); // some other file has a DEFINE_bool(verbose, ...)
+//
+// void MyFunc() {
+// if (FLAGS_verbose) printf("Records %d-%d\n", FLAGS_start, FLAGS_end);
+// }
+//
+// Then, at the command-line:
+// ./foo --noverbose --start=5 --end=100
+//
+// For more details, see
+// doc/gflags.html
+//
+// --- A note about thread-safety:
+//
+// We describe many functions in this routine as being thread-hostile,
+// thread-compatible, or thread-safe. Here are the meanings we use:
+//
+// thread-safe: it is safe for multiple threads to call this routine
+// (or, when referring to a class, methods of this class)
+// concurrently.
+// thread-hostile: it is not safe for multiple threads to call this
+// routine (or methods of this class) concurrently. In gflags,
+// most thread-hostile routines are intended to be called early in,
+// or even before, main() -- that is, before threads are spawned.
+// thread-compatible: it is safe for multiple threads to read from
+// this variable (when applied to variables), or to call const
+// methods of this class (when applied to classes), as long as no
+// other thread is writing to the variable or calling non-const
+// methods of this class.
+
+#ifndef GFLAGS_GFLAGS_H_
+#define GFLAGS_GFLAGS_H_
+
+#include <string>
+#include <vector>
+
+#include "gflags_declare.h" // IWYU pragma: export
+
+
+// We always want to export variables defined in user code
+#ifndef GFLAGS_DLL_DEFINE_FLAG
+# ifdef _MSC_VER
+# define GFLAGS_DLL_DEFINE_FLAG __declspec(dllexport)
+# else
+# define GFLAGS_DLL_DEFINE_FLAG
+# endif
+#endif
+
+
+namespace GFLAGS_NAMESPACE {
+
+
+// --------------------------------------------------------------------
+// To actually define a flag in a file, use DEFINE_bool,
+// DEFINE_string, etc. at the bottom of this file. You may also find
+// it useful to register a validator with the flag. This ensures that
+// when the flag is parsed from the commandline, or is later set via
+// SetCommandLineOption, we call the validation function. It is _not_
+// called when you assign the value to the flag directly using the = operator.
+//
+// The validation function should return true if the flag value is valid, and
+// false otherwise. If the function returns false for the new setting of the
+// flag, the flag will retain its current value. If it returns false for the
+// default value, ParseCommandLineFlags() will die.
+//
+// This function is safe to call at global construct time (as in the
+// example below).
+//
+// Example use:
+// static bool ValidatePort(const char* flagname, int32 value) {
+// if (value > 0 && value < 32768) // value is ok
+// return true;
+// printf("Invalid value for --%s: %d\n", flagname, (int)value);
+// return false;
+// }
+// DEFINE_int32(port, 0, "What port to listen on");
+// static bool dummy = RegisterFlagValidator(&FLAGS_port, &ValidatePort);
+
+// Returns true if successfully registered, false if not (because the
+// first argument doesn't point to a command-line flag, or because a
+// validator is already registered for this flag).
+extern GFLAGS_DLL_DECL bool RegisterFlagValidator(const bool* flag, bool (*validate_fn)(const char*, bool));
+extern GFLAGS_DLL_DECL bool RegisterFlagValidator(const int32* flag, bool (*validate_fn)(const char*, int32));
+extern GFLAGS_DLL_DECL bool RegisterFlagValidator(const int64* flag, bool (*validate_fn)(const char*, int64));
+extern GFLAGS_DLL_DECL bool RegisterFlagValidator(const uint64* flag, bool (*validate_fn)(const char*, uint64));
+extern GFLAGS_DLL_DECL bool RegisterFlagValidator(const double* flag, bool (*validate_fn)(const char*, double));
+extern GFLAGS_DLL_DECL bool RegisterFlagValidator(const std::string* flag, bool (*validate_fn)(const char*, const std::string&));
+
+// Convenience macro for the registration of a flag validator
+#define DEFINE_validator(name, validator) \
+ static const bool name##_validator_registered = \
+ GFLAGS_NAMESPACE::RegisterFlagValidator(&FLAGS_##name, validator)
+
+
+// --------------------------------------------------------------------
+// These methods are the best way to get access to info about the
+// list of commandline flags. Note that these routines are pretty slow.
+// GetAllFlags: mostly-complete info about the list, sorted by file.
+// ShowUsageWithFlags: pretty-prints the list to stdout (what --help does)
+// ShowUsageWithFlagsRestrict: limit to filenames with restrict as a substr
+//
+// In addition to accessing flags, you can also access argv[0] (the program
+// name) and argv (the entire commandline), which we sock away a copy of.
+// These variables are static, so you should only set them once.
+//
+// No need to export this data only structure from DLL, avoiding VS warning 4251.
+struct CommandLineFlagInfo {
+ std::string name; // the name of the flag
+ std::string type; // the type of the flag: int32, etc
+ std::string description; // the "help text" associated with the flag
+ std::string current_value; // the current value, as a string
+ std::string default_value; // the default value, as a string
+ std::string filename; // 'cleaned' version of filename holding the flag
+ bool has_validator_fn; // true if RegisterFlagValidator called on this flag
+ bool is_default; // true if the flag has the default value and
+ // has not been set explicitly from the cmdline
+ // or via SetCommandLineOption
+ const void* flag_ptr; // pointer to the flag's current value (i.e. FLAGS_foo)
+};
+
+// Using this inside of a validator is a recipe for a deadlock.
+// TODO(user) Fix locking when validators are running, to make it safe to
+// call validators during ParseAllFlags.
+// Also make sure then to uncomment the corresponding unit test in
+// gflags_unittest.sh
+extern GFLAGS_DLL_DECL void GetAllFlags(std::vector<CommandLineFlagInfo>* OUTPUT);
+// These two are actually defined in gflags_reporting.cc.
+extern GFLAGS_DLL_DECL void ShowUsageWithFlags(const char *argv0); // what --help does
+extern GFLAGS_DLL_DECL void ShowUsageWithFlagsRestrict(const char *argv0, const char *restrict);
+
+// Create a descriptive string for a flag.
+// Goes to some trouble to make pretty line breaks.
+extern GFLAGS_DLL_DECL std::string DescribeOneFlag(const CommandLineFlagInfo& flag);
+
+// Thread-hostile; meant to be called before any threads are spawned.
+extern GFLAGS_DLL_DECL void SetArgv(int argc, const char** argv);
+
+// The following functions are thread-safe as long as SetArgv() is
+// only called before any threads start.
+extern GFLAGS_DLL_DECL const std::vector<std::string>& GetArgvs();
+extern GFLAGS_DLL_DECL const char* GetArgv(); // all of argv as a string
+extern GFLAGS_DLL_DECL const char* GetArgv0(); // only argv0
+extern GFLAGS_DLL_DECL uint32 GetArgvSum(); // simple checksum of argv
+extern GFLAGS_DLL_DECL const char* ProgramInvocationName(); // argv0, or "UNKNOWN" if not set
+extern GFLAGS_DLL_DECL const char* ProgramInvocationShortName(); // basename(argv0)
+
+// ProgramUsage() is thread-safe as long as SetUsageMessage() is only
+// called before any threads start.
+extern GFLAGS_DLL_DECL const char* ProgramUsage(); // string set by SetUsageMessage()
+
+// VersionString() is thread-safe as long as SetVersionString() is only
+// called before any threads start.
+extern GFLAGS_DLL_DECL const char* VersionString(); // string set by SetVersionString()
+
+
+
+// --------------------------------------------------------------------
+// Normally you access commandline flags by just saying "if (FLAGS_foo)"
+// or whatever, and set them by calling "FLAGS_foo = bar" (or, more
+// commonly, via the DEFINE_foo macro). But if you need a bit more
+// control, we have programmatic ways to get/set the flags as well.
+// These programmatic ways to access flags are thread-safe, but direct
+// access is only thread-compatible.
+
+// Return true iff the flagname was found.
+// OUTPUT is set to the flag's value, or unchanged if we return false.
+extern GFLAGS_DLL_DECL bool GetCommandLineOption(const char* name, std::string* OUTPUT);
+
+// Return true iff the flagname was found. OUTPUT is set to the flag's
+// CommandLineFlagInfo or unchanged if we return false.
+extern GFLAGS_DLL_DECL bool GetCommandLineFlagInfo(const char* name, CommandLineFlagInfo* OUTPUT);
+
+// Return the CommandLineFlagInfo of the flagname. exit() if name not found.
+// Example usage, to check if a flag's value is currently the default value:
+// if (GetCommandLineFlagInfoOrDie("foo").is_default) ...
+extern GFLAGS_DLL_DECL CommandLineFlagInfo GetCommandLineFlagInfoOrDie(const char* name);
+
+enum GFLAGS_DLL_DECL FlagSettingMode {
+ // update the flag's value (can call this multiple times).
+ SET_FLAGS_VALUE,
+ // update the flag's value, but *only if* it has not yet been updated
+ // with SET_FLAGS_VALUE, SET_FLAG_IF_DEFAULT, or "FLAGS_xxx = nondef".
+ SET_FLAG_IF_DEFAULT,
+ // set the flag's default value to this. If the flag has not yet updated
+ // yet (via SET_FLAGS_VALUE, SET_FLAG_IF_DEFAULT, or "FLAGS_xxx = nondef")
+ // change the flag's current value to the new default value as well.
+ SET_FLAGS_DEFAULT
+};
+
+// Set a particular flag ("command line option"). Returns a string
+// describing the new value that the option has been set to. The
+// return value API is not well-specified, so basically just depend on
+// it to be empty if the setting failed for some reason -- the name is
+// not a valid flag name, or the value is not a valid value -- and
+// non-empty else.
+
+// SetCommandLineOption uses set_mode == SET_FLAGS_VALUE (the common case)
+extern GFLAGS_DLL_DECL std::string SetCommandLineOption (const char* name, const char* value);
+extern GFLAGS_DLL_DECL std::string SetCommandLineOptionWithMode(const char* name, const char* value, FlagSettingMode set_mode);
+
+
+// --------------------------------------------------------------------
+// Saves the states (value, default value, whether the user has set
+// the flag, registered validators, etc) of all flags, and restores
+// them when the FlagSaver is destroyed. This is very useful in
+// tests, say, when you want to let your tests change the flags, but
+// make sure that they get reverted to the original states when your
+// test is complete.
+//
+// Example usage:
+// void TestFoo() {
+// FlagSaver s1;
+// FLAG_foo = false;
+// FLAG_bar = "some value";
+//
+// // test happens here. You can return at any time
+// // without worrying about restoring the FLAG values.
+// }
+//
+// Note: This class is marked with GFLAGS_ATTRIBUTE_UNUSED because all
+// the work is done in the constructor and destructor, so in the standard
+// usage example above, the compiler would complain that it's an
+// unused variable.
+//
+// This class is thread-safe. However, its destructor writes to
+// exactly the set of flags that have changed value during its
+// lifetime, so concurrent _direct_ access to those flags
+// (i.e. FLAGS_foo instead of {Get,Set}CommandLineOption()) is unsafe.
+
+class GFLAGS_DLL_DECL FlagSaver {
+ public:
+ FlagSaver();
+ ~FlagSaver();
+
+ private:
+ class FlagSaverImpl* impl_; // we use pimpl here to keep API steady
+
+ FlagSaver(const FlagSaver&); // no copying!
+ void operator=(const FlagSaver&);
+}@GFLAGS_ATTRIBUTE_UNUSED@;
+
+// --------------------------------------------------------------------
+// Some deprecated or hopefully-soon-to-be-deprecated functions.
+
+// This is often used for logging. TODO(csilvers): figure out a better way
+extern GFLAGS_DLL_DECL std::string CommandlineFlagsIntoString();
+// Usually where this is used, a FlagSaver should be used instead.
+extern GFLAGS_DLL_DECL
+bool ReadFlagsFromString(const std::string& flagfilecontents,
+ const char* prog_name,
+ bool errors_are_fatal); // uses SET_FLAGS_VALUE
+
+// These let you manually implement --flagfile functionality.
+// DEPRECATED.
+extern GFLAGS_DLL_DECL bool AppendFlagsIntoFile(const std::string& filename, const char* prog_name);
+extern GFLAGS_DLL_DECL bool ReadFromFlagsFile(const std::string& filename, const char* prog_name, bool errors_are_fatal); // uses SET_FLAGS_VALUE
+
+
+// --------------------------------------------------------------------
+// Useful routines for initializing flags from the environment.
+// In each case, if 'varname' does not exist in the environment
+// return defval. If 'varname' does exist but is not valid
+// (e.g., not a number for an int32 flag), abort with an error.
+// Otherwise, return the value. NOTE: for booleans, for true use
+// 't' or 'T' or 'true' or '1', for false 'f' or 'F' or 'false' or '0'.
+
+extern GFLAGS_DLL_DECL bool BoolFromEnv(const char *varname, bool defval);
+extern GFLAGS_DLL_DECL int32 Int32FromEnv(const char *varname, int32 defval);
+extern GFLAGS_DLL_DECL int64 Int64FromEnv(const char *varname, int64 defval);
+extern GFLAGS_DLL_DECL uint64 Uint64FromEnv(const char *varname, uint64 defval);
+extern GFLAGS_DLL_DECL double DoubleFromEnv(const char *varname, double defval);
+extern GFLAGS_DLL_DECL const char *StringFromEnv(const char *varname, const char *defval);
+
+
+// --------------------------------------------------------------------
+// The next two functions parse gflags from main():
+
+// Set the "usage" message for this program. For example:
+// string usage("This program does nothing. Sample usage:\n");
+// usage += argv[0] + " <uselessarg1> <uselessarg2>";
+// SetUsageMessage(usage);
+// Do not include commandline flags in the usage: we do that for you!
+// Thread-hostile; meant to be called before any threads are spawned.
+extern GFLAGS_DLL_DECL void SetUsageMessage(const std::string& usage);
+
+// Sets the version string, which is emitted with --version.
+// For instance: SetVersionString("1.3");
+// Thread-hostile; meant to be called before any threads are spawned.
+extern GFLAGS_DLL_DECL void SetVersionString(const std::string& version);
+
+
+// Looks for flags in argv and parses them. Rearranges argv to put
+// flags first, or removes them entirely if remove_flags is true.
+// If a flag is defined more than once in the command line or flag
+// file, the last definition is used. Returns the index (into argv)
+// of the first non-flag argument.
+// See top-of-file for more details on this function.
+#ifndef SWIG // In swig, use ParseCommandLineFlagsScript() instead.
+extern GFLAGS_DLL_DECL uint32 ParseCommandLineFlags(int *argc, char*** argv, bool remove_flags);
+#endif
+
+
+// Calls to ParseCommandLineNonHelpFlags and then to
+// HandleCommandLineHelpFlags can be used instead of a call to
+// ParseCommandLineFlags during initialization, in order to allow for
+// changing default values for some FLAGS (via
+// e.g. SetCommandLineOptionWithMode calls) between the time of
+// command line parsing and the time of dumping help information for
+// the flags as a result of command line parsing. If a flag is
+// defined more than once in the command line or flag file, the last
+// definition is used. Returns the index (into argv) of the first
+// non-flag argument. (If remove_flags is true, will always return 1.)
+extern GFLAGS_DLL_DECL uint32 ParseCommandLineNonHelpFlags(int *argc, char*** argv, bool remove_flags);
+
+// This is actually defined in gflags_reporting.cc.
+// This function is misnamed (it also handles --version, etc.), but
+// it's too late to change that now. :-(
+extern GFLAGS_DLL_DECL void HandleCommandLineHelpFlags(); // in gflags_reporting.cc
+
+// Allow command line reparsing. Disables the error normally
+// generated when an unknown flag is found, since it may be found in a
+// later parse. Thread-hostile; meant to be called before any threads
+// are spawned.
+extern GFLAGS_DLL_DECL void AllowCommandLineReparsing();
+
+// Reparse the flags that have not yet been recognized. Only flags
+// registered since the last parse will be recognized. Any flag value
+// must be provided as part of the argument using "=", not as a
+// separate command line argument that follows the flag argument.
+// Intended for handling flags from dynamically loaded libraries,
+// since their flags are not registered until they are loaded.
+extern GFLAGS_DLL_DECL void ReparseCommandLineNonHelpFlags();
+
+// Clean up memory allocated by flags. This is only needed to reduce
+// the quantity of "potentially leaked" reports emitted by memory
+// debugging tools such as valgrind. It is not required for normal
+// operation, or for the google perftools heap-checker. It must only
+// be called when the process is about to exit, and all threads that
+// might access flags are quiescent. Referencing flags after this is
+// called will have unexpected consequences. This is not safe to run
+// when multiple threads might be running: the function is
+// thread-hostile.
+extern GFLAGS_DLL_DECL void ShutDownCommandLineFlags();
+
+
+// --------------------------------------------------------------------
+// Now come the command line flag declaration/definition macros that
+// will actually be used. They're kind of hairy. A major reason
+// for this is initialization: we want people to be able to access
+// variables in global constructors and have that not crash, even if
+// their global constructor runs before the global constructor here.
+// (Obviously, we can't guarantee the flags will have the correct
+// default value in that case, but at least accessing them is safe.)
+// The only way to do that is have flags point to a static buffer.
+// So we make one, using a union to ensure proper alignment, and
+// then use placement-new to actually set up the flag with the
+// correct default value. In the same vein, we have to worry about
+// flag access in global destructors, so FlagRegisterer has to be
+// careful never to destroy the flag-values it constructs.
+//
+// Note that when we define a flag variable FLAGS_<name>, we also
+// preemptively define a junk variable, FLAGS_no<name>. This is to
+// cause a link-time error if someone tries to define 2 flags with
+// names like "logging" and "nologging". We do this because a bool
+// flag FLAG can be set from the command line to true with a "-FLAG"
+// argument, and to false with a "-noFLAG" argument, and so this can
+// potentially avert confusion.
+//
+// We also put flags into their own namespace. It is purposefully
+// named in an opaque way that people should have trouble typing
+// directly. The idea is that DEFINE puts the flag in the weird
+// namespace, and DECLARE imports the flag from there into the current
+// namespace. The net result is to force people to use DECLARE to get
+// access to a flag, rather than saying "extern GFLAGS_DLL_DECL bool FLAGS_whatever;"
+// or some such instead. We want this so we can put extra
+// functionality (like sanity-checking) in DECLARE if we want, and
+// make sure it is picked up everywhere.
+//
+// We also put the type of the variable in the namespace, so that
+// people can't DECLARE_int32 something that they DEFINE_bool'd
+// elsewhere.
+
+class GFLAGS_DLL_DECL FlagRegisterer {
+ public:
+ FlagRegisterer(const char* name, const char* type,
+ const char* help, const char* filename,
+ void* current_storage, void* defvalue_storage);
+};
+
+// If your application #defines STRIP_FLAG_HELP to a non-zero value
+// before #including this file, we remove the help message from the
+// binary file. This can reduce the size of the resulting binary
+// somewhat, and may also be useful for security reasons.
+
+extern GFLAGS_DLL_DECL const char kStrippedFlagHelp[];
+
+
+} // namespace GFLAGS_NAMESPACE
+
+
+#ifndef SWIG // In swig, ignore the main flag declarations
+
+#if defined(STRIP_FLAG_HELP) && STRIP_FLAG_HELP > 0
+// Need this construct to avoid the 'defined but not used' warning.
+#define MAYBE_STRIPPED_HELP(txt) \
+ (false ? (txt) : GFLAGS_NAMESPACE::kStrippedFlagHelp)
+#else
+#define MAYBE_STRIPPED_HELP(txt) txt
+#endif
+
+// Each command-line flag has two variables associated with it: one
+// with the current value, and one with the default value. However,
+// we have a third variable, which is where value is assigned; it's a
+// constant. This guarantees that FLAG_##value is initialized at
+// static initialization time (e.g. before program-start) rather than
+// than global construction time (which is after program-start but
+// before main), at least when 'value' is a compile-time constant. We
+// use a small trick for the "default value" variable, and call it
+// FLAGS_no<name>. This serves the second purpose of assuring a
+// compile error if someone tries to define a flag named no<name>
+// which is illegal (--foo and --nofoo both affect the "foo" flag).
+#define DEFINE_VARIABLE(type, shorttype, name, value, help) \
+ namespace fL##shorttype { \
+ static const type FLAGS_nono##name = value; \
+ /* We always want to export defined variables, dll or no */ \
+ GFLAGS_DLL_DEFINE_FLAG type FLAGS_##name = FLAGS_nono##name; \
+ type FLAGS_no##name = FLAGS_nono##name; \
+ static GFLAGS_NAMESPACE::FlagRegisterer o_##name( \
+ #name, #type, MAYBE_STRIPPED_HELP(help), __FILE__, \
+ &FLAGS_##name, &FLAGS_no##name); \
+ } \
+ using fL##shorttype::FLAGS_##name
+
+// For DEFINE_bool, we want to do the extra check that the passed-in
+// value is actually a bool, and not a string or something that can be
+// coerced to a bool. These declarations (no definition needed!) will
+// help us do that, and never evaluate From, which is important.
+// We'll use 'sizeof(IsBool(val))' to distinguish. This code requires
+// that the compiler have different sizes for bool & double. Since
+// this is not guaranteed by the standard, we check it with a
+// COMPILE_ASSERT.
+namespace fLB {
+struct CompileAssert {};
+typedef CompileAssert expected_sizeof_double_neq_sizeof_bool[
+ (sizeof(double) != sizeof(bool)) ? 1 : -1];
+template<typename From> double GFLAGS_DLL_DECL IsBoolFlag(const From& from);
+GFLAGS_DLL_DECL bool IsBoolFlag(bool from);
+} // namespace fLB
+
+// Here are the actual DEFINE_*-macros. The respective DECLARE_*-macros
+// are in a separate include, gflags_declare.h, for reducing
+// the physical transitive size for DECLARE use.
+#define DEFINE_bool(name, val, txt) \
+ namespace fLB { \
+ typedef ::fLB::CompileAssert FLAG_##name##_value_is_not_a_bool[ \
+ (sizeof(::fLB::IsBoolFlag(val)) != sizeof(double))? 1: -1]; \
+ } \
+ DEFINE_VARIABLE(bool, B, name, val, txt)
+
+#define DEFINE_int32(name, val, txt) \
+ DEFINE_VARIABLE(GFLAGS_NAMESPACE::int32, I, \
+ name, val, txt)
+
+#define DEFINE_int64(name, val, txt) \
+ DEFINE_VARIABLE(GFLAGS_NAMESPACE::int64, I64, \
+ name, val, txt)
+
+#define DEFINE_uint64(name,val, txt) \
+ DEFINE_VARIABLE(GFLAGS_NAMESPACE::uint64, U64, \
+ name, val, txt)
+
+#define DEFINE_double(name, val, txt) \
+ DEFINE_VARIABLE(double, D, name, val, txt)
+
+// Strings are trickier, because they're not a POD, so we can't
+// construct them at static-initialization time (instead they get
+// constructed at global-constructor time, which is much later). To
+// try to avoid crashes in that case, we use a char buffer to store
+// the string, which we can static-initialize, and then placement-new
+// into it later. It's not perfect, but the best we can do.
+
+namespace fLS {
+
+inline clstring* dont_pass0toDEFINE_string(char *stringspot,
+ const char *value) {
+ return new(stringspot) clstring(value);
+}
+inline clstring* dont_pass0toDEFINE_string(char *stringspot,
+ const clstring &value) {
+ return new(stringspot) clstring(value);
+}
+inline clstring* dont_pass0toDEFINE_string(char *stringspot,
+ int value);
+} // namespace fLS
+
+// We need to define a var named FLAGS_no##name so people don't define
+// --string and --nostring. And we need a temporary place to put val
+// so we don't have to evaluate it twice. Two great needs that go
+// great together!
+// The weird 'using' + 'extern' inside the fLS namespace is to work around
+// an unknown compiler bug/issue with the gcc 4.2.1 on SUSE 10. See
+// http://code.google.com/p/google-gflags/issues/detail?id=20
+#define DEFINE_string(name, val, txt) \
+ namespace fLS { \
+ using ::fLS::clstring; \
+ static union { void* align; char s[sizeof(clstring)]; } s_##name[2]; \
+ clstring* const FLAGS_no##name = ::fLS:: \
+ dont_pass0toDEFINE_string(s_##name[0].s, \
+ val); \
+ static GFLAGS_NAMESPACE::FlagRegisterer o_##name( \
+ #name, "string", MAYBE_STRIPPED_HELP(txt), __FILE__, \
+ s_##name[0].s, new (s_##name[1].s) clstring(*FLAGS_no##name)); \
+ extern GFLAGS_DLL_DEFINE_FLAG clstring& FLAGS_##name; \
+ using fLS::FLAGS_##name; \
+ clstring& FLAGS_##name = *FLAGS_no##name; \
+ } \
+ using fLS::FLAGS_##name
+
+#endif // SWIG
+
+
+@INCLUDE_GFLAGS_NS_H@
+
+
+#endif // GFLAGS_GFLAGS_H_
diff --git a/third_party/gflags/src/gflags_completions.cc b/third_party/gflags/src/gflags_completions.cc
new file mode 100644
index 0000000..3a47623
--- /dev/null
+++ b/third_party/gflags/src/gflags_completions.cc
@@ -0,0 +1,769 @@
+// Copyright (c) 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---
+
+// Bash-style command line flag completion for C++ binaries
+//
+// This module implements bash-style completions. It achieves this
+// goal in the following broad chunks:
+//
+// 1) Take a to-be-completed word, and examine it for search hints
+// 2) Identify all potentially matching flags
+// 2a) If there are no matching flags, do nothing.
+// 2b) If all matching flags share a common prefix longer than the
+// completion word, output just that matching prefix
+// 3) Categorize those flags to produce a rough ordering of relevence.
+// 4) Potentially trim the set of flags returned to a smaller number
+// that bash is happier with
+// 5) Output the matching flags in groups ordered by relevence.
+// 5a) Force bash to place most-relevent groups at the top of the list
+// 5b) Trim most flag's descriptions to fit on a single terminal line
+
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h> // for strlen
+
+#include <set>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "gflags.h"
+#include "util.h"
+
+using std::set;
+using std::string;
+using std::vector;
+
+
+DEFINE_string(tab_completion_word, "",
+ "If non-empty, HandleCommandLineCompletions() will hijack the "
+ "process and attempt to do bash-style command line flag "
+ "completion on this value.");
+DEFINE_int32(tab_completion_columns, 80,
+ "Number of columns to use in output for tab completion");
+
+
+namespace GFLAGS_NAMESPACE {
+
+
+namespace {
+// Function prototypes and Type forward declarations. Code may be
+// more easily understood if it is roughly ordered according to
+// control flow, rather than by C's "declare before use" ordering
+struct CompletionOptions;
+struct NotableFlags;
+
+// The entry point if flag completion is to be used.
+static void PrintFlagCompletionInfo(void);
+
+
+// 1) Examine search word
+static void CanonicalizeCursorWordAndSearchOptions(
+ const string &cursor_word,
+ string *canonical_search_token,
+ CompletionOptions *options);
+
+static bool RemoveTrailingChar(string *str, char c);
+
+
+// 2) Find all matches
+static void FindMatchingFlags(
+ const vector<CommandLineFlagInfo> &all_flags,
+ const CompletionOptions &options,
+ const string &match_token,
+ set<const CommandLineFlagInfo *> *all_matches,
+ string *longest_common_prefix);
+
+static bool DoesSingleFlagMatch(
+ const CommandLineFlagInfo &flag,
+ const CompletionOptions &options,
+ const string &match_token);
+
+
+// 3) Categorize matches
+static void CategorizeAllMatchingFlags(
+ const set<const CommandLineFlagInfo *> &all_matches,
+ const string &search_token,
+ const string &module,
+ const string &package_dir,
+ NotableFlags *notable_flags);
+
+static void TryFindModuleAndPackageDir(
+ const vector<CommandLineFlagInfo> all_flags,
+ string *module,
+ string *package_dir);
+
+
+// 4) Decide which flags to use
+static void FinalizeCompletionOutput(
+ const set<const CommandLineFlagInfo *> &matching_flags,
+ CompletionOptions *options,
+ NotableFlags *notable_flags,
+ vector<string> *completions);
+
+static void RetrieveUnusedFlags(
+ const set<const CommandLineFlagInfo *> &matching_flags,
+ const NotableFlags ¬able_flags,
+ set<const CommandLineFlagInfo *> *unused_flags);
+
+
+// 5) Output matches
+static void OutputSingleGroupWithLimit(
+ const set<const CommandLineFlagInfo *> &group,
+ const string &line_indentation,
+ const string &header,
+ const string &footer,
+ bool long_output_format,
+ int *remaining_line_limit,
+ size_t *completion_elements_added,
+ vector<string> *completions);
+
+// (helpers for #5)
+static string GetShortFlagLine(
+ const string &line_indentation,
+ const CommandLineFlagInfo &info);
+
+static string GetLongFlagLine(
+ const string &line_indentation,
+ const CommandLineFlagInfo &info);
+
+
+//
+// Useful types
+
+// Try to deduce the intentions behind this completion attempt. Return the
+// canonical search term in 'canonical_search_token'. Binary search options
+// are returned in the various booleans, which should all have intuitive
+// semantics, possibly except:
+// - return_all_matching_flags: Generally, we'll trim the number of
+// returned candidates to some small number, showing those that are
+// most likely to be useful first. If this is set, however, the user
+// really does want us to return every single flag as an option.
+// - force_no_update: Any time we output lines, all of which share a
+// common prefix, bash will 'helpfully' not even bother to show the
+// output, instead changing the current word to be that common prefix.
+// If it's clear this shouldn't happen, we'll set this boolean
+struct CompletionOptions {
+ bool flag_name_substring_search;
+ bool flag_location_substring_search;
+ bool flag_description_substring_search;
+ bool return_all_matching_flags;
+ bool force_no_update;
+};
+
+// Notable flags are flags that are special or preferred for some
+// reason. For example, flags that are defined in the binary's module
+// are expected to be much more relevent than flags defined in some
+// other random location. These sets are specified roughly in precedence
+// order. Once a flag is placed in one of these 'higher' sets, it won't
+// be placed in any of the 'lower' sets.
+struct NotableFlags {
+ typedef set<const CommandLineFlagInfo *> FlagSet;
+ FlagSet perfect_match_flag;
+ FlagSet module_flags; // Found in module file
+ FlagSet package_flags; // Found in same directory as module file
+ FlagSet most_common_flags; // One of the XXX most commonly supplied flags
+ FlagSet subpackage_flags; // Found in subdirectories of package
+};
+
+
+//
+// Tab completion implementation - entry point
+static void PrintFlagCompletionInfo(void) {
+ string cursor_word = FLAGS_tab_completion_word;
+ string canonical_token;
+ CompletionOptions options = { };
+ CanonicalizeCursorWordAndSearchOptions(
+ cursor_word,
+ &canonical_token,
+ &options);
+
+ DVLOG(1) << "Identified canonical_token: '" << canonical_token << "'";
+
+ vector<CommandLineFlagInfo> all_flags;
+ set<const CommandLineFlagInfo *> matching_flags;
+ GetAllFlags(&all_flags);
+ DVLOG(2) << "Found " << all_flags.size() << " flags overall";
+
+ string longest_common_prefix;
+ FindMatchingFlags(
+ all_flags,
+ options,
+ canonical_token,
+ &matching_flags,
+ &longest_common_prefix);
+ DVLOG(1) << "Identified " << matching_flags.size() << " matching flags";
+ DVLOG(1) << "Identified " << longest_common_prefix
+ << " as longest common prefix.";
+ if (longest_common_prefix.size() > canonical_token.size()) {
+ // There's actually a shared common prefix to all matching flags,
+ // so may as well output that and quit quickly.
+ DVLOG(1) << "The common prefix '" << longest_common_prefix
+ << "' was longer than the token '" << canonical_token
+ << "'. Returning just this prefix for completion.";
+ fprintf(stdout, "--%s", longest_common_prefix.c_str());
+ return;
+ }
+ if (matching_flags.empty()) {
+ VLOG(1) << "There were no matching flags, returning nothing.";
+ return;
+ }
+
+ string module;
+ string package_dir;
+ TryFindModuleAndPackageDir(all_flags, &module, &package_dir);
+ DVLOG(1) << "Identified module: '" << module << "'";
+ DVLOG(1) << "Identified package_dir: '" << package_dir << "'";
+
+ NotableFlags notable_flags;
+ CategorizeAllMatchingFlags(
+ matching_flags,
+ canonical_token,
+ module,
+ package_dir,
+ ¬able_flags);
+ DVLOG(2) << "Categorized matching flags:";
+ DVLOG(2) << " perfect_match: " << notable_flags.perfect_match_flag.size();
+ DVLOG(2) << " module: " << notable_flags.module_flags.size();
+ DVLOG(2) << " package: " << notable_flags.package_flags.size();
+ DVLOG(2) << " most common: " << notable_flags.most_common_flags.size();
+ DVLOG(2) << " subpackage: " << notable_flags.subpackage_flags.size();
+
+ vector<string> completions;
+ FinalizeCompletionOutput(
+ matching_flags,
+ &options,
+ ¬able_flags,
+ &completions);
+
+ if (options.force_no_update)
+ completions.push_back("~");
+
+ DVLOG(1) << "Finalized with " << completions.size()
+ << " chosen completions";
+
+ for (vector<string>::const_iterator it = completions.begin();
+ it != completions.end();
+ ++it) {
+ DVLOG(9) << " Completion entry: '" << *it << "'";
+ fprintf(stdout, "%s\n", it->c_str());
+ }
+}
+
+
+// 1) Examine search word (and helper method)
+static void CanonicalizeCursorWordAndSearchOptions(
+ const string &cursor_word,
+ string *canonical_search_token,
+ CompletionOptions *options) {
+ *canonical_search_token = cursor_word;
+ if (canonical_search_token->empty()) return;
+
+ // Get rid of leading quotes and dashes in the search term
+ if ((*canonical_search_token)[0] == '"')
+ *canonical_search_token = canonical_search_token->substr(1);
+ while ((*canonical_search_token)[0] == '-')
+ *canonical_search_token = canonical_search_token->substr(1);
+
+ options->flag_name_substring_search = false;
+ options->flag_location_substring_search = false;
+ options->flag_description_substring_search = false;
+ options->return_all_matching_flags = false;
+ options->force_no_update = false;
+
+ // Look for all search options we can deduce now. Do this by walking
+ // backwards through the term, looking for up to three '?' and up to
+ // one '+' as suffixed characters. Consume them if found, and remove
+ // them from the canonical search token.
+ int found_question_marks = 0;
+ int found_plusses = 0;
+ while (true) {
+ if (found_question_marks < 3 &&
+ RemoveTrailingChar(canonical_search_token, '?')) {
+ ++found_question_marks;
+ continue;
+ }
+ if (found_plusses < 1 &&
+ RemoveTrailingChar(canonical_search_token, '+')) {
+ ++found_plusses;
+ continue;
+ }
+ break;
+ }
+
+ switch (found_question_marks) { // all fallthroughs
+ case 3: options->flag_description_substring_search = true;
+ case 2: options->flag_location_substring_search = true;
+ case 1: options->flag_name_substring_search = true;
+ };
+
+ options->return_all_matching_flags = (found_plusses > 0);
+}
+
+// Returns true if a char was removed
+static bool RemoveTrailingChar(string *str, char c) {
+ if (str->empty()) return false;
+ if ((*str)[str->size() - 1] == c) {
+ *str = str->substr(0, str->size() - 1);
+ return true;
+ }
+ return false;
+}
+
+
+// 2) Find all matches (and helper methods)
+static void FindMatchingFlags(
+ const vector<CommandLineFlagInfo> &all_flags,
+ const CompletionOptions &options,
+ const string &match_token,
+ set<const CommandLineFlagInfo *> *all_matches,
+ string *longest_common_prefix) {
+ all_matches->clear();
+ bool first_match = true;
+ for (vector<CommandLineFlagInfo>::const_iterator it = all_flags.begin();
+ it != all_flags.end();
+ ++it) {
+ if (DoesSingleFlagMatch(*it, options, match_token)) {
+ all_matches->insert(&*it);
+ if (first_match) {
+ first_match = false;
+ *longest_common_prefix = it->name;
+ } else {
+ if (longest_common_prefix->empty() || it->name.empty()) {
+ longest_common_prefix->clear();
+ continue;
+ }
+ string::size_type pos = 0;
+ while (pos < longest_common_prefix->size() &&
+ pos < it->name.size() &&
+ (*longest_common_prefix)[pos] == it->name[pos])
+ ++pos;
+ longest_common_prefix->erase(pos);
+ }
+ }
+ }
+}
+
+// Given the set of all flags, the parsed match options, and the
+// canonical search token, produce the set of all candidate matching
+// flags for subsequent analysis or filtering.
+static bool DoesSingleFlagMatch(
+ const CommandLineFlagInfo &flag,
+ const CompletionOptions &options,
+ const string &match_token) {
+ // Is there a prefix match?
+ string::size_type pos = flag.name.find(match_token);
+ if (pos == 0) return true;
+
+ // Is there a substring match if we want it?
+ if (options.flag_name_substring_search &&
+ pos != string::npos)
+ return true;
+
+ // Is there a location match if we want it?
+ if (options.flag_location_substring_search &&
+ flag.filename.find(match_token) != string::npos)
+ return true;
+
+ // TODO(user): All searches should probably be case-insensitive
+ // (especially this one...)
+ if (options.flag_description_substring_search &&
+ flag.description.find(match_token) != string::npos)
+ return true;
+
+ return false;
+}
+
+// 3) Categorize matches (and helper method)
+
+// Given a set of matching flags, categorize them by
+// likely relevence to this specific binary
+static void CategorizeAllMatchingFlags(
+ const set<const CommandLineFlagInfo *> &all_matches,
+ const string &search_token,
+ const string &module, // empty if we couldn't find any
+ const string &package_dir, // empty if we couldn't find any
+ NotableFlags *notable_flags) {
+ notable_flags->perfect_match_flag.clear();
+ notable_flags->module_flags.clear();
+ notable_flags->package_flags.clear();
+ notable_flags->most_common_flags.clear();
+ notable_flags->subpackage_flags.clear();
+
+ for (set<const CommandLineFlagInfo *>::const_iterator it =
+ all_matches.begin();
+ it != all_matches.end();
+ ++it) {
+ DVLOG(2) << "Examining match '" << (*it)->name << "'";
+ DVLOG(7) << " filename: '" << (*it)->filename << "'";
+ string::size_type pos = string::npos;
+ if (!package_dir.empty())
+ pos = (*it)->filename.find(package_dir);
+ string::size_type slash = string::npos;
+ if (pos != string::npos) // candidate for package or subpackage match
+ slash = (*it)->filename.find(
+ PATH_SEPARATOR,
+ pos + package_dir.size() + 1);
+
+ if ((*it)->name == search_token) {
+ // Exact match on some flag's name
+ notable_flags->perfect_match_flag.insert(*it);
+ DVLOG(3) << "Result: perfect match";
+ } else if (!module.empty() && (*it)->filename == module) {
+ // Exact match on module filename
+ notable_flags->module_flags.insert(*it);
+ DVLOG(3) << "Result: module match";
+ } else if (!package_dir.empty() &&
+ pos != string::npos && slash == string::npos) {
+ // In the package, since there was no slash after the package portion
+ notable_flags->package_flags.insert(*it);
+ DVLOG(3) << "Result: package match";
+ } else if (false) {
+ // In the list of the XXX most commonly supplied flags overall
+ // TODO(user): Compile this list.
+ DVLOG(3) << "Result: most-common match";
+ } else if (!package_dir.empty() &&
+ pos != string::npos && slash != string::npos) {
+ // In a subdirectory of the package
+ notable_flags->subpackage_flags.insert(*it);
+ DVLOG(3) << "Result: subpackage match";
+ }
+
+ DVLOG(3) << "Result: not special match";
+ }
+}
+
+static void PushNameWithSuffix(vector<string>* suffixes, const char* suffix) {
+ suffixes->push_back(
+ StringPrintf("/%s%s", ProgramInvocationShortName(), suffix));
+}
+
+static void TryFindModuleAndPackageDir(
+ const vector<CommandLineFlagInfo> all_flags,
+ string *module,
+ string *package_dir) {
+ module->clear();
+ package_dir->clear();
+
+ vector<string> suffixes;
+ // TODO(user): There's some inherant ambiguity here - multiple directories
+ // could share the same trailing folder and file structure (and even worse,
+ // same file names), causing us to be unsure as to which of the two is the
+ // actual package for this binary. In this case, we'll arbitrarily choose.
+ PushNameWithSuffix(&suffixes, ".");
+ PushNameWithSuffix(&suffixes, "-main.");
+ PushNameWithSuffix(&suffixes, "_main.");
+ // These four are new but probably merited?
+ PushNameWithSuffix(&suffixes, "-test.");
+ PushNameWithSuffix(&suffixes, "_test.");
+ PushNameWithSuffix(&suffixes, "-unittest.");
+ PushNameWithSuffix(&suffixes, "_unittest.");
+
+ for (vector<CommandLineFlagInfo>::const_iterator it = all_flags.begin();
+ it != all_flags.end();
+ ++it) {
+ for (vector<string>::const_iterator suffix = suffixes.begin();
+ suffix != suffixes.end();
+ ++suffix) {
+ // TODO(user): Make sure the match is near the end of the string
+ if (it->filename.find(*suffix) != string::npos) {
+ *module = it->filename;
+ string::size_type sep = it->filename.rfind(PATH_SEPARATOR);
+ *package_dir = it->filename.substr(0, (sep == string::npos) ? 0 : sep);
+ return;
+ }
+ }
+ }
+}
+
+// Can't specialize template type on a locally defined type. Silly C++...
+struct DisplayInfoGroup {
+ const char* header;
+ const char* footer;
+ set<const CommandLineFlagInfo *> *group;
+
+ int SizeInLines() const {
+ int size_in_lines = static_cast<int>(group->size()) + 1;
+ if (strlen(header) > 0) {
+ size_in_lines++;
+ }
+ if (strlen(footer) > 0) {
+ size_in_lines++;
+ }
+ return size_in_lines;
+ }
+};
+
+// 4) Finalize and trim output flag set
+static void FinalizeCompletionOutput(
+ const set<const CommandLineFlagInfo *> &matching_flags,
+ CompletionOptions *options,
+ NotableFlags *notable_flags,
+ vector<string> *completions) {
+
+ // We want to output lines in groups. Each group needs to be indented
+ // the same to keep its lines together. Unless otherwise required,
+ // only 99 lines should be output to prevent bash from harassing the
+ // user.
+
+ // First, figure out which output groups we'll actually use. For each
+ // nonempty group, there will be ~3 lines of header & footer, plus all
+ // output lines themselves.
+ int max_desired_lines = // "999999 flags should be enough for anyone. -dave"
+ (options->return_all_matching_flags ? 999999 : 98);
+ int lines_so_far = 0;
+
+ vector<DisplayInfoGroup> output_groups;
+ bool perfect_match_found = false;
+ if (lines_so_far < max_desired_lines &&
+ !notable_flags->perfect_match_flag.empty()) {
+ perfect_match_found = true;
+ DisplayInfoGroup group =
+ { "",
+ "==========",
+ ¬able_flags->perfect_match_flag };
+ lines_so_far += group.SizeInLines();
+ output_groups.push_back(group);
+ }
+ if (lines_so_far < max_desired_lines &&
+ !notable_flags->module_flags.empty()) {
+ DisplayInfoGroup group = {
+ "-* Matching module flags *-",
+ "===========================",
+ ¬able_flags->module_flags };
+ lines_so_far += group.SizeInLines();
+ output_groups.push_back(group);
+ }
+ if (lines_so_far < max_desired_lines &&
+ !notable_flags->package_flags.empty()) {
+ DisplayInfoGroup group = {
+ "-* Matching package flags *-",
+ "============================",
+ ¬able_flags->package_flags };
+ lines_so_far += group.SizeInLines();
+ output_groups.push_back(group);
+ }
+ if (lines_so_far < max_desired_lines &&
+ !notable_flags->most_common_flags.empty()) {
+ DisplayInfoGroup group = {
+ "-* Commonly used flags *-",
+ "=========================",
+ ¬able_flags->most_common_flags };
+ lines_so_far += group.SizeInLines();
+ output_groups.push_back(group);
+ }
+ if (lines_so_far < max_desired_lines &&
+ !notable_flags->subpackage_flags.empty()) {
+ DisplayInfoGroup group = {
+ "-* Matching sub-package flags *-",
+ "================================",
+ ¬able_flags->subpackage_flags };
+ lines_so_far += group.SizeInLines();
+ output_groups.push_back(group);
+ }
+
+ set<const CommandLineFlagInfo *> obscure_flags; // flags not notable
+ if (lines_so_far < max_desired_lines) {
+ RetrieveUnusedFlags(matching_flags, *notable_flags, &obscure_flags);
+ if (!obscure_flags.empty()) {
+ DisplayInfoGroup group = {
+ "-* Other flags *-",
+ "",
+ &obscure_flags };
+ lines_so_far += group.SizeInLines();
+ output_groups.push_back(group);
+ }
+ }
+
+ // Second, go through each of the chosen output groups and output
+ // as many of those flags as we can, while remaining below our limit
+ int remaining_lines = max_desired_lines;
+ size_t completions_output = 0;
+ int indent = static_cast<int>(output_groups.size()) - 1;
+ for (vector<DisplayInfoGroup>::const_iterator it =
+ output_groups.begin();
+ it != output_groups.end();
+ ++it, --indent) {
+ OutputSingleGroupWithLimit(
+ *it->group, // group
+ string(indent, ' '), // line indentation
+ string(it->header), // header
+ string(it->footer), // footer
+ perfect_match_found, // long format
+ &remaining_lines, // line limit - reduces this by number printed
+ &completions_output, // completions (not lines) added
+ completions); // produced completions
+ perfect_match_found = false;
+ }
+
+ if (completions_output != matching_flags.size()) {
+ options->force_no_update = false;
+ completions->push_back("~ (Remaining flags hidden) ~");
+ } else {
+ options->force_no_update = true;
+ }
+}
+
+static void RetrieveUnusedFlags(
+ const set<const CommandLineFlagInfo *> &matching_flags,
+ const NotableFlags ¬able_flags,
+ set<const CommandLineFlagInfo *> *unused_flags) {
+ // Remove from 'matching_flags' set all members of the sets of
+ // flags we've already printed (specifically, those in notable_flags)
+ for (set<const CommandLineFlagInfo *>::const_iterator it =
+ matching_flags.begin();
+ it != matching_flags.end();
+ ++it) {
+ if (notable_flags.perfect_match_flag.count(*it) ||
+ notable_flags.module_flags.count(*it) ||
+ notable_flags.package_flags.count(*it) ||
+ notable_flags.most_common_flags.count(*it) ||
+ notable_flags.subpackage_flags.count(*it))
+ continue;
+ unused_flags->insert(*it);
+ }
+}
+
+// 5) Output matches (and helper methods)
+
+static void OutputSingleGroupWithLimit(
+ const set<const CommandLineFlagInfo *> &group,
+ const string &line_indentation,
+ const string &header,
+ const string &footer,
+ bool long_output_format,
+ int *remaining_line_limit,
+ size_t *completion_elements_output,
+ vector<string> *completions) {
+ if (group.empty()) return;
+ if (!header.empty()) {
+ if (*remaining_line_limit < 2) return;
+ *remaining_line_limit -= 2;
+ completions->push_back(line_indentation + header);
+ completions->push_back(line_indentation + string(header.size(), '-'));
+ }
+ for (set<const CommandLineFlagInfo *>::const_iterator it = group.begin();
+ it != group.end() && *remaining_line_limit > 0;
+ ++it) {
+ --*remaining_line_limit;
+ ++*completion_elements_output;
+ completions->push_back(
+ (long_output_format
+ ? GetLongFlagLine(line_indentation, **it)
+ : GetShortFlagLine(line_indentation, **it)));
+ }
+ if (!footer.empty()) {
+ if (*remaining_line_limit < 1) return;
+ --*remaining_line_limit;
+ completions->push_back(line_indentation + footer);
+ }
+}
+
+static string GetShortFlagLine(
+ const string &line_indentation,
+ const CommandLineFlagInfo &info) {
+ string prefix;
+ bool is_string = (info.type == "string");
+ SStringPrintf(&prefix, "%s--%s [%s%s%s] ",
+ line_indentation.c_str(),
+ info.name.c_str(),
+ (is_string ? "'" : ""),
+ info.default_value.c_str(),
+ (is_string ? "'" : ""));
+ int remainder =
+ FLAGS_tab_completion_columns - static_cast<int>(prefix.size());
+ string suffix;
+ if (remainder > 0)
+ suffix =
+ (static_cast<int>(info.description.size()) > remainder ?
+ (info.description.substr(0, remainder - 3) + "...").c_str() :
+ info.description.c_str());
+ return prefix + suffix;
+}
+
+static string GetLongFlagLine(
+ const string &line_indentation,
+ const CommandLineFlagInfo &info) {
+
+ string output = DescribeOneFlag(info);
+
+ // Replace '-' with '--', and remove trailing newline before appending
+ // the module definition location.
+ string old_flagname = "-" + info.name;
+ output.replace(
+ output.find(old_flagname),
+ old_flagname.size(),
+ "-" + old_flagname);
+ // Stick a newline and indentation in front of the type and default
+ // portions of DescribeOneFlag()s description
+ static const char kNewlineWithIndent[] = "\n ";
+ output.replace(output.find(" type:"), 1, string(kNewlineWithIndent));
+ output.replace(output.find(" default:"), 1, string(kNewlineWithIndent));
+ output = StringPrintf("%s Details for '--%s':\n"
+ "%s defined: %s",
+ line_indentation.c_str(),
+ info.name.c_str(),
+ output.c_str(),
+ info.filename.c_str());
+
+ // Eliminate any doubled newlines that crept in. Specifically, if
+ // DescribeOneFlag() decided to break the line just before "type"
+ // or "default", we don't want to introduce an extra blank line
+ static const string line_of_spaces(FLAGS_tab_completion_columns, ' ');
+ static const char kDoubledNewlines[] = "\n \n";
+ for (string::size_type newlines = output.find(kDoubledNewlines);
+ newlines != string::npos;
+ newlines = output.find(kDoubledNewlines))
+ // Replace each 'doubled newline' with a single newline
+ output.replace(newlines, sizeof(kDoubledNewlines) - 1, string("\n"));
+
+ for (string::size_type newline = output.find('\n');
+ newline != string::npos;
+ newline = output.find('\n')) {
+ int newline_pos = static_cast<int>(newline) % FLAGS_tab_completion_columns;
+ int missing_spaces = FLAGS_tab_completion_columns - newline_pos;
+ output.replace(newline, 1, line_of_spaces, 1, missing_spaces);
+ }
+ return output;
+}
+} // anonymous
+
+void HandleCommandLineCompletions(void) {
+ if (FLAGS_tab_completion_word.empty()) return;
+ PrintFlagCompletionInfo();
+ gflags_exitfunc(0);
+}
+
+
+} // namespace GFLAGS_NAMESPACE
diff --git a/third_party/gflags/src/gflags_completions.h.in b/third_party/gflags/src/gflags_completions.h.in
new file mode 100644
index 0000000..b27e5fd
--- /dev/null
+++ b/third_party/gflags/src/gflags_completions.h.in
@@ -0,0 +1,121 @@
+// Copyright (c) 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---
+
+//
+// Implement helpful bash-style command line flag completions
+//
+// ** Functional API:
+// HandleCommandLineCompletions() should be called early during
+// program startup, but after command line flag code has been
+// initialized, such as the beginning of HandleCommandLineHelpFlags().
+// It checks the value of the flag --tab_completion_word. If this
+// flag is empty, nothing happens here. If it contains a string,
+// however, then HandleCommandLineCompletions() will hijack the
+// process, attempting to identify the intention behind this
+// completion. Regardless of the outcome of this deduction, the
+// process will be terminated, similar to --helpshort flag
+// handling.
+//
+// ** Overview of Bash completions:
+// Bash can be told to programatically determine completions for the
+// current 'cursor word'. It does this by (in this case) invoking a
+// command with some additional arguments identifying the command
+// being executed, the word being completed, and the previous word
+// (if any). Bash then expects a sequence of output lines to be
+// printed to stdout. If these lines all contain a common prefix
+// longer than the cursor word, bash will replace the cursor word
+// with that common prefix, and display nothing. If there isn't such
+// a common prefix, bash will display the lines in pages using 'more'.
+//
+// ** Strategy taken for command line completions:
+// If we can deduce either the exact flag intended, or a common flag
+// prefix, we'll output exactly that. Otherwise, if information
+// must be displayed to the user, we'll take the opportunity to add
+// some helpful information beyond just the flag name (specifically,
+// we'll include the default flag value and as much of the flag's
+// description as can fit on a single terminal line width, as specified
+// by the flag --tab_completion_columns). Furthermore, we'll try to
+// make bash order the output such that the most useful or relevent
+// flags are the most likely to be shown at the top.
+//
+// ** Additional features:
+// To assist in finding that one really useful flag, substring matching
+// was implemented. Before pressing a <TAB> to get completion for the
+// current word, you can append one or more '?' to the flag to do
+// substring matching. Here's the semantics:
+// --foo<TAB> Show me all flags with names prefixed by 'foo'
+// --foo?<TAB> Show me all flags with 'foo' somewhere in the name
+// --foo??<TAB> Same as prior case, but also search in module
+// definition path for 'foo'
+// --foo???<TAB> Same as prior case, but also search in flag
+// descriptions for 'foo'
+// Finally, we'll trim the output to a relatively small number of
+// flags to keep bash quiet about the verbosity of output. If one
+// really wanted to see all possible matches, appending a '+' to the
+// search word will force the exhaustive list of matches to be printed.
+//
+// ** How to have bash accept completions from a binary:
+// Bash requires that it be informed about each command that programmatic
+// completion should be enabled for. Example addition to a .bashrc
+// file would be (your path to gflags_completions.sh file may differ):
+
+/*
+$ complete -o bashdefault -o default -o nospace -C \
+ '/home/build/eng/bash/bash_completions.sh --tab_completion_columns $COLUMNS' \
+ time env binary_name another_binary [...]
+*/
+
+// This would allow the following to work:
+// $ /path/to/binary_name --vmodule<TAB>
+// Or:
+// $ ./bin/path/another_binary --gfs_u<TAB>
+// (etc)
+//
+// Sadly, it appears that bash gives no easy way to force this behavior for
+// all commands. That's where the "time" in the above example comes in.
+// If you haven't specifically added a command to the list of completion
+// supported commands, you can still get completions by prefixing the
+// entire command with "env".
+// $ env /some/brand/new/binary --vmod<TAB>
+// Assuming that "binary" is a newly compiled binary, this should still
+// produce the expected completion output.
+
+
+#ifndef GFLAGS_COMPLETIONS_H_
+#define GFLAGS_COMPLETIONS_H_
+
+namespace @GFLAGS_NAMESPACE@ {
+
+extern void HandleCommandLineCompletions(void);
+
+}
+
+#endif // GFLAGS_COMPLETIONS_H_
diff --git a/third_party/gflags/src/gflags_completions.sh b/third_party/gflags/src/gflags_completions.sh
new file mode 100755
index 0000000..c5fb7e6
--- /dev/null
+++ b/third_party/gflags/src/gflags_completions.sh
@@ -0,0 +1,117 @@
+#!/bin/bash
+
+# Copyright (c) 2008, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+# ---
+# Author: Dave Nicponski
+#
+# This script is invoked by bash in response to a matching compspec. When
+# this happens, bash calls this script using the command shown in the -C
+# block of the complete entry, but also appends 3 arguments. They are:
+# - The command being used for completion
+# - The word being completed
+# - The word preceding the completion word.
+#
+# Here's an example of how you might use this script:
+# $ complete -o bashdefault -o default -o nospace -C \
+# '/usr/local/bin/gflags_completions.sh --tab_completion_columns $COLUMNS' \
+# time env binary_name another_binary [...]
+
+# completion_word_index gets the index of the (N-1)th argument for
+# this command line. completion_word gets the actual argument from
+# this command line at the (N-1)th position
+completion_word_index="$(($# - 1))"
+completion_word="${!completion_word_index}"
+
+# TODO(user): Replace this once gflags_completions.cc has
+# a bool parameter indicating unambiguously to hijack the process for
+# completion purposes.
+if [ -z "$completion_word" ]; then
+ # Until an empty value for the completion word stops being misunderstood
+ # by binaries, don't actually execute the binary or the process
+ # won't be hijacked!
+ exit 0
+fi
+
+# binary_index gets the index of the command being completed (which bash
+# places in the (N-2)nd position. binary gets the actual command from
+# this command line at that (N-2)nd position
+binary_index="$(($# - 2))"
+binary="${!binary_index}"
+
+# For completions to be universal, we may have setup the compspec to
+# trigger on 'harmless pass-through' commands, like 'time' or 'env'.
+# If the command being completed is one of those two, we'll need to
+# identify the actual command being executed. To do this, we need
+# the actual command line that the <TAB> was pressed on. Bash helpfully
+# places this in the $COMP_LINE variable.
+if [ "$binary" == "time" ] || [ "$binary" == "env" ]; then
+ # we'll assume that the first 'argument' is actually the
+ # binary
+
+
+ # TODO(user): This is not perfect - the 'env' command, for instance,
+ # is allowed to have options between the 'env' and 'the command to
+ # be executed'. For example, consider:
+ # $ env FOO="bar" bin/do_something --help<TAB>
+ # In this case, we'll mistake the FOO="bar" portion as the binary.
+ # Perhaps we should continuing consuming leading words until we
+ # either run out of words, or find a word that is a valid file
+ # marked as executable. I can't think of any reason this wouldn't
+ # work.
+
+ # Break up the 'original command line' (not this script's command line,
+ # rather the one the <TAB> was pressed on) and find the second word.
+ parts=( ${COMP_LINE} )
+ binary=${parts[1]}
+fi
+
+# Build the command line to use for completion. Basically it involves
+# passing through all the arguments given to this script (except the 3
+# that bash added), and appending a '--tab_completion_word "WORD"' to
+# the arguments.
+params=""
+for ((i=1; i<=$(($# - 3)); ++i)); do
+ params="$params \"${!i}\"";
+done
+params="$params --tab_completion_word \"$completion_word\""
+
+# TODO(user): Perhaps stash the output in a temporary file somewhere
+# in /tmp, and only cat it to stdout if the command returned a success
+# code, to prevent false positives
+
+# If we think we have a reasonable command to execute, then execute it
+# and hope for the best.
+candidate=$(type -p "$binary")
+if [ ! -z "$candidate" ]; then
+ eval "$candidate 2>/dev/null $params"
+elif [ -f "$binary" ] && [ -x "$binary" ]; then
+ eval "$binary 2>/dev/null $params"
+fi
diff --git a/third_party/gflags/src/gflags_declare.h.in b/third_party/gflags/src/gflags_declare.h.in
new file mode 100644
index 0000000..279db24
--- /dev/null
+++ b/third_party/gflags/src/gflags_declare.h.in
@@ -0,0 +1,141 @@
+// Copyright (c) 1999, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// ---
+//
+// Revamped and reorganized by Craig Silverstein
+//
+// This is the file that should be included by any file which declares
+// command line flag.
+
+#ifndef GFLAGS_DECLARE_H_
+#define GFLAGS_DECLARE_H_
+
+
+// ---------------------------------------------------------------------------
+// Namespace of gflags library symbols.
+#define GFLAGS_NAMESPACE @GFLAGS_NAMESPACE@
+
+// ---------------------------------------------------------------------------
+// Windows DLL import/export.
+
+// We always want to import the symbols of the gflags library
+#ifndef GFLAGS_DLL_DECL
+# if @GFLAGS_IS_A_DLL@ && defined(_MSC_VER)
+# define GFLAGS_DLL_DECL __declspec(dllimport)
+# else
+# define GFLAGS_DLL_DECL
+# endif
+#endif
+
+// We always want to import variables declared in user code
+#ifndef GFLAGS_DLL_DECLARE_FLAG
+# ifdef _MSC_VER
+# define GFLAGS_DLL_DECLARE_FLAG __declspec(dllimport)
+# else
+# define GFLAGS_DLL_DECLARE_FLAG
+# endif
+#endif
+
+// ---------------------------------------------------------------------------
+// Flag types
+#include <string>
+#if @HAVE_STDINT_H@
+# include <stdint.h> // the normal place uint32_t is defined
+#elif @HAVE_SYS_TYPES_H@
+# include <sys/types.h> // the normal place u_int32_t is defined
+#elif @HAVE_INTTYPES_H@
+# include <inttypes.h> // a third place for uint32_t or u_int32_t
+#endif
+
+namespace GFLAGS_NAMESPACE {
+
+#if @GFLAGS_INTTYPES_FORMAT_C99@ // C99
+typedef int32_t int32;
+typedef uint32_t uint32;
+typedef int64_t int64;
+typedef uint64_t uint64;
+#elif @GFLAGS_INTTYPES_FORMAT_BSD@ // BSD
+typedef int32_t int32;
+typedef u_int32_t uint32;
+typedef int64_t int64;
+typedef u_int64_t uint64;
+#elif @GFLAGS_INTTYPES_FORMAT_VC7@ // Windows
+typedef __int32 int32;
+typedef unsigned __int32 uint32;
+typedef __int64 int64;
+typedef unsigned __int64 uint64;
+#else
+# error Do not know how to define a 32-bit integer quantity on your system
+#endif
+
+} // namespace GFLAGS_NAMESPACE
+
+
+namespace fLS {
+
+// The meaning of "string" might be different between now and when the
+// macros below get invoked (e.g., if someone is experimenting with
+// other string implementations that get defined after this file is
+// included). Save the current meaning now and use it in the macros.
+typedef std::string clstring;
+
+} // namespace fLS
+
+
+#define DECLARE_VARIABLE(type, shorttype, name) \
+ /* We always want to import declared variables, dll or no */ \
+ namespace fL##shorttype { extern GFLAGS_DLL_DECLARE_FLAG type FLAGS_##name; } \
+ using fL##shorttype::FLAGS_##name
+
+#define DECLARE_bool(name) \
+ DECLARE_VARIABLE(bool, B, name)
+
+#define DECLARE_int32(name) \
+ DECLARE_VARIABLE(::GFLAGS_NAMESPACE::int32, I, name)
+
+#define DECLARE_int64(name) \
+ DECLARE_VARIABLE(::GFLAGS_NAMESPACE::int64, I64, name)
+
+#define DECLARE_uint64(name) \
+ DECLARE_VARIABLE(::GFLAGS_NAMESPACE::uint64, U64, name)
+
+#define DECLARE_double(name) \
+ DECLARE_VARIABLE(double, D, name)
+
+#define DECLARE_string(name) \
+ /* We always want to import declared variables, dll or no */ \
+ namespace fLS { \
+ using ::fLS::clstring; \
+ extern GFLAGS_DLL_DECLARE_FLAG ::fLS::clstring& FLAGS_##name; \
+ } \
+ using fLS::FLAGS_##name
+
+
+#endif // GFLAGS_DECLARE_H_
diff --git a/third_party/gflags/src/gflags_ns.h.in b/third_party/gflags/src/gflags_ns.h.in
new file mode 100644
index 0000000..f692666
--- /dev/null
+++ b/third_party/gflags/src/gflags_ns.h.in
@@ -0,0 +1,101 @@
+// Copyright (c) 2014, Andreas Schuh
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// -----------------------------------------------------------------------------
+// Imports the gflags library symbols into an alternative/deprecated namespace.
+
+#ifndef GFLAGS_GFLAGS_H_
+# error The internal header gflags_@ns@.h may only be included by gflags.h
+#endif
+
+#ifndef GFLAGS_NS_@NS@_H_
+#define GFLAGS_NS_@NS@_H_
+
+
+namespace @ns@ {
+
+
+using GFLAGS_NAMESPACE::int32;
+using GFLAGS_NAMESPACE::uint32;
+using GFLAGS_NAMESPACE::int64;
+using GFLAGS_NAMESPACE::uint64;
+
+using GFLAGS_NAMESPACE::RegisterFlagValidator;
+using GFLAGS_NAMESPACE::CommandLineFlagInfo;
+using GFLAGS_NAMESPACE::GetAllFlags;
+using GFLAGS_NAMESPACE::ShowUsageWithFlags;
+using GFLAGS_NAMESPACE::ShowUsageWithFlagsRestrict;
+using GFLAGS_NAMESPACE::DescribeOneFlag;
+using GFLAGS_NAMESPACE::SetArgv;
+using GFLAGS_NAMESPACE::GetArgvs;
+using GFLAGS_NAMESPACE::GetArgv;
+using GFLAGS_NAMESPACE::GetArgv0;
+using GFLAGS_NAMESPACE::GetArgvSum;
+using GFLAGS_NAMESPACE::ProgramInvocationName;
+using GFLAGS_NAMESPACE::ProgramInvocationShortName;
+using GFLAGS_NAMESPACE::ProgramUsage;
+using GFLAGS_NAMESPACE::VersionString;
+using GFLAGS_NAMESPACE::GetCommandLineOption;
+using GFLAGS_NAMESPACE::GetCommandLineFlagInfo;
+using GFLAGS_NAMESPACE::GetCommandLineFlagInfoOrDie;
+using GFLAGS_NAMESPACE::FlagSettingMode;
+using GFLAGS_NAMESPACE::SET_FLAGS_VALUE;
+using GFLAGS_NAMESPACE::SET_FLAG_IF_DEFAULT;
+using GFLAGS_NAMESPACE::SET_FLAGS_DEFAULT;
+using GFLAGS_NAMESPACE::SetCommandLineOption;
+using GFLAGS_NAMESPACE::SetCommandLineOptionWithMode;
+using GFLAGS_NAMESPACE::FlagSaver;
+using GFLAGS_NAMESPACE::CommandlineFlagsIntoString;
+using GFLAGS_NAMESPACE::ReadFlagsFromString;
+using GFLAGS_NAMESPACE::AppendFlagsIntoFile;
+using GFLAGS_NAMESPACE::ReadFromFlagsFile;
+using GFLAGS_NAMESPACE::BoolFromEnv;
+using GFLAGS_NAMESPACE::Int32FromEnv;
+using GFLAGS_NAMESPACE::Int64FromEnv;
+using GFLAGS_NAMESPACE::Uint64FromEnv;
+using GFLAGS_NAMESPACE::DoubleFromEnv;
+using GFLAGS_NAMESPACE::StringFromEnv;
+using GFLAGS_NAMESPACE::SetUsageMessage;
+using GFLAGS_NAMESPACE::SetVersionString;
+using GFLAGS_NAMESPACE::ParseCommandLineNonHelpFlags;
+using GFLAGS_NAMESPACE::HandleCommandLineHelpFlags;
+using GFLAGS_NAMESPACE::AllowCommandLineReparsing;
+using GFLAGS_NAMESPACE::ReparseCommandLineNonHelpFlags;
+using GFLAGS_NAMESPACE::ShutDownCommandLineFlags;
+using GFLAGS_NAMESPACE::FlagRegisterer;
+
+#ifndef SWIG
+using GFLAGS_NAMESPACE::ParseCommandLineFlags;
+#endif
+
+
+} // namespace @ns@
+
+
+#endif // GFLAGS_NS_@NS@_H_
diff --git a/third_party/gflags/src/gflags_reporting.cc b/third_party/gflags/src/gflags_reporting.cc
new file mode 100644
index 0000000..9cc41a7
--- /dev/null
+++ b/third_party/gflags/src/gflags_reporting.cc
@@ -0,0 +1,441 @@
+// Copyright (c) 1999, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// ---
+//
+// Revamped and reorganized by Craig Silverstein
+//
+// This file contains code for handling the 'reporting' flags. These
+// are flags that, when present, cause the program to report some
+// information and then exit. --help and --version are the canonical
+// reporting flags, but we also have flags like --helpxml, etc.
+//
+// There's only one function that's meant to be called externally:
+// HandleCommandLineHelpFlags(). (Well, actually, ShowUsageWithFlags(),
+// ShowUsageWithFlagsRestrict(), and DescribeOneFlag() can be called
+// externally too, but there's little need for it.) These are all
+// declared in the main gflags.h header file.
+//
+// HandleCommandLineHelpFlags() will check what 'reporting' flags have
+// been defined, if any -- the "help" part of the function name is a
+// bit misleading -- and do the relevant reporting. It should be
+// called after all flag-values have been assigned, that is, after
+// parsing the command-line.
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <assert.h>
+#include <string>
+#include <vector>
+
+#include "config.h"
+#include "gflags.h"
+#include "gflags_completions.h"
+#include "util.h"
+
+
+// The 'reporting' flags. They all call gflags_exitfunc().
+DEFINE_bool (help, false, "show help on all flags [tip: all flags can have two dashes]");
+DEFINE_bool (helpfull, false, "show help on all flags -- same as -help");
+DEFINE_bool (helpshort, false, "show help on only the main module for this program");
+DEFINE_string(helpon, "", "show help on the modules named by this flag value");
+DEFINE_string(helpmatch, "", "show help on modules whose name contains the specified substr");
+DEFINE_bool (helppackage, false, "show help on all modules in the main package");
+DEFINE_bool (helpxml, false, "produce an xml version of help");
+DEFINE_bool (version, false, "show version and build info and exit");
+
+
+namespace GFLAGS_NAMESPACE {
+
+
+using std::string;
+using std::vector;
+
+
+// --------------------------------------------------------------------
+// DescribeOneFlag()
+// DescribeOneFlagInXML()
+// Routines that pretty-print info about a flag. These use
+// a CommandLineFlagInfo, which is the way the gflags
+// API exposes static info about a flag.
+// --------------------------------------------------------------------
+
+static const int kLineLength = 80;
+
+static void AddString(const string& s,
+ string* final_string, int* chars_in_line) {
+ const int slen = static_cast<int>(s.length());
+ if (*chars_in_line + 1 + slen >= kLineLength) { // < 80 chars/line
+ *final_string += "\n ";
+ *chars_in_line = 6;
+ } else {
+ *final_string += " ";
+ *chars_in_line += 1;
+ }
+ *final_string += s;
+ *chars_in_line += slen;
+}
+
+static string PrintStringFlagsWithQuotes(const CommandLineFlagInfo& flag,
+ const string& text, bool current) {
+ const char* c_string = (current ? flag.current_value.c_str() :
+ flag.default_value.c_str());
+ if (strcmp(flag.type.c_str(), "string") == 0) { // add quotes for strings
+ return StringPrintf("%s: \"%s\"", text.c_str(), c_string);
+ } else {
+ return StringPrintf("%s: %s", text.c_str(), c_string);
+ }
+}
+
+// Create a descriptive string for a flag.
+// Goes to some trouble to make pretty line breaks.
+string DescribeOneFlag(const CommandLineFlagInfo& flag) {
+ string main_part;
+ SStringPrintf(&main_part, " -%s (%s)",
+ flag.name.c_str(),
+ flag.description.c_str());
+ const char* c_string = main_part.c_str();
+ int chars_left = static_cast<int>(main_part.length());
+ string final_string = "";
+ int chars_in_line = 0; // how many chars in current line so far?
+ while (1) {
+ assert(chars_left == strlen(c_string)); // Unless there's a \0 in there?
+ const char* newline = strchr(c_string, '\n');
+ if (newline == NULL && chars_in_line+chars_left < kLineLength) {
+ // The whole remainder of the string fits on this line
+ final_string += c_string;
+ chars_in_line += chars_left;
+ break;
+ }
+ if (newline != NULL && newline - c_string < kLineLength - chars_in_line) {
+ int n = static_cast<int>(newline - c_string);
+ final_string.append(c_string, n);
+ chars_left -= n + 1;
+ c_string += n + 1;
+ } else {
+ // Find the last whitespace on this 80-char line
+ int whitespace = kLineLength-chars_in_line-1; // < 80 chars/line
+ while ( whitespace > 0 && !isspace(c_string[whitespace]) ) {
+ --whitespace;
+ }
+ if (whitespace <= 0) {
+ // Couldn't find any whitespace to make a line break. Just dump the
+ // rest out!
+ final_string += c_string;
+ chars_in_line = kLineLength; // next part gets its own line for sure!
+ break;
+ }
+ final_string += string(c_string, whitespace);
+ chars_in_line += whitespace;
+ while (isspace(c_string[whitespace])) ++whitespace;
+ c_string += whitespace;
+ chars_left -= whitespace;
+ }
+ if (*c_string == '\0')
+ break;
+ StringAppendF(&final_string, "\n ");
+ chars_in_line = 6;
+ }
+
+ // Append data type
+ AddString(string("type: ") + flag.type, &final_string, &chars_in_line);
+ // The listed default value will be the actual default from the flag
+ // definition in the originating source file, unless the value has
+ // subsequently been modified using SetCommandLineOptionWithMode() with mode
+ // SET_FLAGS_DEFAULT, or by setting FLAGS_foo = bar before ParseCommandLineFlags().
+ AddString(PrintStringFlagsWithQuotes(flag, "default", false), &final_string,
+ &chars_in_line);
+ if (!flag.is_default) {
+ AddString(PrintStringFlagsWithQuotes(flag, "currently", true),
+ &final_string, &chars_in_line);
+ }
+
+ StringAppendF(&final_string, "\n");
+ return final_string;
+}
+
+// Simple routine to xml-escape a string: escape & and < only.
+static string XMLText(const string& txt) {
+ string ans = txt;
+ for (string::size_type pos = 0; (pos = ans.find("&", pos)) != string::npos; )
+ ans.replace(pos++, 1, "&");
+ for (string::size_type pos = 0; (pos = ans.find("<", pos)) != string::npos; )
+ ans.replace(pos++, 1, "<");
+ return ans;
+}
+
+static void AddXMLTag(string* r, const char* tag, const string& txt) {
+ StringAppendF(r, "<%s>%s</%s>", tag, XMLText(txt).c_str(), tag);
+}
+
+
+static string DescribeOneFlagInXML(const CommandLineFlagInfo& flag) {
+ // The file and flagname could have been attributes, but default
+ // and meaning need to avoid attribute normalization. This way it
+ // can be parsed by simple programs, in addition to xml parsers.
+ string r("<flag>");
+ AddXMLTag(&r, "file", flag.filename);
+ AddXMLTag(&r, "name", flag.name);
+ AddXMLTag(&r, "meaning", flag.description);
+ AddXMLTag(&r, "default", flag.default_value);
+ AddXMLTag(&r, "current", flag.current_value);
+ AddXMLTag(&r, "type", flag.type);
+ r += "</flag>";
+ return r;
+}
+
+// --------------------------------------------------------------------
+// ShowUsageWithFlags()
+// ShowUsageWithFlagsRestrict()
+// ShowXMLOfFlags()
+// These routines variously expose the registry's list of flag
+// values. ShowUsage*() prints the flag-value information
+// to stdout in a user-readable format (that's what --help uses).
+// The Restrict() version limits what flags are shown.
+// ShowXMLOfFlags() prints the flag-value information to stdout
+// in a machine-readable format. In all cases, the flags are
+// sorted: first by filename they are defined in, then by flagname.
+// --------------------------------------------------------------------
+
+static const char* Basename(const char* filename) {
+ const char* sep = strrchr(filename, PATH_SEPARATOR);
+ return sep ? sep + 1 : filename;
+}
+
+static string Dirname(const string& filename) {
+ string::size_type sep = filename.rfind(PATH_SEPARATOR);
+ return filename.substr(0, (sep == string::npos) ? 0 : sep);
+}
+
+// Test whether a filename contains at least one of the substrings.
+static bool FileMatchesSubstring(const string& filename,
+ const vector<string>& substrings) {
+ for (vector<string>::const_iterator target = substrings.begin();
+ target != substrings.end();
+ ++target) {
+ if (strstr(filename.c_str(), target->c_str()) != NULL)
+ return true;
+ // If the substring starts with a '/', that means that we want
+ // the string to be at the beginning of a directory component.
+ // That should match the first directory component as well, so
+ // we allow '/foo' to match a filename of 'foo'.
+ if (!target->empty() && (*target)[0] == PATH_SEPARATOR &&
+ strncmp(filename.c_str(), target->c_str() + 1,
+ strlen(target->c_str() + 1)) == 0)
+ return true;
+ }
+ return false;
+}
+
+// Show help for every filename which matches any of the target substrings.
+// If substrings is empty, shows help for every file. If a flag's help message
+// has been stripped (e.g. by adding '#define STRIP_FLAG_HELP 1'
+// before including gflags/gflags.h), then this flag will not be displayed
+// by '--help' and its variants.
+static void ShowUsageWithFlagsMatching(const char *argv0,
+ const vector<string> &substrings) {
+ fprintf(stdout, "%s: %s\n", Basename(argv0), ProgramUsage());
+
+ vector<CommandLineFlagInfo> flags;
+ GetAllFlags(&flags); // flags are sorted by filename, then flagname
+
+ string last_filename; // so we know when we're at a new file
+ bool first_directory = true; // controls blank lines between dirs
+ bool found_match = false; // stays false iff no dir matches restrict
+ for (vector<CommandLineFlagInfo>::const_iterator flag = flags.begin();
+ flag != flags.end();
+ ++flag) {
+ if (substrings.empty() ||
+ FileMatchesSubstring(flag->filename, substrings)) {
+ // If the flag has been stripped, pretend that it doesn't exist.
+ if (flag->description == kStrippedFlagHelp) continue;
+ found_match = true; // this flag passed the match!
+ if (flag->filename != last_filename) { // new file
+ if (Dirname(flag->filename) != Dirname(last_filename)) { // new dir!
+ if (!first_directory)
+ fprintf(stdout, "\n\n"); // put blank lines between directories
+ first_directory = false;
+ }
+ fprintf(stdout, "\n Flags from %s:\n", flag->filename.c_str());
+ last_filename = flag->filename;
+ }
+ // Now print this flag
+ fprintf(stdout, "%s", DescribeOneFlag(*flag).c_str());
+ }
+ }
+ if (!found_match && !substrings.empty()) {
+ fprintf(stdout, "\n No modules matched: use -help\n");
+ }
+}
+
+void ShowUsageWithFlagsRestrict(const char *argv0, const char *restrict) {
+ vector<string> substrings;
+ if (restrict != NULL && *restrict != '\0') {
+ substrings.push_back(restrict);
+ }
+ ShowUsageWithFlagsMatching(argv0, substrings);
+}
+
+void ShowUsageWithFlags(const char *argv0) {
+ ShowUsageWithFlagsRestrict(argv0, "");
+}
+
+// Convert the help, program, and usage to xml.
+static void ShowXMLOfFlags(const char *prog_name) {
+ vector<CommandLineFlagInfo> flags;
+ GetAllFlags(&flags); // flags are sorted: by filename, then flagname
+
+ // XML. There is no corresponding schema yet
+ fprintf(stdout, "<?xml version=\"1.0\"?>\n");
+ // The document
+ fprintf(stdout, "<AllFlags>\n");
+ // the program name and usage
+ fprintf(stdout, "<program>%s</program>\n",
+ XMLText(Basename(prog_name)).c_str());
+ fprintf(stdout, "<usage>%s</usage>\n",
+ XMLText(ProgramUsage()).c_str());
+ // All the flags
+ for (vector<CommandLineFlagInfo>::const_iterator flag = flags.begin();
+ flag != flags.end();
+ ++flag) {
+ if (flag->description != kStrippedFlagHelp)
+ fprintf(stdout, "%s\n", DescribeOneFlagInXML(*flag).c_str());
+ }
+ // The end of the document
+ fprintf(stdout, "</AllFlags>\n");
+}
+
+// --------------------------------------------------------------------
+// ShowVersion()
+// Called upon --version. Prints build-related info.
+// --------------------------------------------------------------------
+
+static void ShowVersion() {
+ const char* version_string = VersionString();
+ if (version_string && *version_string) {
+ fprintf(stdout, "%s version %s\n",
+ ProgramInvocationShortName(), version_string);
+ } else {
+ fprintf(stdout, "%s\n", ProgramInvocationShortName());
+ }
+# if !defined(NDEBUG)
+ fprintf(stdout, "Debug build (NDEBUG not #defined)\n");
+# endif
+}
+
+static void AppendPrognameStrings(vector<string>* substrings,
+ const char* progname) {
+ string r("");
+ r += PATH_SEPARATOR;
+ r += progname;
+ substrings->push_back(r + ".");
+ substrings->push_back(r + "-main.");
+ substrings->push_back(r + "_main.");
+}
+
+// --------------------------------------------------------------------
+// HandleCommandLineHelpFlags()
+// Checks all the 'reporting' commandline flags to see if any
+// have been set. If so, handles them appropriately. Note
+// that all of them, by definition, cause the program to exit
+// if they trigger.
+// --------------------------------------------------------------------
+
+void HandleCommandLineHelpFlags() {
+ const char* progname = ProgramInvocationShortName();
+
+ HandleCommandLineCompletions();
+
+ vector<string> substrings;
+ AppendPrognameStrings(&substrings, progname);
+
+ if (FLAGS_helpshort) {
+ // show only flags related to this binary:
+ // E.g. for fileutil.cc, want flags containing ... "/fileutil." cc
+ ShowUsageWithFlagsMatching(progname, substrings);
+ gflags_exitfunc(1);
+
+ } else if (FLAGS_help || FLAGS_helpfull) {
+ // show all options
+ ShowUsageWithFlagsRestrict(progname, ""); // empty restrict
+ gflags_exitfunc(1);
+
+ } else if (!FLAGS_helpon.empty()) {
+ string restrict = PATH_SEPARATOR + FLAGS_helpon + ".";
+ ShowUsageWithFlagsRestrict(progname, restrict.c_str());
+ gflags_exitfunc(1);
+
+ } else if (!FLAGS_helpmatch.empty()) {
+ ShowUsageWithFlagsRestrict(progname, FLAGS_helpmatch.c_str());
+ gflags_exitfunc(1);
+
+ } else if (FLAGS_helppackage) {
+ // Shows help for all files in the same directory as main(). We
+ // don't want to resort to looking at dirname(progname), because
+ // the user can pick progname, and it may not relate to the file
+ // where main() resides. So instead, we search the flags for a
+ // filename like "/progname.cc", and take the dirname of that.
+ vector<CommandLineFlagInfo> flags;
+ GetAllFlags(&flags);
+ string last_package;
+ for (vector<CommandLineFlagInfo>::const_iterator flag = flags.begin();
+ flag != flags.end();
+ ++flag) {
+ if (!FileMatchesSubstring(flag->filename, substrings))
+ continue;
+ const string package = Dirname(flag->filename) + PATH_SEPARATOR;
+ if (package != last_package) {
+ ShowUsageWithFlagsRestrict(progname, package.c_str());
+ VLOG(7) << "Found package: " << package;
+ if (!last_package.empty()) { // means this isn't our first pkg
+ LOG(WARNING) << "Multiple packages contain a file=" << progname;
+ }
+ last_package = package;
+ }
+ }
+ if (last_package.empty()) { // never found a package to print
+ LOG(WARNING) << "Unable to find a package for file=" << progname;
+ }
+ gflags_exitfunc(1);
+
+ } else if (FLAGS_helpxml) {
+ ShowXMLOfFlags(progname);
+ gflags_exitfunc(1);
+
+ } else if (FLAGS_version) {
+ ShowVersion();
+ // Unlike help, we may be asking for version in a script, so return 0
+ gflags_exitfunc(0);
+
+ }
+}
+
+
+} // namespace GFLAGS_NAMESPACE
diff --git a/third_party/gflags/src/mutex.h b/third_party/gflags/src/mutex.h
new file mode 100644
index 0000000..0bdd9d5
--- /dev/null
+++ b/third_party/gflags/src/mutex.h
@@ -0,0 +1,351 @@
+// Copyright (c) 2007, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---
+//
+// A simple mutex wrapper, supporting locks and read-write locks.
+// You should assume the locks are *not* re-entrant.
+//
+// This class is meant to be internal-only and should be wrapped by an
+// internal namespace. Before you use this module, please give the
+// name of your internal namespace for this module. Or, if you want
+// to expose it, you'll want to move it to the Google namespace. We
+// cannot put this class in global namespace because there can be some
+// problems when we have multiple versions of Mutex in each shared object.
+//
+// NOTE: by default, we have #ifdef'ed out the TryLock() method.
+// This is for two reasons:
+// 1) TryLock() under Windows is a bit annoying (it requires a
+// #define to be defined very early).
+// 2) TryLock() is broken for NO_THREADS mode, at least in NDEBUG
+// mode.
+// If you need TryLock(), and either these two caveats are not a
+// problem for you, or you're willing to work around them, then
+// feel free to #define GMUTEX_TRYLOCK, or to remove the #ifdefs
+// in the code below.
+//
+// CYGWIN NOTE: Cygwin support for rwlock seems to be buggy:
+// http://www.cygwin.com/ml/cygwin/2008-12/msg00017.html
+// Because of that, we might as well use windows locks for
+// cygwin. They seem to be more reliable than the cygwin pthreads layer.
+//
+// TRICKY IMPLEMENTATION NOTE:
+// This class is designed to be safe to use during
+// dynamic-initialization -- that is, by global constructors that are
+// run before main() starts. The issue in this case is that
+// dynamic-initialization happens in an unpredictable order, and it
+// could be that someone else's dynamic initializer could call a
+// function that tries to acquire this mutex -- but that all happens
+// before this mutex's constructor has run. (This can happen even if
+// the mutex and the function that uses the mutex are in the same .cc
+// file.) Basically, because Mutex does non-trivial work in its
+// constructor, it's not, in the naive implementation, safe to use
+// before dynamic initialization has run on it.
+//
+// The solution used here is to pair the actual mutex primitive with a
+// bool that is set to true when the mutex is dynamically initialized.
+// (Before that it's false.) Then we modify all mutex routines to
+// look at the bool, and not try to lock/unlock until the bool makes
+// it to true (which happens after the Mutex constructor has run.)
+//
+// This works because before main() starts -- particularly, during
+// dynamic initialization -- there are no threads, so a) it's ok that
+// the mutex operations are a no-op, since we don't need locking then
+// anyway; and b) we can be quite confident our bool won't change
+// state between a call to Lock() and a call to Unlock() (that would
+// require a global constructor in one translation unit to call Lock()
+// and another global constructor in another translation unit to call
+// Unlock() later, which is pretty perverse).
+//
+// That said, it's tricky, and can conceivably fail; it's safest to
+// avoid trying to acquire a mutex in a global constructor, if you
+// can. One way it can fail is that a really smart compiler might
+// initialize the bool to true at static-initialization time (too
+// early) rather than at dynamic-initialization time. To discourage
+// that, we set is_safe_ to true in code (not the constructor
+// colon-initializer) and set it to true via a function that always
+// evaluates to true, but that the compiler can't know always
+// evaluates to true. This should be good enough.
+//
+// A related issue is code that could try to access the mutex
+// after it's been destroyed in the global destructors (because
+// the Mutex global destructor runs before some other global
+// destructor, that tries to acquire the mutex). The way we
+// deal with this is by taking a constructor arg that global
+// mutexes should pass in, that causes the destructor to do no
+// work. We still depend on the compiler not doing anything
+// weird to a Mutex's memory after it is destroyed, but for a
+// static global variable, that's pretty safe.
+
+#ifndef GFLAGS_MUTEX_H_
+#define GFLAGS_MUTEX_H_
+
+#include "gflags_declare.h" // to figure out pthreads support
+
+#if defined(NO_THREADS)
+ typedef int MutexType; // to keep a lock-count
+#elif defined(OS_WINDOWS)
+# ifndef WIN32_LEAN_AND_MEAN
+# define WIN32_LEAN_AND_MEAN // We only need minimal includes
+# endif
+# ifndef NOMINMAX
+# define NOMINMAX // Don't want windows to override min()/max()
+# endif
+# ifdef GMUTEX_TRYLOCK
+ // We need Windows NT or later for TryEnterCriticalSection(). If you
+ // don't need that functionality, you can remove these _WIN32_WINNT
+ // lines, and change TryLock() to assert(0) or something.
+# ifndef _WIN32_WINNT
+# define _WIN32_WINNT 0x0400
+# endif
+# endif
+# include <windows.h>
+ typedef CRITICAL_SECTION MutexType;
+#elif defined(HAVE_PTHREAD) && defined(HAVE_RWLOCK)
+ // Needed for pthread_rwlock_*. If it causes problems, you could take it
+ // out, but then you'd have to unset HAVE_RWLOCK (at least on linux -- it
+ // *does* cause problems for FreeBSD, or MacOSX, but isn't needed
+ // for locking there.)
+# ifdef __linux__
+# if _XOPEN_SOURCE < 500 // including not being defined at all
+# undef _XOPEN_SOURCE
+# define _XOPEN_SOURCE 500 // may be needed to get the rwlock calls
+# endif
+# endif
+# include <pthread.h>
+ typedef pthread_rwlock_t MutexType;
+#elif defined(HAVE_PTHREAD)
+# include <pthread.h>
+ typedef pthread_mutex_t MutexType;
+#else
+# error Need to implement mutex.h for your architecture, or #define NO_THREADS
+#endif
+
+#include <assert.h>
+#include <stdlib.h> // for abort()
+
+#define MUTEX_NAMESPACE gflags_mutex_namespace
+
+namespace MUTEX_NAMESPACE {
+
+class Mutex {
+ public:
+ // This is used for the single-arg constructor
+ enum LinkerInitialized { LINKER_INITIALIZED };
+
+ // Create a Mutex that is not held by anybody. This constructor is
+ // typically used for Mutexes allocated on the heap or the stack.
+ inline Mutex();
+ // This constructor should be used for global, static Mutex objects.
+ // It inhibits work being done by the destructor, which makes it
+ // safer for code that tries to acqiure this mutex in their global
+ // destructor.
+ inline Mutex(LinkerInitialized);
+
+ // Destructor
+ inline ~Mutex();
+
+ inline void Lock(); // Block if needed until free then acquire exclusively
+ inline void Unlock(); // Release a lock acquired via Lock()
+#ifdef GMUTEX_TRYLOCK
+ inline bool TryLock(); // If free, Lock() and return true, else return false
+#endif
+ // Note that on systems that don't support read-write locks, these may
+ // be implemented as synonyms to Lock() and Unlock(). So you can use
+ // these for efficiency, but don't use them anyplace where being able
+ // to do shared reads is necessary to avoid deadlock.
+ inline void ReaderLock(); // Block until free or shared then acquire a share
+ inline void ReaderUnlock(); // Release a read share of this Mutex
+ inline void WriterLock() { Lock(); } // Acquire an exclusive lock
+ inline void WriterUnlock() { Unlock(); } // Release a lock from WriterLock()
+
+ private:
+ MutexType mutex_;
+ // We want to make sure that the compiler sets is_safe_ to true only
+ // when we tell it to, and never makes assumptions is_safe_ is
+ // always true. volatile is the most reliable way to do that.
+ volatile bool is_safe_;
+ // This indicates which constructor was called.
+ bool destroy_;
+
+ inline void SetIsSafe() { is_safe_ = true; }
+
+ // Catch the error of writing Mutex when intending MutexLock.
+ Mutex(Mutex* /*ignored*/) {}
+ // Disallow "evil" constructors
+ Mutex(const Mutex&);
+ void operator=(const Mutex&);
+};
+
+// Now the implementation of Mutex for various systems
+#if defined(NO_THREADS)
+
+// When we don't have threads, we can be either reading or writing,
+// but not both. We can have lots of readers at once (in no-threads
+// mode, that's most likely to happen in recursive function calls),
+// but only one writer. We represent this by having mutex_ be -1 when
+// writing and a number > 0 when reading (and 0 when no lock is held).
+//
+// In debug mode, we assert these invariants, while in non-debug mode
+// we do nothing, for efficiency. That's why everything is in an
+// assert.
+
+Mutex::Mutex() : mutex_(0) { }
+Mutex::Mutex(Mutex::LinkerInitialized) : mutex_(0) { }
+Mutex::~Mutex() { assert(mutex_ == 0); }
+void Mutex::Lock() { assert(--mutex_ == -1); }
+void Mutex::Unlock() { assert(mutex_++ == -1); }
+#ifdef GMUTEX_TRYLOCK
+bool Mutex::TryLock() { if (mutex_) return false; Lock(); return true; }
+#endif
+void Mutex::ReaderLock() { assert(++mutex_ > 0); }
+void Mutex::ReaderUnlock() { assert(mutex_-- > 0); }
+
+#elif defined(OS_WINDOWS)
+
+Mutex::Mutex() : destroy_(true) {
+ InitializeCriticalSection(&mutex_);
+ SetIsSafe();
+}
+Mutex::Mutex(LinkerInitialized) : destroy_(false) {
+ InitializeCriticalSection(&mutex_);
+ SetIsSafe();
+}
+Mutex::~Mutex() { if (destroy_) DeleteCriticalSection(&mutex_); }
+void Mutex::Lock() { if (is_safe_) EnterCriticalSection(&mutex_); }
+void Mutex::Unlock() { if (is_safe_) LeaveCriticalSection(&mutex_); }
+#ifdef GMUTEX_TRYLOCK
+bool Mutex::TryLock() { return is_safe_ ?
+ TryEnterCriticalSection(&mutex_) != 0 : true; }
+#endif
+void Mutex::ReaderLock() { Lock(); } // we don't have read-write locks
+void Mutex::ReaderUnlock() { Unlock(); }
+
+#elif defined(HAVE_PTHREAD) && defined(HAVE_RWLOCK)
+
+#define SAFE_PTHREAD(fncall) do { /* run fncall if is_safe_ is true */ \
+ if (is_safe_ && fncall(&mutex_) != 0) abort(); \
+} while (0)
+
+Mutex::Mutex() : destroy_(true) {
+ SetIsSafe();
+ if (is_safe_ && pthread_rwlock_init(&mutex_, NULL) != 0) abort();
+}
+Mutex::Mutex(Mutex::LinkerInitialized) : destroy_(false) {
+ SetIsSafe();
+ if (is_safe_ && pthread_rwlock_init(&mutex_, NULL) != 0) abort();
+}
+Mutex::~Mutex() { if (destroy_) SAFE_PTHREAD(pthread_rwlock_destroy); }
+void Mutex::Lock() { SAFE_PTHREAD(pthread_rwlock_wrlock); }
+void Mutex::Unlock() { SAFE_PTHREAD(pthread_rwlock_unlock); }
+#ifdef GMUTEX_TRYLOCK
+bool Mutex::TryLock() { return is_safe_ ?
+ pthread_rwlock_trywrlock(&mutex_) == 0 : true; }
+#endif
+void Mutex::ReaderLock() { SAFE_PTHREAD(pthread_rwlock_rdlock); }
+void Mutex::ReaderUnlock() { SAFE_PTHREAD(pthread_rwlock_unlock); }
+#undef SAFE_PTHREAD
+
+#elif defined(HAVE_PTHREAD)
+
+#define SAFE_PTHREAD(fncall) do { /* run fncall if is_safe_ is true */ \
+ if (is_safe_ && fncall(&mutex_) != 0) abort(); \
+} while (0)
+
+Mutex::Mutex() : destroy_(true) {
+ SetIsSafe();
+ if (is_safe_ && pthread_mutex_init(&mutex_, NULL) != 0) abort();
+}
+Mutex::Mutex(Mutex::LinkerInitialized) : destroy_(false) {
+ SetIsSafe();
+ if (is_safe_ && pthread_mutex_init(&mutex_, NULL) != 0) abort();
+}
+Mutex::~Mutex() { if (destroy_) SAFE_PTHREAD(pthread_mutex_destroy); }
+void Mutex::Lock() { SAFE_PTHREAD(pthread_mutex_lock); }
+void Mutex::Unlock() { SAFE_PTHREAD(pthread_mutex_unlock); }
+#ifdef GMUTEX_TRYLOCK
+bool Mutex::TryLock() { return is_safe_ ?
+ pthread_mutex_trylock(&mutex_) == 0 : true; }
+#endif
+void Mutex::ReaderLock() { Lock(); }
+void Mutex::ReaderUnlock() { Unlock(); }
+#undef SAFE_PTHREAD
+
+#endif
+
+// --------------------------------------------------------------------------
+// Some helper classes
+
+// MutexLock(mu) acquires mu when constructed and releases it when destroyed.
+class MutexLock {
+ public:
+ explicit MutexLock(Mutex *mu) : mu_(mu) { mu_->Lock(); }
+ ~MutexLock() { mu_->Unlock(); }
+ private:
+ Mutex * const mu_;
+ // Disallow "evil" constructors
+ MutexLock(const MutexLock&);
+ void operator=(const MutexLock&);
+};
+
+// ReaderMutexLock and WriterMutexLock do the same, for rwlocks
+class ReaderMutexLock {
+ public:
+ explicit ReaderMutexLock(Mutex *mu) : mu_(mu) { mu_->ReaderLock(); }
+ ~ReaderMutexLock() { mu_->ReaderUnlock(); }
+ private:
+ Mutex * const mu_;
+ // Disallow "evil" constructors
+ ReaderMutexLock(const ReaderMutexLock&);
+ void operator=(const ReaderMutexLock&);
+};
+
+class WriterMutexLock {
+ public:
+ explicit WriterMutexLock(Mutex *mu) : mu_(mu) { mu_->WriterLock(); }
+ ~WriterMutexLock() { mu_->WriterUnlock(); }
+ private:
+ Mutex * const mu_;
+ // Disallow "evil" constructors
+ WriterMutexLock(const WriterMutexLock&);
+ void operator=(const WriterMutexLock&);
+};
+
+// Catch bug where variable name is omitted, e.g. MutexLock (&mu);
+#define MutexLock(x) COMPILE_ASSERT(0, mutex_lock_decl_missing_var_name)
+#define ReaderMutexLock(x) COMPILE_ASSERT(0, rmutex_lock_decl_missing_var_name)
+#define WriterMutexLock(x) COMPILE_ASSERT(0, wmutex_lock_decl_missing_var_name)
+
+} // namespace MUTEX_NAMESPACE
+
+using namespace MUTEX_NAMESPACE;
+
+#undef MUTEX_NAMESPACE
+
+#endif /* #define GFLAGS_MUTEX_H__ */
diff --git a/third_party/gflags/src/util.h b/third_party/gflags/src/util.h
new file mode 100644
index 0000000..366e1be
--- /dev/null
+++ b/third_party/gflags/src/util.h
@@ -0,0 +1,373 @@
+// Copyright (c) 2011, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// ---
+//
+// Some generically useful utility routines that in google-land would
+// be their own projects. We make a shortened version here.
+
+#ifndef GFLAGS_UTIL_H_
+#define GFLAGS_UTIL_H_
+
+#include "config.h"
+
+#include <assert.h>
+#include <config.h>
+#ifdef HAVE_INTTYPES_H
+# include <inttypes.h>
+#endif
+#include <stdarg.h> // for va_*
+#include <stdlib.h>
+#include <stdio.h>
+#include <iostream>
+#include <string>
+#include <errno.h>
+#ifdef HAVE_SYS_STAT_H
+# include <sys/stat.h> // for mkdir
+#endif
+
+
+namespace GFLAGS_NAMESPACE {
+
+
+// This is used for unittests for death-testing. It is defined in gflags.cc.
+extern GFLAGS_DLL_DECL void (*gflags_exitfunc)(int);
+
+// Work properly if either strtoll or strtoq is on this system.
+#if defined(strtoll) || defined(HAVE_STRTOLL)
+# define strto64 strtoll
+# define strtou64 strtoull
+#elif defined(HAVE_STRTOQ)
+# define strto64 strtoq
+# define strtou64 strtouq
+// Neither strtoll nor strtoq are defined. I hope strtol works!
+#else
+# define strto64 strtol
+# define strtou64 strtoul
+#endif
+
+// If we have inttypes.h, it will have defined PRId32/etc for us.
+// If not, take our best guess.
+#ifndef PRId32
+# define PRId32 "d"
+#endif
+#ifndef PRId64
+# define PRId64 "lld"
+#endif
+#ifndef PRIu64
+# define PRIu64 "llu"
+#endif
+
+typedef signed char int8;
+typedef unsigned char uint8;
+
+// -- utility macros ---------------------------------------------------------
+
+template <bool> struct CompileAssert {};
+#define COMPILE_ASSERT(expr, msg) \
+ typedef CompileAssert<(bool(expr))> msg[bool(expr) ? 1 : -1]
+
+// Returns the number of elements in an array.
+#define arraysize(arr) (sizeof(arr)/sizeof(*(arr)))
+
+
+// -- logging and testing ---------------------------------------------------
+
+// For now, we ignore the level for logging, and don't show *VLOG's at
+// all, except by hand-editing the lines below
+#define LOG(level) std::cerr
+#define VLOG(level) if (true) {} else std::cerr
+#define DVLOG(level) if (true) {} else std::cerr
+
+// CHECK dies with a fatal error if condition is not true. It is *not*
+// controlled by NDEBUG, so the check will be executed regardless of
+// compilation mode. Therefore, it is safe to do things like:
+// CHECK(fp->Write(x) == 4)
+// We allow stream-like objects after this for debugging, but they're ignored.
+#define EXPECT_TRUE(condition) \
+ if (true) { \
+ if (!(condition)) { \
+ fprintf(stderr, "Check failed: %s\n", #condition); \
+ exit(1); \
+ } \
+ } else std::cerr << ""
+
+#define EXPECT_OP(op, val1, val2) \
+ if (true) { \
+ if (!((val1) op (val2))) { \
+ fprintf(stderr, "Check failed: %s %s %s\n", #val1, #op, #val2); \
+ exit(1); \
+ } \
+ } else std::cerr << ""
+
+#define EXPECT_EQ(val1, val2) EXPECT_OP(==, val1, val2)
+#define EXPECT_NE(val1, val2) EXPECT_OP(!=, val1, val2)
+#define EXPECT_LE(val1, val2) EXPECT_OP(<=, val1, val2)
+#define EXPECT_LT(val1, val2) EXPECT_OP(< , val1, val2)
+#define EXPECT_GE(val1, val2) EXPECT_OP(>=, val1, val2)
+#define EXPECT_GT(val1, val2) EXPECT_OP(> , val1, val2)
+#define EXPECT_FALSE(cond) EXPECT_TRUE(!(cond))
+
+// C99 declares isnan and isinf should be macros, so the #ifdef test
+// should be reliable everywhere. Of course, it's not, but these
+// are testing pertty marginal functionality anyway, so it's ok to
+// not-run them even in situations they might, with effort, be made to work.
+#ifdef isnan // Some compilers, like sun's for Solaris 10, don't define this
+#define EXPECT_NAN(arg) \
+ do { \
+ if (!isnan(arg)) { \
+ fprintf(stderr, "Check failed: isnan(%s)\n", #arg); \
+ exit(1); \
+ } \
+ } while (0)
+#else
+#define EXPECT_NAN(arg)
+#endif
+
+#ifdef isinf // Some compilers, like sun's for Solaris 10, don't define this
+#define EXPECT_INF(arg) \
+ do { \
+ if (!isinf(arg)) { \
+ fprintf(stderr, "Check failed: isinf(%s)\n", #arg); \
+ exit(1); \
+ } \
+ } while (0)
+#else
+#define EXPECT_INF(arg)
+#endif
+
+#define EXPECT_DOUBLE_EQ(val1, val2) \
+ do { \
+ if (((val1) < (val2) - 0.001 || (val1) > (val2) + 0.001)) { \
+ fprintf(stderr, "Check failed: %s == %s\n", #val1, #val2); \
+ exit(1); \
+ } \
+ } while (0)
+
+#define EXPECT_STREQ(val1, val2) \
+ do { \
+ if (strcmp((val1), (val2)) != 0) { \
+ fprintf(stderr, "Check failed: streq(%s, %s)\n", #val1, #val2); \
+ exit(1); \
+ } \
+ } while (0)
+
+// Call this in a .cc file where you will later call RUN_ALL_TESTS in main().
+#define TEST_INIT \
+ static std::vector<void (*)()> g_testlist; /* the tests to run */ \
+ static int RUN_ALL_TESTS() { \
+ std::vector<void (*)()>::const_iterator it; \
+ for (it = g_testlist.begin(); it != g_testlist.end(); ++it) { \
+ (*it)(); /* The test will error-exit if there's a problem. */ \
+ } \
+ fprintf(stderr, "\nPassed %d tests\n\nPASS\n", \
+ static_cast<int>(g_testlist.size())); \
+ return 0; \
+ }
+
+// Note that this macro uses a FlagSaver to keep tests isolated.
+#define TEST(a, b) \
+ struct Test_##a##_##b { \
+ Test_##a##_##b() { g_testlist.push_back(&Run); } \
+ static void Run() { \
+ FlagSaver fs; \
+ fprintf(stderr, "Running test %s/%s\n", #a, #b); \
+ RunTest(); \
+ } \
+ static void RunTest(); \
+ }; \
+ static Test_##a##_##b g_test_##a##_##b; \
+ void Test_##a##_##b::RunTest()
+
+// This is a dummy class that eases the google->opensource transition.
+namespace testing {
+class Test {};
+}
+
+// Call this in a .cc file where you will later call EXPECT_DEATH
+#define EXPECT_DEATH_INIT \
+ static bool g_called_exit; \
+ static void CalledExit(int) { g_called_exit = true; }
+
+#define EXPECT_DEATH(fn, msg) \
+ do { \
+ g_called_exit = false; \
+ gflags_exitfunc = &CalledExit; \
+ fn; \
+ gflags_exitfunc = &exit; /* set back to its default */ \
+ if (!g_called_exit) { \
+ fprintf(stderr, "Function didn't die (%s): %s\n", msg, #fn); \
+ exit(1); \
+ } \
+ } while (0)
+
+#define GTEST_HAS_DEATH_TEST 1
+
+// -- path routines ----------------------------------------------------------
+
+// Tries to create the directory path as a temp-dir. If it fails,
+// changes path to some directory it *can* create.
+#if defined(__MINGW32__)
+#include <io.h>
+inline void MakeTmpdir(std::string* path) {
+ if (!path->empty()) {
+ path->append("/gflags_unittest_testdir");
+ int err = mkdir(path->c_str());
+ if (err == 0 || errno == EEXIST) return;
+ }
+ // I had trouble creating a directory in /tmp from mingw
+ *path = "./gflags_unittest";
+ mkdir(path->c_str());
+}
+#elif defined(_MSC_VER)
+#include <direct.h>
+inline void MakeTmpdir(std::string* path) {
+ if (!path->empty()) {
+ int err = _mkdir(path->c_str());
+ if (err == 0 || errno == EEXIST) return;
+ }
+ char tmppath_buffer[1024];
+ int tmppath_len = GetTempPathA(sizeof(tmppath_buffer), tmppath_buffer);
+ assert(tmppath_len > 0 && tmppath_len < sizeof(tmppath_buffer));
+ assert(tmppath_buffer[tmppath_len - 1] == '\\'); // API guarantees it
+ *path = std::string(tmppath_buffer) + "gflags_unittest";
+ _mkdir(path->c_str());
+}
+#else
+inline void MakeTmpdir(std::string* path) {
+ if (!path->empty()) {
+ int err = mkdir(path->c_str(), 0755);
+ if (err == 0 || errno == EEXIST) return;
+ }
+ mkdir("/tmp/gflags_unittest", 0755);
+}
+#endif
+
+// -- string routines --------------------------------------------------------
+
+inline void InternalStringPrintf(std::string* output, const char* format,
+ va_list ap) {
+ char space[128]; // try a small buffer and hope it fits
+
+ // It's possible for methods that use a va_list to invalidate
+ // the data in it upon use. The fix is to make a copy
+ // of the structure before using it and use that copy instead.
+ va_list backup_ap;
+ va_copy(backup_ap, ap);
+ int bytes_written = vsnprintf(space, sizeof(space), format, backup_ap);
+ va_end(backup_ap);
+
+ if ((bytes_written >= 0) && (static_cast<size_t>(bytes_written) < sizeof(space))) {
+ output->append(space, bytes_written);
+ return;
+ }
+
+ // Repeatedly increase buffer size until it fits.
+ int length = sizeof(space);
+ while (true) {
+ if (bytes_written < 0) {
+ // Older snprintf() behavior. :-( Just try doubling the buffer size
+ length *= 2;
+ } else {
+ // We need exactly "bytes_written+1" characters
+ length = bytes_written+1;
+ }
+ char* buf = new char[length];
+
+ // Restore the va_list before we use it again
+ va_copy(backup_ap, ap);
+ bytes_written = vsnprintf(buf, length, format, backup_ap);
+ va_end(backup_ap);
+
+ if ((bytes_written >= 0) && (bytes_written < length)) {
+ output->append(buf, bytes_written);
+ delete[] buf;
+ return;
+ }
+ delete[] buf;
+ }
+}
+
+// Clears output before writing to it.
+inline void SStringPrintf(std::string* output, const char* format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ output->clear();
+ InternalStringPrintf(output, format, ap);
+ va_end(ap);
+}
+
+inline void StringAppendF(std::string* output, const char* format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ InternalStringPrintf(output, format, ap);
+ va_end(ap);
+}
+
+inline std::string StringPrintf(const char* format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ std::string output;
+ InternalStringPrintf(&output, format, ap);
+ va_end(ap);
+ return output;
+}
+
+inline bool SafeGetEnv(const char *varname, std::string &valstr)
+{
+#if defined(_MSC_VER) && _MSC_VER >= 1400
+ char *val;
+ size_t sz;
+ if (_dupenv_s(&val, &sz, varname) != 0 || !val) return false;
+ valstr = val;
+ free(val);
+#else
+ const char * const val = getenv(varname);
+ if (!val) return false;
+ valstr = val;
+#endif
+ return true;
+}
+
+inline int SafeFOpen(FILE **fp, const char* fname, const char *mode)
+{
+#if defined(_MSC_VER) && _MSC_VER >= 1400
+ return fopen_s(fp, fname, mode);
+#else
+ assert(fp != NULL);
+ *fp = fopen(fname, mode);
+ // errno only guaranteed to be set on failure
+ return ((*fp == NULL) ? errno : 0);
+#endif
+}
+
+
+} // namespace GFLAGS_NAMESPACE
+
+
+#endif // GFLAGS_UTIL_H_
diff --git a/third_party/gflags/src/windows_port.cc b/third_party/gflags/src/windows_port.cc
new file mode 100644
index 0000000..9ccb630
--- /dev/null
+++ b/third_party/gflags/src/windows_port.cc
@@ -0,0 +1,75 @@
+/* Copyright (c) 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * ---
+ * Author: Craig Silverstein
+ */
+
+#ifndef _WIN32
+# error You should only be including windows/port.cc in a windows environment!
+#endif
+
+#include <string.h> // for strlen(), memset(), memcmp()
+#include <assert.h>
+#include <stdarg.h> // for va_list, va_start, va_end
+#include <windows.h>
+
+#include "windows_port.h"
+
+// These call the windows _vsnprintf, but always NUL-terminate.
+#if !defined(__MINGW32__) && !defined(__MINGW64__) /* mingw already defines */
+
+#ifdef _MSC_VER
+# pragma warning(push)
+# pragma warning(disable: 4996) // ignore _vsnprintf security warning
+#endif
+int safe_vsnprintf(char *str, size_t size, const char *format, va_list ap) {
+ if (size == 0) // not even room for a \0?
+ return -1; // not what C99 says to do, but what windows does
+ str[size-1] = '\0';
+ return _vsnprintf(str, size-1, format, ap);
+}
+#ifdef _MSC_VER
+# pragma warning(pop)
+#endif
+
+#if _MSC_VER < 1900 // msvs 2015 finally includes snprintf
+
+int snprintf(char *str, size_t size, const char *format, ...) {
+ int r;
+ va_list ap;
+ va_start(ap, format);
+ r = vsnprintf(str, size, format, ap);
+ va_end(ap);
+ return r;
+}
+
+#endif
+
+#endif /* #if !defined(__MINGW32__) && !defined(__MINGW64__) */
diff --git a/third_party/gflags/src/windows_port.h b/third_party/gflags/src/windows_port.h
new file mode 100644
index 0000000..c8ff24f
--- /dev/null
+++ b/third_party/gflags/src/windows_port.h
@@ -0,0 +1,127 @@
+/* Copyright (c) 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * ---
+ * Author: Craig Silverstein
+ *
+ * These are some portability typedefs and defines to make it a bit
+ * easier to compile this code under VC++.
+ *
+ * Several of these are taken from glib:
+ * http://developer.gnome.org/doc/API/glib/glib-windows-compatability-functions.html
+ */
+
+#ifndef GFLAGS_WINDOWS_PORT_H_
+#define GFLAGS_WINDOWS_PORT_H_
+
+#include "config.h"
+
+// This must be defined before the windows.h is included.
+// It's needed for mutex.h, to give access to the TryLock method.
+# if !defined(_WIN32_WINNT) && !(defined( __MINGW32__) || defined(__MINGW64__))
+# define _WIN32_WINNT 0x0400
+# endif
+// We always want minimal includes
+#ifndef WIN32_LEAN_AND_MEAN
+# define WIN32_LEAN_AND_MEAN
+#endif
+#include <windows.h>
+#include <direct.h> /* for mkdir */
+#include <stdlib.h> /* for _putenv, getenv */
+#include <stdio.h> /* need this to override stdio's snprintf, also defines _unlink used by unit tests */
+#include <stdarg.h> /* util.h uses va_copy */
+#include <string.h> /* for _stricmp and _strdup */
+
+/* We can't just use _vsnprintf and _snprintf as drop-in-replacements,
+ * because they don't always NUL-terminate. :-( We also can't use the
+ * name vsnprintf, since windows defines that (but not snprintf (!)).
+ */
+#if !defined(__MINGW32__) && !defined(__MINGW64__) /* mingw already defines */
+extern GFLAGS_DLL_DECL int snprintf(char *str, size_t size,
+ const char *format, ...);
+extern int GFLAGS_DLL_DECL safe_vsnprintf(char *str, size_t size,
+ const char *format, va_list ap);
+#define vsnprintf(str, size, format, ap) safe_vsnprintf(str, size, format, ap)
+#define va_copy(dst, src) (dst) = (src)
+#endif /* #if !defined(__MINGW32__) && !defined(__MINGW64__) */
+
+#ifdef _MSC_VER
+# pragma warning(push)
+# pragma warning(disable: 4996) // ignore getenv security warning
+#endif
+inline void setenv(const char* name, const char* value, int) {
+ // In windows, it's impossible to set a variable to the empty string.
+ // We handle this by setting it to "0" and the NUL-ing out the \0.
+ // That is, we putenv("FOO=0") and then find out where in memory the
+ // putenv wrote "FOO=0", and change it in-place to "FOO=\0".
+ // c.f. http://svn.apache.org/viewvc/stdcxx/trunk/tests/src/environ.cpp?r1=611451&r2=637508&pathrev=637508
+ static const char* const kFakeZero = "0";
+ if (*value == '\0')
+ value = kFakeZero;
+ // Apparently the semantics of putenv() is that the input
+ // must live forever, so we leak memory here. :-(
+ const size_t nameval_len = strlen(name) + 1 + strlen(value) + 1;
+ char* nameval = reinterpret_cast<char*>(malloc(nameval_len));
+ snprintf(nameval, nameval_len, "%s=%s", name, value);
+ _putenv(nameval);
+ if (value == kFakeZero) {
+ nameval[nameval_len - 2] = '\0'; // works when putenv() makes no copy
+ if (*getenv(name) != '\0')
+ *getenv(name) = '\0'; // works when putenv() copies nameval
+ }
+}
+#ifdef _MSC_VER
+# pragma warning(pop)
+#endif
+
+#define strcasecmp _stricmp
+
+#if defined(_MSC_VER) && _MSC_VER >= 1400
+#define strdup _strdup
+#define unlink _unlink
+#endif
+
+#define PRId32 "d"
+#define PRIu32 "u"
+#define PRId64 "I64d"
+#define PRIu64 "I64u"
+
+#if !defined(__MINGW32__) && !defined(__MINGW64__)
+#define strtoq _strtoi64
+#define strtouq _strtoui64
+#define strtoll _strtoi64
+#define strtoull _strtoui64
+#define atoll _atoi64
+#endif
+
+#ifndef PATH_MAX
+#define PATH_MAX 1024
+#endif
+
+#endif /* GFLAGS_WINDOWS_PORT_H_ */
diff --git a/third_party/gflags/test/CMakeLists.txt b/third_party/gflags/test/CMakeLists.txt
new file mode 100644
index 0000000..77b582f
--- /dev/null
+++ b/third_party/gflags/test/CMakeLists.txt
@@ -0,0 +1,205 @@
+## gflags tests
+
+# ----------------------------------------------------------------------------
+# output directories
+set (CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/bin")
+set (CMAKE_LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/lib")
+set (CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/lib")
+
+# set working directory of test commands
+set (GFLAGS_FLAGFILES_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
+
+# ----------------------------------------------------------------------------
+# common include directories and link libraries
+include_directories ("${CMAKE_CURRENT_SOURCE_DIR}")
+include_directories ("${gflags_SOURCE_DIR}/src")
+include_directories ("${gflags_BINARY_DIR}/include")
+include_directories ("${gflags_BINARY_DIR}/include/gflags")
+
+if (BUILD_SHARED_LIBS)
+ set (type shared)
+else ()
+ set (type static)
+endif ()
+if (BUILD_gflags_LIB)
+ link_libraries (gflags-${type})
+else ()
+ link_libraries (gflags_nothreads-${type})
+endif ()
+
+# ----------------------------------------------------------------------------
+# STRIP_FLAG_HELP
+add_executable (gflags_strip_flags_test gflags_strip_flags_test.cc)
+# Make sure the --help output doesn't print the stripped text.
+add_gflags_test (strip_flags_help 1 "" "This text should be stripped out" gflags_strip_flags_test --help)
+# Make sure the stripped text isn't in the binary at all.
+add_test (
+ NAME strip_flags_binary
+ COMMAND "${CMAKE_COMMAND}" "-DBINARY=$<TARGET_FILE:gflags_strip_flags_test>"
+ -P "${CMAKE_CURRENT_SOURCE_DIR}/gflags_strip_flags_test.cmake"
+)
+
+# ----------------------------------------------------------------------------
+# unit tests
+configure_file (gflags_unittest.cc gflags_unittest-main.cc COPYONLY)
+configure_file (gflags_unittest.cc gflags_unittest_main.cc COPYONLY)
+
+add_executable (gflags_unittest gflags_unittest.cc)
+add_executable (gflags_unittest-main gflags_unittest-main.cc)
+add_executable (gflags_unittest_main gflags_unittest_main.cc)
+
+if (OS_WINDOWS)
+ set (SLASH "\\\\")
+else ()
+ set (SLASH "/")
+endif ()
+
+# First, just make sure the gflags_unittest works as-is
+add_gflags_test(unittest 0 "" "" gflags_unittest)
+
+# --help should show all flags, including flags from gflags_reporting
+add_gflags_test(help-reporting 1 "${SLASH}gflags_reporting.cc:" "" gflags_unittest --help)
+
+# Make sure that --help prints even very long helpstrings.
+add_gflags_test(long-helpstring 1 "end of a long helpstring" "" gflags_unittest --help)
+
+# Make sure --help reflects flag changes made before flag-parsing
+add_gflags_test(changed_bool1 1 "-changed_bool1 (changed) type: bool default: true" "" gflags_unittest --help)
+add_gflags_test(changed_bool2 1 "-changed_bool2 (changed) type: bool default: false currently: true" "" gflags_unittest --help)
+# And on the command-line, too
+add_gflags_test(changeable_string_var 1 "-changeable_string_var () type: string default: \"1\" currently: \"2\"" "" gflags_unittest --changeable_string_var 2 --help)
+
+# --nohelp and --help=false should be as if we didn't say anything
+add_gflags_test(nohelp 0 "PASS" "" gflags_unittest --nohelp)
+add_gflags_test(help=false 0 "PASS" "" gflags_unittest --help=false)
+
+# --helpfull is the same as help
+add_gflags_test(helpfull 1 "${SLASH}gflags_reporting.cc:" "" gflags_unittest --helpfull)
+
+# --helpshort should show only flags from the gflags_unittest itself
+add_gflags_test(helpshort 1 "${SLASH}gflags_unittest.cc:" "${SLASH}gflags_reporting.cc:" gflags_unittest --helpshort)
+
+# --helpshort should show the tldflag we created in the gflags_unittest dir
+add_gflags_test(helpshort-tldflag1 1 "tldflag1" "${SLASH}google.cc:" gflags_unittest --helpshort)
+add_gflags_test(helpshort-tldflag2 1 "tldflag2" "${SLASH}google.cc:" gflags_unittest --helpshort)
+
+# --helpshort should work if the main source file is suffixed with [_-]main
+add_gflags_test(helpshort-main 1 "${SLASH}gflags_unittest-main.cc:" "${SLASH}gflags_reporting.cc:" gflags_unittest-main --helpshort)
+add_gflags_test(helpshort_main 1 "${SLASH}gflags_unittest_main.cc:" "${SLASH}gflags_reporting.cc:" gflags_unittest_main --helpshort)
+
+# --helpon needs an argument
+add_gflags_test(helpon 1 "'--helpon' is missing its argument; flag description: show help on" "" gflags_unittest --helpon)
+# --helpon argument indicates what file we'll show args from
+add_gflags_test(helpon=gflags 1 "${SLASH}gflags.cc:" "${SLASH}gflags_unittest.cc:" gflags_unittest --helpon=gflags)
+# another way of specifying the argument
+add_gflags_test(helpon_gflags 1 "${SLASH}gflags.cc:" "${SLASH}gflags_unittest.cc:" gflags_unittest --helpon gflags)
+# test another argument
+add_gflags_test(helpon=gflags_unittest 1 "${SLASH}gflags_unittest.cc:" "${SLASH}gflags.cc:" gflags_unittest --helpon=gflags_unittest)
+
+# helpmatch is like helpon but takes substrings
+add_gflags_test(helpmatch_reporting 1 "${SLASH}gflags_reporting.cc:" "${SLASH}gflags_unittest.cc:" gflags_unittest -helpmatch reporting)
+add_gflags_test(helpmatch=unittest 1 "${SLASH}gflags_unittest.cc:" "${SLASH}gflags.cc:" gflags_unittest -helpmatch=unittest)
+
+# if no flags are found with helpmatch or helpon, suggest --help
+add_gflags_test(helpmatch=nosuchsubstring 1 "No modules matched" "${SLASH}gflags_unittest.cc:" gflags_unittest -helpmatch=nosuchsubstring)
+add_gflags_test(helpon=nosuchmodule 1 "No modules matched" "${SLASH}gflags_unittest.cc:" gflags_unittest -helpon=nosuchmodule)
+
+# helppackage shows all the flags in the same dir as this unittest
+# --help should show all flags, including flags from google.cc
+add_gflags_test(helppackage 1 "${SLASH}gflags_reporting.cc:" "" gflags_unittest --helppackage)
+
+# xml!
+add_gflags_test(helpxml 1 "${SLASH}gflags_unittest.cc</file>" "${SLASH}gflags_unittest.cc:" gflags_unittest --helpxml)
+
+# just print the version info and exit
+add_gflags_test(version-1 0 "gflags_unittest" "${SLASH}gflags_unittest.cc:" gflags_unittest --version)
+add_gflags_test(version-2 0 "version test_version" "${SLASH}gflags_unittest.cc:" gflags_unittest --version)
+
+# --undefok is a fun flag...
+add_gflags_test(undefok-1 1 "unknown command line flag 'foo'" "" gflags_unittest --undefok= --foo --unused_bool)
+add_gflags_test(undefok-2 0 "PASS" "" gflags_unittest --undefok=foo --foo --unused_bool)
+# If you say foo is ok to be undefined, we'll accept --nofoo as well
+add_gflags_test(undefok-3 0 "PASS" "" gflags_unittest --undefok=foo --nofoo --unused_bool)
+# It's ok if the foo is in the middle
+add_gflags_test(undefok-4 0 "PASS" "" gflags_unittest --undefok=fee,fi,foo,fum --foo --unused_bool)
+# But the spelling has to be just right...
+add_gflags_test(undefok-5 1 "unknown command line flag 'foo'" "" gflags_unittest --undefok=fo --foo --unused_bool)
+add_gflags_test(undefok-6 1 "unknown command line flag 'foo'" "" gflags_unittest --undefok=foot --foo --unused_bool)
+
+# See if we can successfully load our flags from the flagfile
+add_gflags_test(flagfile.1 0 "gflags_unittest" "${SLASH}gflags_unittest.cc:" gflags_unittest "--flagfile=flagfile.1")
+add_gflags_test(flagfile.2 0 "PASS" "" gflags_unittest "--flagfile=flagfile.2")
+add_gflags_test(flagfile.3 0 "PASS" "" gflags_unittest "--flagfile=flagfile.3")
+
+# Also try to load flags from the environment
+add_gflags_test(fromenv=version 0 "gflags_unittest" "${SLASH}gflags_unittest.cc:" gflags_unittest --fromenv=version)
+add_gflags_test(tryfromenv=version 0 "gflags_unittest" "${SLASH}gflags_unittest.cc:" gflags_unittest --tryfromenv=version)
+add_gflags_test(fromenv=help 0 "PASS" "" gflags_unittest --fromenv=help)
+add_gflags_test(tryfromenv=help 0 "PASS" "" gflags_unittest --tryfromenv=help)
+add_gflags_test(fromenv=helpfull 1 "helpfull not found in environment" "" gflags_unittest --fromenv=helpfull)
+add_gflags_test(tryfromenv=helpfull 0 "PASS" "" gflags_unittest --tryfromenv=helpfull)
+add_gflags_test(tryfromenv=undefok 0 "PASS" "" gflags_unittest --tryfromenv=undefok --foo)
+add_gflags_test(tryfromenv=weirdo 1 "unknown command line flag" "" gflags_unittest --tryfromenv=weirdo)
+add_gflags_test(tryfromenv-multiple 0 "gflags_unittest" "${SLASH}gflags_unittest.cc:" gflags_unittest --tryfromenv=test_bool,version,unused_bool)
+add_gflags_test(fromenv=test_bool 1 "not found in environment" "" gflags_unittest --fromenv=test_bool)
+add_gflags_test(fromenv=test_bool-ok 1 "unknown command line flag" "" gflags_unittest --fromenv=test_bool,ok)
+# Here, the --version overrides the fromenv
+add_gflags_test(version-overrides-fromenv 0 "gflags_unittest" "${SLASH}gflags_unittest.cc:" gflags_unittest --fromenv=test_bool,version,ok)
+
+# Make sure -- by itself stops argv processing
+add_gflags_test(dashdash 0 "PASS" "" gflags_unittest -- --help)
+
+# And we should die if the flag value doesn't pass the validator
+add_gflags_test(always_fail 1 "ERROR: failed validation of new value 'true' for flag 'always_fail'" "" gflags_unittest --always_fail)
+
+# And if locking in validators fails
+# TODO(andreas): Worked on Windows 7 Release configuration, but causes
+# debugger abort() intervention in case of Debug configuration.
+#add_gflags_test(deadlock_if_cant_lock 0 "PASS" "" gflags_unittest --deadlock_if_cant_lock)
+
+# ----------------------------------------------------------------------------
+# use gflags_declare.h
+add_executable (gflags_declare_test gflags_declare_test.cc gflags_declare_flags.cc)
+
+add_test(NAME gflags_declare COMMAND gflags_declare_test --message "Hello gflags!")
+set_tests_properties(gflags_declare PROPERTIES PASS_REGULAR_EXPRESSION "Hello gflags!")
+
+# ----------------------------------------------------------------------------
+# configure Python script which configures and builds a test project
+if (BUILD_NC_TESTS OR BUILD_CONFIG_TESTS)
+ find_package (PythonInterp)
+ if (NOT PYTHON_EXECUTABLE)
+ message (FATAL_ERROR "No Python installation found! It is required by the (negative) compilation tests."
+ " Either install Python or set BUILD_NC_TESTS and BUILD_CONFIG_TESTS to FALSE.")
+ endif ()
+ set (TMPDIR "${PROJECT_BINARY_DIR}/Testing/Temporary")
+ configure_file (gflags_build.py.in "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/build.py" @ONLY)
+ function (add_gflags_build_test name srcdir expect_fail)
+ set (srcdir "${CMAKE_CURRENT_SOURCE_DIR}/${srcdir}")
+ add_test (
+ NAME "${name}"
+ COMMAND "${PYTHON_EXECUTABLE}" "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/build.py"
+ ${name} ${srcdir} ${expect_fail}
+ )
+ endfunction ()
+endif ()
+
+# ----------------------------------------------------------------------------
+# negative compilation tests
+option (BUILD_NC_TESTS "Request addition of negative compilation tests." OFF)
+mark_as_advanced (BUILD_NC_TESTS)
+if (BUILD_NC_TESTS)
+ add_gflags_build_test (nc_sanity nc 0)
+ add_gflags_build_test (nc_swapped_args nc 1)
+ add_gflags_build_test (nc_int_instead_of_bool nc 1)
+ add_gflags_build_test (nc_bool_in_quotes nc 1)
+ add_gflags_build_test (nc_define_string_with_0 nc 1)
+endif ()
+
+# ----------------------------------------------------------------------------
+# build configuration test
+option (BUILD_CONFIG_TESTS "Request addition of package configuration tests." OFF)
+mark_as_advanced (BUILD_CONFIG_TESTS)
+if (BUILD_CONFIG_TESTS)
+ add_gflags_build_test (cmake_config config 0)
+endif ()
diff --git a/third_party/gflags/test/config/CMakeLists.txt b/third_party/gflags/test/config/CMakeLists.txt
new file mode 100644
index 0000000..b5bc282
--- /dev/null
+++ b/third_party/gflags/test/config/CMakeLists.txt
@@ -0,0 +1,10 @@
+## gflags package configuration tests
+
+cmake_minimum_required (VERSION 2.8)
+
+project (gflags_${TEST_NAME})
+
+find_package (gflags REQUIRED)
+
+add_executable (foo main.cc)
+target_link_libraries (foo gflags)
diff --git a/third_party/gflags/test/config/main.cc b/third_party/gflags/test/config/main.cc
new file mode 100644
index 0000000..9b2b898
--- /dev/null
+++ b/third_party/gflags/test/config/main.cc
@@ -0,0 +1,12 @@
+#include <iostream>
+#include <gflags/gflags.h>
+
+DEFINE_string(message, "Hello World!", "The message to print");
+
+int main(int argc, char **argv)
+{
+ gflags::SetUsageMessage("Test CMake configuration of gflags library (gflags-config.cmake)");
+ gflags::ParseCommandLineFlags(&argc, &argv, true);
+ std::cout << FLAGS_message << std::endl;
+ return 0;
+}
diff --git a/third_party/gflags/test/config_for_unittests.h b/third_party/gflags/test/config_for_unittests.h
new file mode 100644
index 0000000..914571b
--- /dev/null
+++ b/third_party/gflags/test/config_for_unittests.h
@@ -0,0 +1,79 @@
+// Copyright (c) 2007, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// ---
+// All Rights Reserved.
+//
+//
+// This file is needed for windows -- unittests are not part of the
+// gflags dll, but still want to include config.h just like the
+// dll does, so they can use internal tools and APIs for testing.
+//
+// The problem is that config.h declares GFLAGS_DLL_DECL to be
+// for exporting symbols, but the unittest needs to *import* symbols
+// (since it's not the dll).
+//
+// The solution is to have this file, which is just like config.h but
+// sets GFLAGS_DLL_DECL to do a dllimport instead of a dllexport.
+//
+// The reason we need this extra GFLAGS_DLL_DECL_FOR_UNITTESTS
+// variable is in case people want to set GFLAGS_DLL_DECL explicitly
+// to something other than __declspec(dllexport). In that case, they
+// may want to use something other than __declspec(dllimport) for the
+// unittest case. For that, we allow folks to define both
+// GFLAGS_DLL_DECL and GFLAGS_DLL_DECL_FOR_UNITTESTS explicitly.
+//
+// NOTE: This file is equivalent to config.h on non-windows systems,
+// which never defined GFLAGS_DLL_DECL_FOR_UNITTESTS and always
+// define GFLAGS_DLL_DECL to the empty string.
+
+#include "config.h"
+
+#ifdef GFLAGS_DLL_DECL
+# undef GFLAGS_DLL_DECL
+#endif
+#ifdef GFLAGS_DLL_DEFINE_FLAG
+# undef GFLAGS_DLL_DEFINE_FLAG
+#endif
+#ifdef GFLAGS_DLL_DECLARE_FLAG
+# undef GFLAGS_DLL_DECLARE_FLAG
+#endif
+
+#ifdef GFLAGS_DLL_DECL_FOR_UNITTESTS
+# define GFLAGS_DLL_DECL GFLAGS_DLL_DECL_FOR_UNITTESTS
+#else
+# define GFLAGS_DLL_DECL // if DLL_DECL_FOR_UNITTESTS isn't defined, use ""
+#endif
+
+// Import flags defined by gflags.cc
+#if GFLAGS_IS_A_DLL && defined(_MSC_VER)
+# define GFLAGS_DLL_DECLARE_FLAG __declspec(dllimport)
+#else
+# define GFLAGS_DLL_DECLARE_FLAG
+#endif
\ No newline at end of file
diff --git a/third_party/gflags/test/flagfile.1 b/third_party/gflags/test/flagfile.1
new file mode 100644
index 0000000..e0f9217
--- /dev/null
+++ b/third_party/gflags/test/flagfile.1
@@ -0,0 +1 @@
+--version
\ No newline at end of file
diff --git a/third_party/gflags/test/flagfile.2 b/third_party/gflags/test/flagfile.2
new file mode 100644
index 0000000..864f8e8
--- /dev/null
+++ b/third_party/gflags/test/flagfile.2
@@ -0,0 +1,2 @@
+--foo=bar
+--nounused_bool
\ No newline at end of file
diff --git a/third_party/gflags/test/flagfile.3 b/third_party/gflags/test/flagfile.3
new file mode 100644
index 0000000..76d92bb
--- /dev/null
+++ b/third_party/gflags/test/flagfile.3
@@ -0,0 +1 @@
+--flagfile=flagfile.2
\ No newline at end of file
diff --git a/third_party/gflags/test/gflags_build.py.in b/third_party/gflags/test/gflags_build.py.in
new file mode 100644
index 0000000..a8cba2b
--- /dev/null
+++ b/third_party/gflags/test/gflags_build.py.in
@@ -0,0 +1,43 @@
+#!/usr/bin/env python
+
+import os
+import sys
+import subprocess
+import shutil
+
+CMAKE = '@CMAKE_COMMAND@'
+CMAKE_BUILD_TYPE = '@CMAKE_BUILD_TYPE@'
+TMPDIR = '@TMPDIR@'
+SRCDIR = '@SRCDIR@'
+GFLAGS_DIR = '@gflags_BINARY_DIR@'
+
+if __name__ == "__main__":
+ if len(sys.argv) != 4:
+ sys.stderr.write(' '.join(['usage:', sys.argv[0], '<test_name> <srcdir> <expect_fail:0|1>\n']))
+ sys.exit(1)
+ test_name = sys.argv[1]
+ srcdir = sys.argv[2]
+ expect_fail = (sys.argv[3].lower() in ['true', 'yes', 'on', '1'])
+ bindir = os.path.join(TMPDIR, test_name)
+ if TMPDIR == '':
+ sys.stderr.write('Temporary directory not set!\n')
+ sys.exit(1)
+ # create build directory
+ if os.path.isdir(bindir): shutil.rmtree(bindir)
+ os.makedirs(bindir)
+ # configure the build tree
+ if subprocess.call([CMAKE, '-DCMAKE_BUILD_TYPE:STRING='+CMAKE_BUILD_TYPE,
+ '-Dgflags_DIR:PATH='+GFLAGS_DIR,
+ '-DTEST_NAME:STRING='+test_name, srcdir], cwd=bindir) != 0:
+ sys.stderr.write('Failed to configure the build tree!\n')
+ sys.exit(1)
+ # build the test project
+ exit_code = subprocess.call([CMAKE, '--build', bindir, '--config', CMAKE_BUILD_TYPE], cwd=bindir)
+ if expect_fail == True:
+ if exit_code == 0:
+ sys.stderr.write('Build expected to fail, but it succeeded!\n')
+ sys.exit(1)
+ else:
+ sys.stderr.write('Build failed as expected\n')
+ exit_code = 0
+ sys.exit(exit_code)
diff --git a/third_party/gflags/test/gflags_declare_flags.cc b/third_party/gflags/test/gflags_declare_flags.cc
new file mode 100644
index 0000000..dc53de5
--- /dev/null
+++ b/third_party/gflags/test/gflags_declare_flags.cc
@@ -0,0 +1,9 @@
+#include <iostream>
+#include <gflags/gflags_declare.h>
+
+DECLARE_string(message); // in gflags_delcare_test.cc
+
+void print_message()
+{
+ std::cout << FLAGS_message << std::endl;
+}
diff --git a/third_party/gflags/test/gflags_declare_test.cc b/third_party/gflags/test/gflags_declare_test.cc
new file mode 100644
index 0000000..707bcc0
--- /dev/null
+++ b/third_party/gflags/test/gflags_declare_test.cc
@@ -0,0 +1,12 @@
+#include <gflags/gflags.h>
+
+DEFINE_string(message, "", "The message to print");
+void print_message(); // in gflags_declare_flags.cc
+
+int main(int argc, char **argv)
+{
+ gflags::SetUsageMessage("Test compilation and use of gflags_declare.h");
+ gflags::ParseCommandLineFlags(&argc, &argv, true);
+ print_message();
+ return 0;
+}
diff --git a/third_party/gflags/test/gflags_strip_flags_test.cc b/third_party/gflags/test/gflags_strip_flags_test.cc
new file mode 100644
index 0000000..25ef53a
--- /dev/null
+++ b/third_party/gflags/test/gflags_strip_flags_test.cc
@@ -0,0 +1,61 @@
+// Copyright (c) 2011, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---
+// Author: csilvers@google.com (Craig Silverstein)
+//
+// A simple program that uses STRIP_FLAG_HELP. We'll have a shell
+// script that runs 'strings' over this program and makes sure
+// that the help string is not in there.
+
+#include "config_for_unittests.h"
+#define STRIP_FLAG_HELP 1
+#include <gflags/gflags.h>
+
+#include <stdio.h>
+
+using GFLAGS_NAMESPACE::SetUsageMessage;
+using GFLAGS_NAMESPACE::ParseCommandLineFlags;
+
+
+DEFINE_bool(test, true, "This text should be stripped out");
+
+int main(int argc, char** argv) {
+ SetUsageMessage("Usage message");
+ ParseCommandLineFlags(&argc, &argv, false);
+
+ // Unfortunately, for us, libtool can replace executables with a shell
+ // script that does some work before calling the 'real' executable
+ // under a different name. We need the 'real' executable name to run
+ // 'strings' on it, so we construct this binary to print the real
+ // name (argv[0]) on stdout when run.
+ puts(argv[0]);
+
+ return 0;
+}
diff --git a/third_party/gflags/test/gflags_strip_flags_test.cmake b/third_party/gflags/test/gflags_strip_flags_test.cmake
new file mode 100644
index 0000000..bd419c4
--- /dev/null
+++ b/third_party/gflags/test/gflags_strip_flags_test.cmake
@@ -0,0 +1,7 @@
+if (NOT BINARY)
+ message (FATAl_ERROR "BINARY file to check not specified!")
+endif ()
+file (STRINGS "${BINARY}" strings REGEX "This text should be stripped out")
+if (strings)
+ message (FATAL_ERROR "Text not stripped from binary like it should be: ${BINARY}")
+endif ()
\ No newline at end of file
diff --git a/third_party/gflags/test/gflags_unittest.cc b/third_party/gflags/test/gflags_unittest.cc
new file mode 100644
index 0000000..80f7398
--- /dev/null
+++ b/third_party/gflags/test/gflags_unittest.cc
@@ -0,0 +1,1536 @@
+// Copyright (c) 2005, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// ---
+//
+// For now, this unit test does not cover all features of
+// gflags.cc
+
+#include "config_for_unittests.h"
+#include <gflags/gflags.h>
+
+#include <math.h> // for isinf() and isnan()
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef HAVE_UNISTD_H
+# include <unistd.h> // for unlink()
+#endif
+#include <vector>
+#include <string>
+#include "util.h"
+TEST_INIT
+EXPECT_DEATH_INIT
+
+// I don't actually use this header file, but #include it under the
+// old location to make sure that the include-header-forwarding
+// works. But don't bother on windows; the windows port is so new
+// it never had the old location-names.
+#ifndef _MSC_VER
+#include <gflags/gflags_completions.h>
+void (*unused_fn)() = &GFLAGS_NAMESPACE::HandleCommandLineCompletions;
+#endif
+
+using std::string;
+using std::vector;
+using GFLAGS_NAMESPACE::int32;
+using GFLAGS_NAMESPACE::FlagRegisterer;
+using GFLAGS_NAMESPACE::StringFromEnv;
+using GFLAGS_NAMESPACE::RegisterFlagValidator;
+using GFLAGS_NAMESPACE::CommandLineFlagInfo;
+using GFLAGS_NAMESPACE::GetAllFlags;
+
+DEFINE_string(test_tmpdir, "", "Dir we use for temp files");
+DEFINE_string(srcdir, StringFromEnv("SRCDIR", "."), "Source-dir root, needed to find gflags_unittest_flagfile");
+
+DECLARE_string(tryfromenv); // in gflags.cc
+
+DEFINE_bool(test_bool, false, "tests bool-ness");
+DEFINE_int32(test_int32, -1, "");
+DEFINE_int64(test_int64, -2, "");
+DEFINE_uint64(test_uint64, 2, "");
+DEFINE_double(test_double, -1.0, "");
+DEFINE_string(test_string, "initial", "");
+
+//
+// The below ugliness gets some additional code coverage in the -helpxml
+// and -helpmatch test cases having to do with string lengths and formatting
+//
+DEFINE_bool(test_bool_with_quite_quite_quite_quite_quite_quite_quite_quite_quite_quite_quite_quite_quite_quite_long_name,
+ false,
+ "extremely_extremely_extremely_extremely_extremely_extremely_extremely_extremely_long_meaning");
+
+DEFINE_string(test_str1, "initial", "");
+DEFINE_string(test_str2, "initial", "");
+DEFINE_string(test_str3, "initial", "");
+
+// This is used to test setting tryfromenv manually
+DEFINE_string(test_tryfromenv, "initial", "");
+
+// Don't try this at home!
+static int changeable_var = 12;
+DEFINE_int32(changeable_var, ++changeable_var, "");
+
+static int changeable_bool_var = 8008;
+DEFINE_bool(changeable_bool_var, ++changeable_bool_var == 8009, "");
+
+static int changeable_string_var = 0;
+static string ChangeableString() {
+ char r[] = {static_cast<char>('0' + ++changeable_string_var), '\0'};
+ return r;
+}
+DEFINE_string(changeable_string_var, ChangeableString(), "");
+
+// These are never used in this unittest, but can be used by
+// gflags_unittest.sh when it needs to specify flags
+// that are legal for gflags_unittest but don't need to
+// be a particular value.
+DEFINE_bool(unused_bool, true, "unused bool-ness");
+DEFINE_int32(unused_int32, -1001, "");
+DEFINE_int64(unused_int64, -2001, "");
+DEFINE_uint64(unused_uint64, 2000, "");
+DEFINE_double(unused_double, -1000.0, "");
+DEFINE_string(unused_string, "unused", "");
+
+// These flags are used by gflags_unittest.sh
+DEFINE_bool(changed_bool1, false, "changed");
+DEFINE_bool(changed_bool2, false, "changed");
+DEFINE_bool(long_helpstring, false,
+ "This helpstring goes on forever and ever and ever and ever and "
+ "ever and ever and ever and ever and ever and ever and ever and "
+ "ever and ever and ever and ever and ever and ever and ever and "
+ "ever and ever and ever and ever and ever and ever and ever and "
+ "ever and ever and ever and ever and ever and ever and ever and "
+ "ever and ever and ever and ever and ever and ever and ever and "
+ "ever and ever and ever and ever and ever and ever and ever and "
+ "ever and ever and ever and ever and ever and ever and ever and "
+ "ever and ever and ever and ever and ever and ever and ever and "
+ "ever and ever and ever and ever and ever and ever and ever and "
+ "ever. This is the end of a long helpstring");
+
+
+static bool AlwaysFail(const char* flag, bool value) { return value == false; }
+DEFINE_bool(always_fail, false, "will fail to validate when you set it");
+DEFINE_validator(always_fail, AlwaysFail);
+
+// See the comment by GetAllFlags in gflags.h
+static bool DeadlockIfCantLockInValidators(const char* flag, bool value) {
+ if (!value) {
+ return true;
+ }
+ vector<CommandLineFlagInfo> dummy;
+ GetAllFlags(&dummy);
+ return true;
+}
+DEFINE_bool(deadlock_if_cant_lock,
+ false,
+ "will deadlock if set to true and "
+ "if locking of registry in validators fails.");
+DEFINE_validator(deadlock_if_cant_lock, DeadlockIfCantLockInValidators);
+
+#define MAKEFLAG(x) DEFINE_int32(test_flag_num##x, x, "Test flag")
+
+// Define 10 flags
+#define MAKEFLAG10(x) \
+ MAKEFLAG(x##0); \
+ MAKEFLAG(x##1); \
+ MAKEFLAG(x##2); \
+ MAKEFLAG(x##3); \
+ MAKEFLAG(x##4); \
+ MAKEFLAG(x##5); \
+ MAKEFLAG(x##6); \
+ MAKEFLAG(x##7); \
+ MAKEFLAG(x##8); \
+ MAKEFLAG(x##9)
+
+// Define 100 flags
+#define MAKEFLAG100(x) \
+ MAKEFLAG10(x##0); \
+ MAKEFLAG10(x##1); \
+ MAKEFLAG10(x##2); \
+ MAKEFLAG10(x##3); \
+ MAKEFLAG10(x##4); \
+ MAKEFLAG10(x##5); \
+ MAKEFLAG10(x##6); \
+ MAKEFLAG10(x##7); \
+ MAKEFLAG10(x##8); \
+ MAKEFLAG10(x##9)
+
+// Define a bunch of command-line flags. Each occurrence of the MAKEFLAG100
+// macro defines 100 integer flags. This lets us test the effect of having
+// many flags on startup time.
+MAKEFLAG100(1);
+MAKEFLAG100(2);
+MAKEFLAG100(3);
+MAKEFLAG100(4);
+MAKEFLAG100(5);
+MAKEFLAG100(6);
+MAKEFLAG100(7);
+MAKEFLAG100(8);
+MAKEFLAG100(9);
+MAKEFLAG100(10);
+MAKEFLAG100(11);
+MAKEFLAG100(12);
+MAKEFLAG100(13);
+MAKEFLAG100(14);
+MAKEFLAG100(15);
+
+#undef MAKEFLAG100
+#undef MAKEFLAG10
+#undef MAKEFLAG
+
+// This is a pseudo-flag -- we want to register a flag with a filename
+// at the top level, but there is no way to do this except by faking
+// the filename.
+namespace fLI {
+ static const int32 FLAGS_nonotldflag1 = 12;
+ int32 FLAGS_tldflag1 = FLAGS_nonotldflag1;
+ int32 FLAGS_notldflag1 = FLAGS_nonotldflag1;
+ static FlagRegisterer o_tldflag1(
+ "tldflag1", "int32",
+ "should show up in --helpshort", "gflags_unittest.cc",
+ &FLAGS_tldflag1, &FLAGS_notldflag1);
+}
+using fLI::FLAGS_tldflag1;
+
+namespace fLI {
+ static const int32 FLAGS_nonotldflag2 = 23;
+ int32 FLAGS_tldflag2 = FLAGS_nonotldflag2;
+ int32 FLAGS_notldflag2 = FLAGS_nonotldflag2;
+ static FlagRegisterer o_tldflag2(
+ "tldflag2", "int32",
+ "should show up in --helpshort", "gflags_unittest.",
+ &FLAGS_tldflag2, &FLAGS_notldflag2);
+}
+using fLI::FLAGS_tldflag2;
+
+namespace GFLAGS_NAMESPACE {
+
+namespace {
+
+
+static string TmpFile(const string& basename) {
+#ifdef _MSC_VER
+ return FLAGS_test_tmpdir + "\\" + basename;
+#else
+ return FLAGS_test_tmpdir + "/" + basename;
+#endif
+}
+
+// Returns the definition of the --flagfile flag to be used in the tests.
+// Must be called after ParseCommandLineFlags().
+static const char* GetFlagFileFlag() {
+#ifdef _MSC_VER
+ static const string flagfile = FLAGS_srcdir + "\\gflags_unittest_flagfile";
+#else
+ static const string flagfile = FLAGS_srcdir + "/gflags_unittest_flagfile";
+#endif
+ static const string flagfile_flag = string("--flagfile=") + flagfile;
+ return flagfile_flag.c_str();
+}
+
+
+// Defining a variable of type CompileAssertTypesEqual<T1, T2> will cause a
+// compiler error iff T1 and T2 are different types.
+template <typename T1, typename T2>
+struct CompileAssertTypesEqual;
+
+template <typename T>
+struct CompileAssertTypesEqual<T, T> {
+};
+
+
+template <typename Expected, typename Actual>
+void AssertIsType(Actual& x) {
+ CompileAssertTypesEqual<Expected, Actual>();
+}
+
+// Verify all the flags are the right type.
+TEST(FlagTypes, FlagTypes) {
+ AssertIsType<bool>(FLAGS_test_bool);
+ AssertIsType<int32>(FLAGS_test_int32);
+ AssertIsType<int64>(FLAGS_test_int64);
+ AssertIsType<uint64>(FLAGS_test_uint64);
+ AssertIsType<double>(FLAGS_test_double);
+ AssertIsType<string>(FLAGS_test_string);
+}
+
+#ifdef GTEST_HAS_DEATH_TEST
+// Death tests for "help" options.
+//
+// The help system automatically calls gflags_exitfunc(1) when you specify any of
+// the help-related flags ("-helpmatch", "-helpxml") so we can't test
+// those mainline.
+
+// Tests that "-helpmatch" causes the process to die.
+TEST(ReadFlagsFromStringDeathTest, HelpMatch) {
+ EXPECT_DEATH(ReadFlagsFromString("-helpmatch=base", GetArgv0(), true),
+ "");
+}
+
+
+// Tests that "-helpxml" causes the process to die.
+TEST(ReadFlagsFromStringDeathTest, HelpXml) {
+ EXPECT_DEATH(ReadFlagsFromString("-helpxml", GetArgv0(), true),
+ "");
+}
+#endif
+
+
+// A subroutine needed for testing reading flags from a string.
+void TestFlagString(const string& flags,
+ const string& expected_string,
+ bool expected_bool,
+ int32 expected_int32,
+ double expected_double) {
+ EXPECT_TRUE(ReadFlagsFromString(flags,
+ GetArgv0(),
+ // errors are fatal
+ true));
+
+ EXPECT_EQ(expected_string, FLAGS_test_string);
+ EXPECT_EQ(expected_bool, FLAGS_test_bool);
+ EXPECT_EQ(expected_int32, FLAGS_test_int32);
+ EXPECT_DOUBLE_EQ(expected_double, FLAGS_test_double);
+}
+
+
+// Tests reading flags from a string.
+TEST(FlagFileTest, ReadFlagsFromString) {
+ TestFlagString(
+ // Flag string
+ "-test_string=continued\n"
+ "# some comments are in order\n"
+ "# some\n"
+ " # comments\n"
+ "#are\n"
+ " #trickier\n"
+ "# than others\n"
+ "-test_bool=true\n"
+ " -test_int32=1\n"
+ "-test_double=0.0\n",
+ // Expected values
+ "continued",
+ true,
+ 1,
+ 0.0);
+
+ TestFlagString(
+ // Flag string
+ "# let's make sure it can update values\n"
+ "-test_string=initial\n"
+ "-test_bool=false\n"
+ "-test_int32=123\n"
+ "-test_double=123.0\n",
+ // Expected values
+ "initial",
+ false,
+ 123,
+ 123.0);
+}
+
+// Tests the filename part of the flagfile
+TEST(FlagFileTest, FilenamesOurfileLast) {
+ FLAGS_test_string = "initial";
+ FLAGS_test_bool = false;
+ FLAGS_test_int32 = -1;
+ FLAGS_test_double = -1.0;
+ TestFlagString(
+ // Flag string
+ "-test_string=continued\n"
+ "# some comments are in order\n"
+ "# some\n"
+ " # comments\n"
+ "#are\n"
+ " #trickier\n"
+ "# than others\n"
+ "not_our_filename\n"
+ "-test_bool=true\n"
+ " -test_int32=1\n"
+ "gflags_unittest\n"
+ "-test_double=1000.0\n",
+ // Expected values
+ "continued",
+ false,
+ -1,
+ 1000.0);
+}
+
+TEST(FlagFileTest, FilenamesOurfileFirst) {
+ FLAGS_test_string = "initial";
+ FLAGS_test_bool = false;
+ FLAGS_test_int32 = -1;
+ FLAGS_test_double = -1.0;
+ TestFlagString(
+ // Flag string
+ "-test_string=continued\n"
+ "# some comments are in order\n"
+ "# some\n"
+ " # comments\n"
+ "#are\n"
+ " #trickier\n"
+ "# than others\n"
+ "gflags_unittest\n"
+ "-test_bool=true\n"
+ " -test_int32=1\n"
+ "not_our_filename\n"
+ "-test_double=1000.0\n",
+ // Expected values
+ "continued",
+ true,
+ 1,
+ -1.0);
+}
+
+#if defined(HAVE_FNMATCH_H) || defined(HAVE_SHLWAPI_H) // otherwise glob isn't supported
+TEST(FlagFileTest, FilenamesOurfileGlob) {
+ FLAGS_test_string = "initial";
+ FLAGS_test_bool = false;
+ FLAGS_test_int32 = -1;
+ FLAGS_test_double = -1.0;
+ TestFlagString(
+ // Flag string
+ "-test_string=continued\n"
+ "# some comments are in order\n"
+ "# some\n"
+ " # comments\n"
+ "#are\n"
+ " #trickier\n"
+ "# than others\n"
+ "*flags*\n"
+ "-test_bool=true\n"
+ " -test_int32=1\n"
+ "flags\n"
+ "-test_double=1000.0\n",
+ // Expected values
+ "continued",
+ true,
+ 1,
+ -1.0);
+}
+
+TEST(FlagFileTest, FilenamesOurfileInBigList) {
+ FLAGS_test_string = "initial";
+ FLAGS_test_bool = false;
+ FLAGS_test_int32 = -1;
+ FLAGS_test_double = -1.0;
+ TestFlagString(
+ // Flag string
+ "-test_string=continued\n"
+ "# some comments are in order\n"
+ "# some\n"
+ " # comments\n"
+ "#are\n"
+ " #trickier\n"
+ "# than others\n"
+ "*first* *flags* *third*\n"
+ "-test_bool=true\n"
+ " -test_int32=1\n"
+ "flags\n"
+ "-test_double=1000.0\n",
+ // Expected values
+ "continued",
+ true,
+ 1,
+ -1.0);
+}
+#endif // defined(HAVE_FNMATCH_H) || defined(HAVE_SHLWAPI_H)
+
+// Tests that a failed flag-from-string read keeps flags at default values
+TEST(FlagFileTest, FailReadFlagsFromString) {
+ FLAGS_test_int32 = 119;
+ string flags("# let's make sure it can update values\n"
+ "-test_string=non_initial\n"
+ "-test_bool=false\n"
+ "-test_int32=123\n"
+ "-test_double=illegal\n");
+
+ EXPECT_FALSE(ReadFlagsFromString(flags,
+ GetArgv0(),
+ // errors are fatal
+ false));
+
+ EXPECT_EQ(119, FLAGS_test_int32);
+ EXPECT_EQ("initial", FLAGS_test_string);
+}
+
+// Tests that flags can be set to ordinary values.
+TEST(SetFlagValueTest, OrdinaryValues) {
+ EXPECT_EQ("initial", FLAGS_test_str1);
+
+ SetCommandLineOptionWithMode("test_str1", "second", SET_FLAG_IF_DEFAULT);
+ EXPECT_EQ("second", FLAGS_test_str1); // set; was default
+
+ SetCommandLineOptionWithMode("test_str1", "third", SET_FLAG_IF_DEFAULT);
+ EXPECT_EQ("second", FLAGS_test_str1); // already set once
+
+ FLAGS_test_str1 = "initial";
+ SetCommandLineOptionWithMode("test_str1", "third", SET_FLAG_IF_DEFAULT);
+ EXPECT_EQ("initial", FLAGS_test_str1); // still already set before
+
+ SetCommandLineOptionWithMode("test_str1", "third", SET_FLAGS_VALUE);
+ EXPECT_EQ("third", FLAGS_test_str1); // changed value
+
+ SetCommandLineOptionWithMode("test_str1", "fourth", SET_FLAGS_DEFAULT);
+ EXPECT_EQ("third", FLAGS_test_str1);
+ // value not changed (already set before)
+
+ EXPECT_EQ("initial", FLAGS_test_str2);
+
+ SetCommandLineOptionWithMode("test_str2", "second", SET_FLAGS_DEFAULT);
+ EXPECT_EQ("second", FLAGS_test_str2); // changed (was default)
+
+ FLAGS_test_str2 = "extra";
+ EXPECT_EQ("extra", FLAGS_test_str2);
+
+ FLAGS_test_str2 = "second";
+ SetCommandLineOptionWithMode("test_str2", "third", SET_FLAGS_DEFAULT);
+ EXPECT_EQ("third", FLAGS_test_str2); // still changed (was equal to default)
+
+ SetCommandLineOptionWithMode("test_str2", "fourth", SET_FLAG_IF_DEFAULT);
+ EXPECT_EQ("fourth", FLAGS_test_str2); // changed (was default)
+
+ EXPECT_EQ("initial", FLAGS_test_str3);
+
+ SetCommandLineOptionWithMode("test_str3", "second", SET_FLAGS_DEFAULT);
+ EXPECT_EQ("second", FLAGS_test_str3); // changed
+
+ FLAGS_test_str3 = "third";
+ SetCommandLineOptionWithMode("test_str3", "fourth", SET_FLAGS_DEFAULT);
+ EXPECT_EQ("third", FLAGS_test_str3); // not changed (was set)
+
+ SetCommandLineOptionWithMode("test_str3", "fourth", SET_FLAG_IF_DEFAULT);
+ EXPECT_EQ("third", FLAGS_test_str3); // not changed (was set)
+
+ SetCommandLineOptionWithMode("test_str3", "fourth", SET_FLAGS_VALUE);
+ EXPECT_EQ("fourth", FLAGS_test_str3); // changed value
+}
+
+
+// Tests that flags can be set to exceptional values.
+// Note: apparently MINGW doesn't parse inf and nan correctly:
+// http://www.mail-archive.com/bug-gnulib@gnu.org/msg09573.html
+// This url says FreeBSD also has a problem, but I didn't see that.
+TEST(SetFlagValueTest, ExceptionalValues) {
+#if defined(isinf) && !defined(__MINGW32__)
+ EXPECT_EQ("test_double set to inf\n",
+ SetCommandLineOption("test_double", "inf"));
+ EXPECT_INF(FLAGS_test_double);
+
+ EXPECT_EQ("test_double set to inf\n",
+ SetCommandLineOption("test_double", "INF"));
+ EXPECT_INF(FLAGS_test_double);
+#endif
+
+ // set some bad values
+ EXPECT_EQ("",
+ SetCommandLineOption("test_double", "0.1xxx"));
+ EXPECT_EQ("",
+ SetCommandLineOption("test_double", " "));
+ EXPECT_EQ("",
+ SetCommandLineOption("test_double", ""));
+#if defined(isinf) && !defined(__MINGW32__)
+ EXPECT_EQ("test_double set to -inf\n",
+ SetCommandLineOption("test_double", "-inf"));
+ EXPECT_INF(FLAGS_test_double);
+ EXPECT_GT(0, FLAGS_test_double);
+#endif
+
+#if defined(isnan) && !defined(__MINGW32__)
+ EXPECT_EQ("test_double set to nan\n",
+ SetCommandLineOption("test_double", "NaN"));
+ EXPECT_NAN(FLAGS_test_double);
+#endif
+}
+
+// Tests that integer flags can be specified in many ways
+TEST(SetFlagValueTest, DifferentRadices) {
+ EXPECT_EQ("test_int32 set to 12\n",
+ SetCommandLineOption("test_int32", "12"));
+
+ EXPECT_EQ("test_int32 set to 16\n",
+ SetCommandLineOption("test_int32", "0x10"));
+
+ EXPECT_EQ("test_int32 set to 34\n",
+ SetCommandLineOption("test_int32", "0X22"));
+
+ // Leading 0 is *not* octal; it's still decimal
+ EXPECT_EQ("test_int32 set to 10\n",
+ SetCommandLineOption("test_int32", "010"));
+}
+
+// Tests what happens when you try to set a flag to an illegal value
+TEST(SetFlagValueTest, IllegalValues) {
+ FLAGS_test_bool = true;
+ FLAGS_test_int32 = 119;
+ FLAGS_test_int64 = 1191;
+ FLAGS_test_uint64 = 11911;
+
+ EXPECT_EQ("",
+ SetCommandLineOption("test_bool", "12"));
+
+ EXPECT_EQ("",
+ SetCommandLineOption("test_int32", "7000000000000"));
+
+ EXPECT_EQ("",
+ SetCommandLineOption("test_uint64", "-1"));
+
+ EXPECT_EQ("",
+ SetCommandLineOption("test_int64", "not a number!"));
+
+ // Test the empty string with each type of input
+ EXPECT_EQ("", SetCommandLineOption("test_bool", ""));
+ EXPECT_EQ("", SetCommandLineOption("test_int32", ""));
+ EXPECT_EQ("", SetCommandLineOption("test_int64", ""));
+ EXPECT_EQ("", SetCommandLineOption("test_uint64", ""));
+ EXPECT_EQ("", SetCommandLineOption("test_double", ""));
+ EXPECT_EQ("test_string set to \n", SetCommandLineOption("test_string", ""));
+
+ EXPECT_TRUE(FLAGS_test_bool);
+ EXPECT_EQ(119, FLAGS_test_int32);
+ EXPECT_EQ(1191, FLAGS_test_int64);
+ EXPECT_EQ(11911, FLAGS_test_uint64);
+}
+
+
+// Tests that we only evaluate macro args once
+TEST(MacroArgs, EvaluateOnce) {
+ EXPECT_EQ(13, FLAGS_changeable_var);
+ // Make sure we don't ++ the value somehow, when evaluating the flag.
+ EXPECT_EQ(13, FLAGS_changeable_var);
+ // Make sure the macro only evaluated this var once.
+ EXPECT_EQ(13, changeable_var);
+ // Make sure the actual value and default value are the same
+ SetCommandLineOptionWithMode("changeable_var", "21", SET_FLAG_IF_DEFAULT);
+ EXPECT_EQ(21, FLAGS_changeable_var);
+}
+
+TEST(MacroArgs, EvaluateOnceBool) {
+ EXPECT_TRUE(FLAGS_changeable_bool_var);
+ EXPECT_TRUE(FLAGS_changeable_bool_var);
+ EXPECT_EQ(8009, changeable_bool_var);
+ SetCommandLineOptionWithMode("changeable_bool_var", "false",
+ SET_FLAG_IF_DEFAULT);
+ EXPECT_FALSE(FLAGS_changeable_bool_var);
+}
+
+TEST(MacroArgs, EvaluateOnceStrings) {
+ EXPECT_EQ("1", FLAGS_changeable_string_var);
+ EXPECT_EQ("1", FLAGS_changeable_string_var);
+ EXPECT_EQ(1, changeable_string_var);
+ SetCommandLineOptionWithMode("changeable_string_var", "different",
+ SET_FLAG_IF_DEFAULT);
+ EXPECT_EQ("different", FLAGS_changeable_string_var);
+}
+
+// Tests that the FooFromEnv does the right thing
+TEST(FromEnvTest, LegalValues) {
+ setenv("BOOL_VAL1", "true", 1);
+ setenv("BOOL_VAL2", "false", 1);
+ setenv("BOOL_VAL3", "1", 1);
+ setenv("BOOL_VAL4", "F", 1);
+ EXPECT_TRUE(BoolFromEnv("BOOL_VAL1", false));
+ EXPECT_FALSE(BoolFromEnv("BOOL_VAL2", true));
+ EXPECT_TRUE(BoolFromEnv("BOOL_VAL3", false));
+ EXPECT_FALSE(BoolFromEnv("BOOL_VAL4", true));
+ EXPECT_TRUE(BoolFromEnv("BOOL_VAL_UNKNOWN", true));
+ EXPECT_FALSE(BoolFromEnv("BOOL_VAL_UNKNOWN", false));
+
+ setenv("INT_VAL1", "1", 1);
+ setenv("INT_VAL2", "-1", 1);
+ EXPECT_EQ(1, Int32FromEnv("INT_VAL1", 10));
+ EXPECT_EQ(-1, Int32FromEnv("INT_VAL2", 10));
+ EXPECT_EQ(10, Int32FromEnv("INT_VAL_UNKNOWN", 10));
+
+ setenv("INT_VAL3", "1099511627776", 1);
+ EXPECT_EQ(1, Int64FromEnv("INT_VAL1", 20));
+ EXPECT_EQ(-1, Int64FromEnv("INT_VAL2", 20));
+ EXPECT_EQ(1099511627776LL, Int64FromEnv("INT_VAL3", 20));
+ EXPECT_EQ(20, Int64FromEnv("INT_VAL_UNKNOWN", 20));
+
+ EXPECT_EQ(1, Uint64FromEnv("INT_VAL1", 30));
+ EXPECT_EQ(1099511627776ULL, Uint64FromEnv("INT_VAL3", 30));
+ EXPECT_EQ(30, Uint64FromEnv("INT_VAL_UNKNOWN", 30));
+
+ // I pick values here that can be easily represented exactly in floating-point
+ setenv("DOUBLE_VAL1", "0.0", 1);
+ setenv("DOUBLE_VAL2", "1.0", 1);
+ setenv("DOUBLE_VAL3", "-1.0", 1);
+ EXPECT_EQ(0.0, DoubleFromEnv("DOUBLE_VAL1", 40.0));
+ EXPECT_EQ(1.0, DoubleFromEnv("DOUBLE_VAL2", 40.0));
+ EXPECT_EQ(-1.0, DoubleFromEnv("DOUBLE_VAL3", 40.0));
+ EXPECT_EQ(40.0, DoubleFromEnv("DOUBLE_VAL_UNKNOWN", 40.0));
+
+ setenv("STRING_VAL1", "", 1);
+ setenv("STRING_VAL2", "my happy string!", 1);
+ EXPECT_STREQ("", StringFromEnv("STRING_VAL1", "unknown"));
+ EXPECT_STREQ("my happy string!", StringFromEnv("STRING_VAL2", "unknown"));
+ EXPECT_STREQ("unknown", StringFromEnv("STRING_VAL_UNKNOWN", "unknown"));
+}
+
+#ifdef GTEST_HAS_DEATH_TEST
+// Tests that the FooFromEnv dies on parse-error
+TEST(FromEnvDeathTest, IllegalValues) {
+ setenv("BOOL_BAD1", "so true!", 1);
+ setenv("BOOL_BAD2", "", 1);
+ EXPECT_DEATH(BoolFromEnv("BOOL_BAD1", false), "error parsing env variable");
+ EXPECT_DEATH(BoolFromEnv("BOOL_BAD2", true), "error parsing env variable");
+
+ setenv("INT_BAD1", "one", 1);
+ setenv("INT_BAD2", "100000000000000000", 1);
+ setenv("INT_BAD3", "0xx10", 1);
+ setenv("INT_BAD4", "", 1);
+ EXPECT_DEATH(Int32FromEnv("INT_BAD1", 10), "error parsing env variable");
+ EXPECT_DEATH(Int32FromEnv("INT_BAD2", 10), "error parsing env variable");
+ EXPECT_DEATH(Int32FromEnv("INT_BAD3", 10), "error parsing env variable");
+ EXPECT_DEATH(Int32FromEnv("INT_BAD4", 10), "error parsing env variable");
+
+ setenv("BIGINT_BAD1", "18446744073709551616000", 1);
+ EXPECT_DEATH(Int64FromEnv("INT_BAD1", 20), "error parsing env variable");
+ EXPECT_DEATH(Int64FromEnv("INT_BAD3", 20), "error parsing env variable");
+ EXPECT_DEATH(Int64FromEnv("INT_BAD4", 20), "error parsing env variable");
+ EXPECT_DEATH(Int64FromEnv("BIGINT_BAD1", 200), "error parsing env variable");
+
+ setenv("BIGINT_BAD2", "-1", 1);
+ EXPECT_DEATH(Uint64FromEnv("INT_BAD1", 30), "error parsing env variable");
+ EXPECT_DEATH(Uint64FromEnv("INT_BAD3", 30), "error parsing env variable");
+ EXPECT_DEATH(Uint64FromEnv("INT_BAD4", 30), "error parsing env variable");
+ EXPECT_DEATH(Uint64FromEnv("BIGINT_BAD1", 30), "error parsing env variable");
+ // TODO(csilvers): uncomment this when we disallow negative numbers for uint64
+#if 0
+ EXPECT_DEATH(Uint64FromEnv("BIGINT_BAD2", 30), "error parsing env variable");
+#endif
+
+ setenv("DOUBLE_BAD1", "0.0.0", 1);
+ setenv("DOUBLE_BAD2", "", 1);
+ EXPECT_DEATH(DoubleFromEnv("DOUBLE_BAD1", 40.0), "error parsing env variable");
+ EXPECT_DEATH(DoubleFromEnv("DOUBLE_BAD2", 40.0), "error parsing env variable");
+}
+#endif
+
+
+// Tests that FlagSaver can save the states of string flags.
+TEST(FlagSaverTest, CanSaveStringFlagStates) {
+ // 1. Initializes the flags.
+
+ // State of flag test_str1:
+ // default value - "initial"
+ // current value - "initial"
+ // not set - true
+
+ SetCommandLineOptionWithMode("test_str2", "second", SET_FLAGS_VALUE);
+ // State of flag test_str2:
+ // default value - "initial"
+ // current value - "second"
+ // not set - false
+
+ SetCommandLineOptionWithMode("test_str3", "second", SET_FLAGS_DEFAULT);
+ // State of flag test_str3:
+ // default value - "second"
+ // current value - "second"
+ // not set - true
+
+ // 2. Saves the flag states.
+
+ {
+ FlagSaver fs;
+
+ // 3. Modifies the flag states.
+
+ SetCommandLineOptionWithMode("test_str1", "second", SET_FLAGS_VALUE);
+ EXPECT_EQ("second", FLAGS_test_str1);
+ // State of flag test_str1:
+ // default value - "second"
+ // current value - "second"
+ // not set - true
+
+ SetCommandLineOptionWithMode("test_str2", "third", SET_FLAGS_DEFAULT);
+ EXPECT_EQ("second", FLAGS_test_str2);
+ // State of flag test_str2:
+ // default value - "third"
+ // current value - "second"
+ // not set - false
+
+ SetCommandLineOptionWithMode("test_str3", "third", SET_FLAGS_VALUE);
+ EXPECT_EQ("third", FLAGS_test_str3);
+ // State of flag test_str1:
+ // default value - "second"
+ // current value - "third"
+ // not set - false
+
+ // 4. Restores the flag states.
+ }
+
+ // 5. Verifies that the states were restored.
+
+ // Verifies that the value of test_str1 was restored.
+ EXPECT_EQ("initial", FLAGS_test_str1);
+ // Verifies that the "not set" attribute of test_str1 was restored to true.
+ SetCommandLineOptionWithMode("test_str1", "second", SET_FLAG_IF_DEFAULT);
+ EXPECT_EQ("second", FLAGS_test_str1);
+
+ // Verifies that the value of test_str2 was restored.
+ EXPECT_EQ("second", FLAGS_test_str2);
+ // Verifies that the "not set" attribute of test_str2 was restored to false.
+ SetCommandLineOptionWithMode("test_str2", "fourth", SET_FLAG_IF_DEFAULT);
+ EXPECT_EQ("second", FLAGS_test_str2);
+
+ // Verifies that the value of test_str3 was restored.
+ EXPECT_EQ("second", FLAGS_test_str3);
+ // Verifies that the "not set" attribute of test_str3 was restored to true.
+ SetCommandLineOptionWithMode("test_str3", "fourth", SET_FLAG_IF_DEFAULT);
+ EXPECT_EQ("fourth", FLAGS_test_str3);
+}
+
+
+// Tests that FlagSaver can save the values of various-typed flags.
+TEST(FlagSaverTest, CanSaveVariousTypedFlagValues) {
+ // Initializes the flags.
+ FLAGS_test_bool = false;
+ FLAGS_test_int32 = -1;
+ FLAGS_test_int64 = -2;
+ FLAGS_test_uint64 = 3;
+ FLAGS_test_double = 4.0;
+ FLAGS_test_string = "good";
+
+ // Saves the flag states.
+ {
+ FlagSaver fs;
+
+ // Modifies the flags.
+ FLAGS_test_bool = true;
+ FLAGS_test_int32 = -5;
+ FLAGS_test_int64 = -6;
+ FLAGS_test_uint64 = 7;
+ FLAGS_test_double = 8.0;
+ FLAGS_test_string = "bad";
+
+ // Restores the flag states.
+ }
+
+ // Verifies the flag values were restored.
+ EXPECT_FALSE(FLAGS_test_bool);
+ EXPECT_EQ(-1, FLAGS_test_int32);
+ EXPECT_EQ(-2, FLAGS_test_int64);
+ EXPECT_EQ(3, FLAGS_test_uint64);
+ EXPECT_DOUBLE_EQ(4.0, FLAGS_test_double);
+ EXPECT_EQ("good", FLAGS_test_string);
+}
+
+TEST(GetAllFlagsTest, BaseTest) {
+ vector<CommandLineFlagInfo> flags;
+ GetAllFlags(&flags);
+ bool found_test_bool = false;
+ vector<CommandLineFlagInfo>::const_iterator i;
+ for (i = flags.begin(); i != flags.end(); ++i) {
+ if (i->name == "test_bool") {
+ found_test_bool = true;
+ EXPECT_EQ(i->type, "bool");
+ EXPECT_EQ(i->default_value, "false");
+ EXPECT_EQ(i->flag_ptr, &FLAGS_test_bool);
+ break;
+ }
+ }
+ EXPECT_TRUE(found_test_bool);
+}
+
+TEST(ShowUsageWithFlagsTest, BaseTest) {
+ // TODO(csilvers): test this by allowing output other than to stdout.
+ // Not urgent since this functionality is tested via
+ // gflags_unittest.sh, though only through use of --help.
+}
+
+TEST(ShowUsageWithFlagsRestrictTest, BaseTest) {
+ // TODO(csilvers): test this by allowing output other than to stdout.
+ // Not urgent since this functionality is tested via
+ // gflags_unittest.sh, though only through use of --helpmatch.
+}
+
+// Note: all these argv-based tests depend on SetArgv being called
+// before ParseCommandLineFlags() in main(), below.
+TEST(GetArgvsTest, BaseTest) {
+ vector<string> argvs = GetArgvs();
+ EXPECT_EQ(4, argvs.size());
+ EXPECT_EQ("/test/argv/for/gflags_unittest", argvs[0]);
+ EXPECT_EQ("argv 2", argvs[1]);
+ EXPECT_EQ("3rd argv", argvs[2]);
+ EXPECT_EQ("argv #4", argvs[3]);
+}
+
+TEST(GetArgvTest, BaseTest) {
+ EXPECT_STREQ("/test/argv/for/gflags_unittest "
+ "argv 2 3rd argv argv #4", GetArgv());
+}
+
+TEST(GetArgv0Test, BaseTest) {
+ EXPECT_STREQ("/test/argv/for/gflags_unittest", GetArgv0());
+}
+
+TEST(GetArgvSumTest, BaseTest) {
+ // This number is just the sum of the ASCII values of all the chars
+ // in GetArgv().
+ EXPECT_EQ(4904, GetArgvSum());
+}
+
+TEST(ProgramInvocationNameTest, BaseTest) {
+ EXPECT_STREQ("/test/argv/for/gflags_unittest",
+ ProgramInvocationName());
+}
+
+TEST(ProgramInvocationShortNameTest, BaseTest) {
+ EXPECT_STREQ("gflags_unittest", ProgramInvocationShortName());
+}
+
+TEST(ProgramUsageTest, BaseTest) { // Depends on 1st arg to ParseCommandLineFlags()
+ EXPECT_STREQ("/test/argv/for/gflags_unittest: "
+ "<useless flag> [...]\nDoes something useless.\n",
+ ProgramUsage());
+}
+
+TEST(GetCommandLineOptionTest, NameExistsAndIsDefault) {
+ string value("will be changed");
+ bool r = GetCommandLineOption("test_bool", &value);
+ EXPECT_TRUE(r);
+ EXPECT_EQ("false", value);
+
+ r = GetCommandLineOption("test_int32", &value);
+ EXPECT_TRUE(r);
+ EXPECT_EQ("-1", value);
+}
+
+TEST(GetCommandLineOptionTest, NameExistsAndWasAssigned) {
+ FLAGS_test_int32 = 400;
+ string value("will be changed");
+ const bool r = GetCommandLineOption("test_int32", &value);
+ EXPECT_TRUE(r);
+ EXPECT_EQ("400", value);
+}
+
+TEST(GetCommandLineOptionTest, NameExistsAndWasSet) {
+ SetCommandLineOption("test_int32", "700");
+ string value("will be changed");
+ const bool r = GetCommandLineOption("test_int32", &value);
+ EXPECT_TRUE(r);
+ EXPECT_EQ("700", value);
+}
+
+TEST(GetCommandLineOptionTest, NameExistsAndWasNotSet) {
+ // This doesn't set the flag's value, but rather its default value.
+ // is_default is still true, but the 'default' value returned has changed!
+ SetCommandLineOptionWithMode("test_int32", "800", SET_FLAGS_DEFAULT);
+ string value("will be changed");
+ const bool r = GetCommandLineOption("test_int32", &value);
+ EXPECT_TRUE(r);
+ EXPECT_EQ("800", value);
+ EXPECT_TRUE(GetCommandLineFlagInfoOrDie("test_int32").is_default);
+}
+
+TEST(GetCommandLineOptionTest, NameExistsAndWasConditionallySet) {
+ SetCommandLineOptionWithMode("test_int32", "900", SET_FLAG_IF_DEFAULT);
+ string value("will be changed");
+ const bool r = GetCommandLineOption("test_int32", &value);
+ EXPECT_TRUE(r);
+ EXPECT_EQ("900", value);
+}
+
+TEST(GetCommandLineOptionTest, NameDoesNotExist) {
+ string value("will not be changed");
+ const bool r = GetCommandLineOption("test_int3210", &value);
+ EXPECT_FALSE(r);
+ EXPECT_EQ("will not be changed", value);
+}
+
+TEST(GetCommandLineFlagInfoTest, FlagExists) {
+ CommandLineFlagInfo info;
+ bool r = GetCommandLineFlagInfo("test_int32", &info);
+ EXPECT_TRUE(r);
+ EXPECT_EQ("test_int32", info.name);
+ EXPECT_EQ("int32", info.type);
+ EXPECT_EQ("", info.description);
+ EXPECT_EQ("-1", info.current_value);
+ EXPECT_EQ("-1", info.default_value);
+ EXPECT_TRUE(info.is_default);
+ EXPECT_FALSE(info.has_validator_fn);
+ EXPECT_EQ(&FLAGS_test_int32, info.flag_ptr);
+
+ FLAGS_test_bool = true;
+ r = GetCommandLineFlagInfo("test_bool", &info);
+ EXPECT_TRUE(r);
+ EXPECT_EQ("test_bool", info.name);
+ EXPECT_EQ("bool", info.type);
+ EXPECT_EQ("tests bool-ness", info.description);
+ EXPECT_EQ("true", info.current_value);
+ EXPECT_EQ("false", info.default_value);
+ EXPECT_FALSE(info.is_default);
+ EXPECT_FALSE(info.has_validator_fn);
+ EXPECT_EQ(&FLAGS_test_bool, info.flag_ptr);
+
+ FLAGS_test_bool = false;
+ r = GetCommandLineFlagInfo("test_bool", &info);
+ EXPECT_TRUE(r);
+ EXPECT_EQ("test_bool", info.name);
+ EXPECT_EQ("bool", info.type);
+ EXPECT_EQ("tests bool-ness", info.description);
+ EXPECT_EQ("false", info.current_value);
+ EXPECT_EQ("false", info.default_value);
+ EXPECT_FALSE(info.is_default); // value is same, but flag *was* modified
+ EXPECT_FALSE(info.has_validator_fn);
+ EXPECT_EQ(&FLAGS_test_bool, info.flag_ptr);
+}
+
+TEST(GetCommandLineFlagInfoTest, FlagDoesNotExist) {
+ CommandLineFlagInfo info;
+ // Set to some random values that GetCommandLineFlagInfo should not change
+ info.name = "name";
+ info.type = "type";
+ info.current_value = "curr";
+ info.default_value = "def";
+ info.filename = "/";
+ info.is_default = false;
+ info.has_validator_fn = true;
+ info.flag_ptr = NULL;
+ bool r = GetCommandLineFlagInfo("test_int3210", &info);
+ EXPECT_FALSE(r);
+ EXPECT_EQ("name", info.name);
+ EXPECT_EQ("type", info.type);
+ EXPECT_EQ("", info.description);
+ EXPECT_EQ("curr", info.current_value);
+ EXPECT_EQ("def", info.default_value);
+ EXPECT_EQ("/", info.filename);
+ EXPECT_FALSE(info.is_default);
+ EXPECT_TRUE(info.has_validator_fn);
+ EXPECT_EQ(NULL, info.flag_ptr);
+}
+
+TEST(GetCommandLineFlagInfoOrDieTest, FlagExistsAndIsDefault) {
+ CommandLineFlagInfo info;
+ info = GetCommandLineFlagInfoOrDie("test_int32");
+ EXPECT_EQ("test_int32", info.name);
+ EXPECT_EQ("int32", info.type);
+ EXPECT_EQ("", info.description);
+ EXPECT_EQ("-1", info.current_value);
+ EXPECT_EQ("-1", info.default_value);
+ EXPECT_TRUE(info.is_default);
+ EXPECT_EQ(&FLAGS_test_int32, info.flag_ptr);
+ info = GetCommandLineFlagInfoOrDie("test_bool");
+ EXPECT_EQ("test_bool", info.name);
+ EXPECT_EQ("bool", info.type);
+ EXPECT_EQ("tests bool-ness", info.description);
+ EXPECT_EQ("false", info.current_value);
+ EXPECT_EQ("false", info.default_value);
+ EXPECT_TRUE(info.is_default);
+ EXPECT_FALSE(info.has_validator_fn);
+ EXPECT_EQ(&FLAGS_test_bool, info.flag_ptr);
+}
+
+TEST(GetCommandLineFlagInfoOrDieTest, FlagExistsAndWasAssigned) {
+ FLAGS_test_int32 = 400;
+ CommandLineFlagInfo info;
+ info = GetCommandLineFlagInfoOrDie("test_int32");
+ EXPECT_EQ("test_int32", info.name);
+ EXPECT_EQ("int32", info.type);
+ EXPECT_EQ("", info.description);
+ EXPECT_EQ("400", info.current_value);
+ EXPECT_EQ("-1", info.default_value);
+ EXPECT_FALSE(info.is_default);
+ EXPECT_EQ(&FLAGS_test_int32, info.flag_ptr);
+ FLAGS_test_bool = true;
+ info = GetCommandLineFlagInfoOrDie("test_bool");
+ EXPECT_EQ("test_bool", info.name);
+ EXPECT_EQ("bool", info.type);
+ EXPECT_EQ("tests bool-ness", info.description);
+ EXPECT_EQ("true", info.current_value);
+ EXPECT_EQ("false", info.default_value);
+ EXPECT_FALSE(info.is_default);
+ EXPECT_FALSE(info.has_validator_fn);
+ EXPECT_EQ(&FLAGS_test_bool, info.flag_ptr);
+}
+
+#ifdef GTEST_HAS_DEATH_TEST
+TEST(GetCommandLineFlagInfoOrDieDeathTest, FlagDoesNotExist) {
+ EXPECT_DEATH(GetCommandLineFlagInfoOrDie("test_int3210"),
+ ".*: flag test_int3210 does not exist");
+}
+#endif
+
+
+// These are lightly tested because they're deprecated. Basically,
+// the tests are meant to cover how existing users use these functions,
+// but not necessarily how new users could use them.
+TEST(DeprecatedFunctionsTest, CommandlineFlagsIntoString) {
+ string s = CommandlineFlagsIntoString();
+ EXPECT_NE(string::npos, s.find("--test_bool="));
+}
+
+TEST(DeprecatedFunctionsTest, AppendFlagsIntoFile) {
+ FLAGS_test_int32 = 10; // just to make the test more interesting
+ string filename(TmpFile("flagfile"));
+ unlink(filename.c_str()); // just to be safe
+ const bool r = AppendFlagsIntoFile(filename, "not the real argv0");
+ EXPECT_TRUE(r);
+
+ FILE* fp;
+ EXPECT_EQ(0, SafeFOpen(&fp, filename.c_str(), "r"));
+ EXPECT_TRUE(fp != NULL);
+ char line[8192];
+ EXPECT_TRUE(fgets(line, sizeof(line)-1, fp) != NULL); // get the first line
+ // First line should be progname.
+ EXPECT_STREQ("not the real argv0\n", line);
+
+ bool found_bool = false, found_int32 = false;
+ while (fgets(line, sizeof(line)-1, fp)) {
+ line[sizeof(line)-1] = '\0'; // just to be safe
+ if (strcmp(line, "--test_bool=false\n") == 0)
+ found_bool = true;
+ if (strcmp(line, "--test_int32=10\n") == 0)
+ found_int32 = true;
+ }
+ EXPECT_TRUE(found_int32);
+ EXPECT_TRUE(found_bool);
+ fclose(fp);
+}
+
+TEST(DeprecatedFunctionsTest, ReadFromFlagsFile) {
+ FLAGS_test_int32 = -10; // just to make the test more interesting
+ string filename(TmpFile("flagfile2"));
+ unlink(filename.c_str()); // just to be safe
+ bool r = AppendFlagsIntoFile(filename, GetArgv0());
+ EXPECT_TRUE(r);
+
+ FLAGS_test_int32 = -11;
+ r = ReadFromFlagsFile(filename, GetArgv0(), true);
+ EXPECT_TRUE(r);
+ EXPECT_EQ(-10, FLAGS_test_int32);
+} // unnamed namespace
+
+TEST(DeprecatedFunctionsTest, ReadFromFlagsFileFailure) {
+ FLAGS_test_int32 = -20;
+ string filename(TmpFile("flagfile3"));
+ FILE* fp;
+ EXPECT_EQ(0, SafeFOpen(&fp, filename.c_str(), "w"));
+ EXPECT_TRUE(fp != NULL);
+ // Note the error in the bool assignment below...
+ fprintf(fp, "%s\n--test_int32=-21\n--test_bool=not_a_bool!\n", GetArgv0());
+ fclose(fp);
+
+ FLAGS_test_int32 = -22;
+ const bool r = ReadFromFlagsFile(filename, GetArgv0(), false);
+ EXPECT_FALSE(r);
+ EXPECT_EQ(-22, FLAGS_test_int32); // the -21 from the flagsfile didn't take
+}
+
+TEST(FlagsSetBeforeInitTest, TryFromEnv) {
+ EXPECT_EQ("pre-set", FLAGS_test_tryfromenv);
+}
+
+// The following test case verifies that ParseCommandLineFlags() and
+// ParseCommandLineNonHelpFlags() uses the last definition of a flag
+// in case it's defined more than once.
+
+DEFINE_int32(test_flag, -1, "used for testing gflags.cc");
+
+// Parses and returns the --test_flag flag.
+// If with_help is true, calls ParseCommandLineFlags; otherwise calls
+// ParseCommandLineNonHelpFlags.
+int32 ParseTestFlag(bool with_help, int argc, const char** const_argv) {
+ FlagSaver fs; // Restores the flags before returning.
+
+ // Makes a copy of the input array s.t. it can be reused
+ // (ParseCommandLineFlags() will alter the array).
+ char** const argv_save = new char*[argc + 1];
+ char** argv = argv_save;
+ memcpy(argv, const_argv, sizeof(*argv)*(argc + 1));
+
+ if (with_help) {
+ ParseCommandLineFlags(&argc, &argv, true);
+ } else {
+ ParseCommandLineNonHelpFlags(&argc, &argv, true);
+ }
+
+ delete[] argv_save;
+ return FLAGS_test_flag;
+}
+
+TEST(ParseCommandLineFlagsUsesLastDefinitionTest,
+ WhenFlagIsDefinedTwiceOnCommandLine) {
+ const char* argv[] = {
+ "my_test",
+ "--test_flag=1",
+ "--test_flag=2",
+ NULL,
+ };
+
+ EXPECT_EQ(2, ParseTestFlag(true, arraysize(argv) - 1, argv));
+ EXPECT_EQ(2, ParseTestFlag(false, arraysize(argv) - 1, argv));
+}
+
+TEST(ParseCommandLineFlagsUsesLastDefinitionTest,
+ WhenFlagIsDefinedTwiceInFlagFile) {
+ const char* argv[] = {
+ "my_test",
+ GetFlagFileFlag(),
+ NULL,
+ };
+
+ EXPECT_EQ(2, ParseTestFlag(true, arraysize(argv) - 1, argv));
+ EXPECT_EQ(2, ParseTestFlag(false, arraysize(argv) - 1, argv));
+}
+
+TEST(ParseCommandLineFlagsUsesLastDefinitionTest,
+ WhenFlagIsDefinedInCommandLineAndThenFlagFile) {
+ const char* argv[] = {
+ "my_test",
+ "--test_flag=0",
+ GetFlagFileFlag(),
+ NULL,
+ };
+
+ EXPECT_EQ(2, ParseTestFlag(true, arraysize(argv) - 1, argv));
+ EXPECT_EQ(2, ParseTestFlag(false, arraysize(argv) - 1, argv));
+}
+
+TEST(ParseCommandLineFlagsUsesLastDefinitionTest,
+ WhenFlagIsDefinedInFlagFileAndThenCommandLine) {
+ const char* argv[] = {
+ "my_test",
+ GetFlagFileFlag(),
+ "--test_flag=3",
+ NULL,
+ };
+
+ EXPECT_EQ(3, ParseTestFlag(true, arraysize(argv) - 1, argv));
+ EXPECT_EQ(3, ParseTestFlag(false, arraysize(argv) - 1, argv));
+}
+
+TEST(ParseCommandLineFlagsUsesLastDefinitionTest,
+ WhenFlagIsDefinedInCommandLineAndFlagFileAndThenCommandLine) {
+ const char* argv[] = {
+ "my_test",
+ "--test_flag=0",
+ GetFlagFileFlag(),
+ "--test_flag=3",
+ NULL,
+ };
+
+ EXPECT_EQ(3, ParseTestFlag(true, arraysize(argv) - 1, argv));
+ EXPECT_EQ(3, ParseTestFlag(false, arraysize(argv) - 1, argv));
+}
+
+TEST(ParseCommandLineFlagsAndDashArgs, TwoDashArgFirst) {
+ const char* argv[] = {
+ "my_test",
+ "--",
+ "--test_flag=0",
+ NULL,
+ };
+
+ EXPECT_EQ(-1, ParseTestFlag(true, arraysize(argv) - 1, argv));
+ EXPECT_EQ(-1, ParseTestFlag(false, arraysize(argv) - 1, argv));
+}
+
+TEST(ParseCommandLineFlagsAndDashArgs, TwoDashArgMiddle) {
+ const char* argv[] = {
+ "my_test",
+ "--test_flag=7",
+ "--",
+ "--test_flag=0",
+ NULL,
+ };
+
+ EXPECT_EQ(7, ParseTestFlag(true, arraysize(argv) - 1, argv));
+ EXPECT_EQ(7, ParseTestFlag(false, arraysize(argv) - 1, argv));
+}
+
+TEST(ParseCommandLineFlagsAndDashArgs, OneDashArg) {
+ const char* argv[] = {
+ "my_test",
+ "-",
+ "--test_flag=0",
+ NULL,
+ };
+
+ EXPECT_EQ(0, ParseTestFlag(true, arraysize(argv) - 1, argv));
+ EXPECT_EQ(0, ParseTestFlag(false, arraysize(argv) - 1, argv));
+}
+
+#ifdef GTEST_HAS_DEATH_TEST
+TEST(ParseCommandLineFlagsUnknownFlagDeathTest,
+ FlagIsCompletelyUnknown) {
+ const char* argv[] = {
+ "my_test",
+ "--this_flag_does_not_exist",
+ NULL,
+ };
+
+ EXPECT_DEATH(ParseTestFlag(true, arraysize(argv) - 1, argv),
+ "unknown command line flag.*");
+ EXPECT_DEATH(ParseTestFlag(false, arraysize(argv) - 1, argv),
+ "unknown command line flag.*");
+}
+
+TEST(ParseCommandLineFlagsUnknownFlagDeathTest,
+ BoolFlagIsCompletelyUnknown) {
+ const char* argv[] = {
+ "my_test",
+ "--nothis_flag_does_not_exist",
+ NULL,
+ };
+
+ EXPECT_DEATH(ParseTestFlag(true, arraysize(argv) - 1, argv),
+ "unknown command line flag.*");
+ EXPECT_DEATH(ParseTestFlag(false, arraysize(argv) - 1, argv),
+ "unknown command line flag.*");
+}
+
+TEST(ParseCommandLineFlagsUnknownFlagDeathTest,
+ FlagIsNotABool) {
+ const char* argv[] = {
+ "my_test",
+ "--notest_string",
+ NULL,
+ };
+
+ EXPECT_DEATH(ParseTestFlag(true, arraysize(argv) - 1, argv),
+ "boolean value .* specified for .* command line flag");
+ EXPECT_DEATH(ParseTestFlag(false, arraysize(argv) - 1, argv),
+ "boolean value .* specified for .* command line flag");
+}
+#endif
+
+TEST(ParseCommandLineFlagsWrongFields,
+ DescriptionIsInvalid) {
+ // These must not be automatic variables, since command line flags
+ // aren't unregistered and gUnit uses FlagSaver to save and restore
+ // command line flags' values. If these are on the stack, then when
+ // later tests attempt to save and restore their values, the stack
+ // addresses of these variables will be overwritten... Stack smash!
+ static bool current_storage;
+ static bool defvalue_storage;
+ FlagRegisterer fr("flag_name", "bool", 0, "filename",
+ ¤t_storage, &defvalue_storage);
+ CommandLineFlagInfo fi;
+ EXPECT_TRUE(GetCommandLineFlagInfo("flag_name", &fi));
+ EXPECT_EQ("", fi.description);
+ EXPECT_EQ(¤t_storage, fi.flag_ptr);
+}
+
+static bool ValidateTestFlagIs5(const char* flagname, int32 flagval) {
+ if (flagval == 5)
+ return true;
+ printf("%s isn't 5!\n", flagname);
+ return false;
+}
+
+static bool ValidateTestFlagIs10(const char* flagname, int32 flagval) {
+ return flagval == 10;
+}
+
+
+TEST(FlagsValidator, ValidFlagViaArgv) {
+ const char* argv[] = {
+ "my_test",
+ "--test_flag=5",
+ NULL,
+ };
+ EXPECT_TRUE(RegisterFlagValidator(&FLAGS_test_flag, &ValidateTestFlagIs5));
+ EXPECT_EQ(5, ParseTestFlag(true, arraysize(argv) - 1, argv));
+ // Undo the flag validator setting
+ EXPECT_TRUE(RegisterFlagValidator(&FLAGS_test_flag, NULL));
+}
+
+TEST(FlagsValidator, ValidFlagViaSetDefault) {
+ EXPECT_TRUE(RegisterFlagValidator(&FLAGS_test_flag, &ValidateTestFlagIs5));
+ // SetCommandLineOptionWithMode returns the empty string on error.
+ EXPECT_NE("", SetCommandLineOptionWithMode("test_flag", "5",
+ SET_FLAG_IF_DEFAULT));
+ EXPECT_TRUE(RegisterFlagValidator(&FLAGS_test_flag, NULL));
+}
+
+TEST(FlagsValidator, ValidFlagViaSetValue) {
+ EXPECT_TRUE(RegisterFlagValidator(&FLAGS_test_flag, &ValidateTestFlagIs5));
+ FLAGS_test_flag = 100; // doesn't trigger the validator
+ // SetCommandLineOptionWithMode returns the empty string on error.
+ EXPECT_NE("", SetCommandLineOptionWithMode("test_flag", "5",
+ SET_FLAGS_VALUE));
+ EXPECT_NE("", SetCommandLineOptionWithMode("test_flag", "5",
+ SET_FLAGS_DEFAULT));
+ EXPECT_NE("", SetCommandLineOption("test_flag", "5"));
+ EXPECT_TRUE(RegisterFlagValidator(&FLAGS_test_flag, NULL));
+}
+
+#ifdef GTEST_HAS_DEATH_TEST
+TEST(FlagsValidatorDeathTest, InvalidFlagViaArgv) {
+ const char* argv[] = {
+ "my_test",
+ "--test_flag=50",
+ NULL,
+ };
+ EXPECT_TRUE(RegisterFlagValidator(&FLAGS_test_flag, &ValidateTestFlagIs5));
+ EXPECT_DEATH(ParseTestFlag(true, arraysize(argv) - 1, argv),
+ "ERROR: failed validation of new value '50' for flag 'test_flag'");
+ EXPECT_TRUE(RegisterFlagValidator(&FLAGS_test_flag, NULL));
+}
+#endif
+
+TEST(FlagsValidator, InvalidFlagViaSetDefault) {
+ EXPECT_TRUE(RegisterFlagValidator(&FLAGS_test_flag, &ValidateTestFlagIs5));
+ // SetCommandLineOptionWithMode returns the empty string on error.
+ EXPECT_EQ("", SetCommandLineOptionWithMode("test_flag", "50",
+ SET_FLAG_IF_DEFAULT));
+ EXPECT_EQ(-1, FLAGS_test_flag); // the setting-to-50 should have failed
+ EXPECT_TRUE(RegisterFlagValidator(&FLAGS_test_flag, NULL));
+}
+
+TEST(FlagsValidator, InvalidFlagViaSetValue) {
+ EXPECT_TRUE(RegisterFlagValidator(&FLAGS_test_flag, &ValidateTestFlagIs5));
+ FLAGS_test_flag = 100; // doesn't trigger the validator
+ // SetCommandLineOptionWithMode returns the empty string on error.
+ EXPECT_EQ("", SetCommandLineOptionWithMode("test_flag", "50",
+ SET_FLAGS_VALUE));
+ EXPECT_EQ("", SetCommandLineOptionWithMode("test_flag", "50",
+ SET_FLAGS_DEFAULT));
+ EXPECT_EQ("", SetCommandLineOption("test_flag", "50"));
+ EXPECT_EQ(100, FLAGS_test_flag); // the setting-to-50 should have failed
+ EXPECT_TRUE(RegisterFlagValidator(&FLAGS_test_flag, NULL));
+}
+
+#ifdef GTEST_HAS_DEATH_TEST
+TEST(FlagsValidatorDeathTest, InvalidFlagNeverSet) {
+ // If a flag keeps its default value, and that default value is
+ // invalid, we should die at argv-parse time.
+ const char* argv[] = {
+ "my_test",
+ NULL,
+ };
+ EXPECT_TRUE(RegisterFlagValidator(&FLAGS_test_flag, &ValidateTestFlagIs5));
+ EXPECT_DEATH(ParseTestFlag(true, arraysize(argv) - 1, argv),
+ "ERROR: --test_flag must be set on the commandline");
+}
+#endif
+
+TEST(FlagsValidator, InvalidFlagPtr) {
+ int32 dummy;
+ EXPECT_FALSE(RegisterFlagValidator(NULL, &ValidateTestFlagIs5));
+ EXPECT_FALSE(RegisterFlagValidator(&dummy, &ValidateTestFlagIs5));
+}
+
+TEST(FlagsValidator, RegisterValidatorTwice) {
+ EXPECT_TRUE(RegisterFlagValidator(&FLAGS_test_flag, &ValidateTestFlagIs5));
+ EXPECT_TRUE(RegisterFlagValidator(&FLAGS_test_flag, &ValidateTestFlagIs5));
+ EXPECT_FALSE(RegisterFlagValidator(&FLAGS_test_flag, &ValidateTestFlagIs10));
+ EXPECT_FALSE(RegisterFlagValidator(&FLAGS_test_flag, &ValidateTestFlagIs10));
+ EXPECT_TRUE(RegisterFlagValidator(&FLAGS_test_flag, &ValidateTestFlagIs5));
+ EXPECT_TRUE(RegisterFlagValidator(&FLAGS_test_flag, NULL));
+ EXPECT_TRUE(RegisterFlagValidator(&FLAGS_test_flag, &ValidateTestFlagIs10));
+ EXPECT_TRUE(RegisterFlagValidator(&FLAGS_test_flag, NULL));
+}
+
+TEST(FlagsValidator, CommandLineFlagInfo) {
+ CommandLineFlagInfo info;
+ info = GetCommandLineFlagInfoOrDie("test_flag");
+ EXPECT_FALSE(info.has_validator_fn);
+
+ EXPECT_TRUE(RegisterFlagValidator(&FLAGS_test_flag, &ValidateTestFlagIs5));
+ info = GetCommandLineFlagInfoOrDie("test_flag");
+ EXPECT_TRUE(info.has_validator_fn);
+
+ EXPECT_TRUE(RegisterFlagValidator(&FLAGS_test_flag, NULL));
+ info = GetCommandLineFlagInfoOrDie("test_flag");
+ EXPECT_FALSE(info.has_validator_fn);
+}
+
+TEST(FlagsValidator, FlagSaver) {
+ {
+ FlagSaver fs;
+ EXPECT_TRUE(RegisterFlagValidator(&FLAGS_test_flag, &ValidateTestFlagIs5));
+ EXPECT_EQ("", SetCommandLineOption("test_flag", "50")); // fails validation
+ }
+ EXPECT_NE("", SetCommandLineOption("test_flag", "50")); // validator is gone
+
+ EXPECT_TRUE(RegisterFlagValidator(&FLAGS_test_flag, &ValidateTestFlagIs5));
+ {
+ FlagSaver fs;
+ EXPECT_TRUE(RegisterFlagValidator(&FLAGS_test_flag, NULL));
+ EXPECT_NE("", SetCommandLineOption("test_flag", "50")); // no validator
+ }
+ EXPECT_EQ("", SetCommandLineOption("test_flag", "50")); // validator is back
+}
+
+
+} // unnamed namespace
+
+int main(int argc, char **argv) {
+
+ // Run unit tests only if called without arguments, otherwise this program
+ // is used by an "external" usage test
+ const bool run_tests = (argc == 1);
+
+ // We need to call SetArgv before parsing flags, so our "test" argv will
+ // win out over this executable's real argv. That makes running this
+ // test with a real --help flag kinda annoying, unfortunately.
+ const char* test_argv[] = { "/test/argv/for/gflags_unittest",
+ "argv 2", "3rd argv", "argv #4" };
+ SetArgv(arraysize(test_argv), test_argv);
+
+ // The first arg is the usage message, also important for testing.
+ string usage_message = (string(GetArgv0()) +
+ ": <useless flag> [...]\nDoes something useless.\n");
+
+ // We test setting tryfromenv manually, and making sure
+ // ParseCommandLineFlags still evaluates it.
+ FLAGS_tryfromenv = "test_tryfromenv";
+ setenv("FLAGS_test_tryfromenv", "pre-set", 1);
+
+ // Modify flag values from declared default value in two ways.
+ // The recommended way:
+ SetCommandLineOptionWithMode("changed_bool1", "true", SET_FLAGS_DEFAULT);
+
+ // The non-recommended way:
+ FLAGS_changed_bool2 = true;
+
+ SetUsageMessage(usage_message.c_str());
+ SetVersionString("test_version");
+ ParseCommandLineFlags(&argc, &argv, true);
+ MakeTmpdir(&FLAGS_test_tmpdir);
+
+ int exit_status = 0;
+ if (run_tests) {
+ fprintf(stdout, "Running the unit tests now...\n\n"); fflush(stdout);
+ exit_status = RUN_ALL_TESTS();
+ } else fprintf(stderr, "\n\nPASS\n");
+ ShutDownCommandLineFlags();
+ return exit_status;
+}
+
+} // GFLAGS_NAMESPACE
+
+int main(int argc, char** argv) {
+ return GFLAGS_NAMESPACE::main(argc, argv);
+}
+
diff --git a/third_party/gflags/test/gflags_unittest_flagfile b/third_party/gflags/test/gflags_unittest_flagfile
new file mode 100644
index 0000000..f4fa0c4
--- /dev/null
+++ b/third_party/gflags/test/gflags_unittest_flagfile
@@ -0,0 +1,2 @@
+--test_flag=1
+--test_flag=2
diff --git a/third_party/gflags/test/nc/CMakeLists.txt b/third_party/gflags/test/nc/CMakeLists.txt
new file mode 100644
index 0000000..e425d91
--- /dev/null
+++ b/third_party/gflags/test/nc/CMakeLists.txt
@@ -0,0 +1,16 @@
+## gflags negative compilation tests
+
+cmake_minimum_required (VERSION 2.8)
+
+if (NOT TEST_NAME)
+ message (FATAL_ERROR "Missing TEST_NAME CMake flag")
+endif ()
+string (TOUPPER ${TEST_NAME} TEST_NAME_UPPER)
+
+project (gflags_${TEST_NAME})
+
+find_package (gflags REQUIRED)
+include_directories ("${CMAKE_CURRENT_SOURCE_DIR}/..")
+add_definitions (-DTEST_${TEST_NAME_UPPER})
+add_executable (gflags_${TEST_NAME} gflags_nc.cc)
+target_link_libraries(gflags_${TEST_NAME} ${gflags_LIBRARIES})
diff --git a/third_party/gflags/test/nc/gflags_nc.cc b/third_party/gflags/test/nc/gflags_nc.cc
new file mode 100644
index 0000000..1990c30
--- /dev/null
+++ b/third_party/gflags/test/nc/gflags_nc.cc
@@ -0,0 +1,73 @@
+// Copyright (c) 2009, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// ---
+//
+// A negative comiple test for gflags.
+
+#include <gflags/gflags.h>
+
+#if defined(TEST_NC_SWAPPED_ARGS)
+
+DEFINE_bool(some_bool_flag,
+ "the default value should go here, not the description",
+ false);
+
+
+#elif defined(TEST_NC_INT_INSTEAD_OF_BOOL)
+
+DEFINE_bool(some_bool_flag_2,
+ 0,
+ "should have been an int32 flag but mistakenly used bool instead");
+
+#elif defined(TEST_NC_BOOL_IN_QUOTES)
+
+
+DEFINE_bool(some_bool_flag_3,
+ "false",
+ "false in in quotes, which is wrong");
+
+#elif defined(TEST_NC_SANITY)
+
+DEFINE_bool(some_bool_flag_4,
+ true,
+ "this is the correct usage of DEFINE_bool");
+
+#elif defined(TEST_NC_DEFINE_STRING_WITH_0)
+
+DEFINE_string(some_string_flag,
+ 0,
+ "Trying to construct a string by passing 0 would cause a crash.");
+
+#endif
+
+int main(int, char **)
+{
+ return 0;
+}
diff --git a/.travis.yml b/third_party/googletest/.travis.yml
similarity index 100%
rename from .travis.yml
rename to third_party/googletest/.travis.yml
diff --git a/README.md b/third_party/googletest/README.md
similarity index 100%
rename from README.md
rename to third_party/googletest/README.md
diff --git a/googlemock/CHANGES b/third_party/googletest/googlemock/CHANGES
similarity index 100%
rename from googlemock/CHANGES
rename to third_party/googletest/googlemock/CHANGES
diff --git a/googlemock/CMakeLists.txt b/third_party/googletest/googlemock/CMakeLists.txt
similarity index 100%
rename from googlemock/CMakeLists.txt
rename to third_party/googletest/googlemock/CMakeLists.txt
diff --git a/googlemock/CONTRIBUTORS b/third_party/googletest/googlemock/CONTRIBUTORS
similarity index 100%
rename from googlemock/CONTRIBUTORS
rename to third_party/googletest/googlemock/CONTRIBUTORS
diff --git a/googlemock/LICENSE b/third_party/googletest/googlemock/LICENSE
similarity index 100%
rename from googlemock/LICENSE
rename to third_party/googletest/googlemock/LICENSE
diff --git a/googlemock/Makefile.am b/third_party/googletest/googlemock/Makefile.am
similarity index 100%
rename from googlemock/Makefile.am
rename to third_party/googletest/googlemock/Makefile.am
diff --git a/googlemock/README.md b/third_party/googletest/googlemock/README.md
similarity index 100%
rename from googlemock/README.md
rename to third_party/googletest/googlemock/README.md
diff --git a/googlemock/build-aux/.keep b/third_party/googletest/googlemock/build-aux/.keep
similarity index 100%
rename from googlemock/build-aux/.keep
rename to third_party/googletest/googlemock/build-aux/.keep
diff --git a/googlemock/configure.ac b/third_party/googletest/googlemock/configure.ac
similarity index 100%
rename from googlemock/configure.ac
rename to third_party/googletest/googlemock/configure.ac
diff --git a/googlemock/docs/CheatSheet.md b/third_party/googletest/googlemock/docs/CheatSheet.md
similarity index 100%
rename from googlemock/docs/CheatSheet.md
rename to third_party/googletest/googlemock/docs/CheatSheet.md
diff --git a/googlemock/docs/CookBook.md b/third_party/googletest/googlemock/docs/CookBook.md
similarity index 100%
rename from googlemock/docs/CookBook.md
rename to third_party/googletest/googlemock/docs/CookBook.md
diff --git a/googlemock/docs/DesignDoc.md b/third_party/googletest/googlemock/docs/DesignDoc.md
similarity index 100%
rename from googlemock/docs/DesignDoc.md
rename to third_party/googletest/googlemock/docs/DesignDoc.md
diff --git a/googlemock/docs/DevGuide.md b/third_party/googletest/googlemock/docs/DevGuide.md
similarity index 100%
rename from googlemock/docs/DevGuide.md
rename to third_party/googletest/googlemock/docs/DevGuide.md
diff --git a/googlemock/docs/Documentation.md b/third_party/googletest/googlemock/docs/Documentation.md
similarity index 100%
rename from googlemock/docs/Documentation.md
rename to third_party/googletest/googlemock/docs/Documentation.md
diff --git a/googlemock/docs/ForDummies.md b/third_party/googletest/googlemock/docs/ForDummies.md
similarity index 100%
rename from googlemock/docs/ForDummies.md
rename to third_party/googletest/googlemock/docs/ForDummies.md
diff --git a/googlemock/docs/FrequentlyAskedQuestions.md b/third_party/googletest/googlemock/docs/FrequentlyAskedQuestions.md
similarity index 100%
rename from googlemock/docs/FrequentlyAskedQuestions.md
rename to third_party/googletest/googlemock/docs/FrequentlyAskedQuestions.md
diff --git a/googlemock/docs/KnownIssues.md b/third_party/googletest/googlemock/docs/KnownIssues.md
similarity index 100%
rename from googlemock/docs/KnownIssues.md
rename to third_party/googletest/googlemock/docs/KnownIssues.md
diff --git a/googlemock/docs/v1_5/CheatSheet.md b/third_party/googletest/googlemock/docs/v1_5/CheatSheet.md
similarity index 100%
rename from googlemock/docs/v1_5/CheatSheet.md
rename to third_party/googletest/googlemock/docs/v1_5/CheatSheet.md
diff --git a/googlemock/docs/v1_5/CookBook.md b/third_party/googletest/googlemock/docs/v1_5/CookBook.md
similarity index 100%
rename from googlemock/docs/v1_5/CookBook.md
rename to third_party/googletest/googlemock/docs/v1_5/CookBook.md
diff --git a/googlemock/docs/v1_5/Documentation.md b/third_party/googletest/googlemock/docs/v1_5/Documentation.md
similarity index 100%
rename from googlemock/docs/v1_5/Documentation.md
rename to third_party/googletest/googlemock/docs/v1_5/Documentation.md
diff --git a/googlemock/docs/v1_5/ForDummies.md b/third_party/googletest/googlemock/docs/v1_5/ForDummies.md
similarity index 100%
rename from googlemock/docs/v1_5/ForDummies.md
rename to third_party/googletest/googlemock/docs/v1_5/ForDummies.md
diff --git a/googlemock/docs/v1_5/FrequentlyAskedQuestions.md b/third_party/googletest/googlemock/docs/v1_5/FrequentlyAskedQuestions.md
similarity index 100%
rename from googlemock/docs/v1_5/FrequentlyAskedQuestions.md
rename to third_party/googletest/googlemock/docs/v1_5/FrequentlyAskedQuestions.md
diff --git a/googlemock/docs/v1_6/CheatSheet.md b/third_party/googletest/googlemock/docs/v1_6/CheatSheet.md
similarity index 100%
rename from googlemock/docs/v1_6/CheatSheet.md
rename to third_party/googletest/googlemock/docs/v1_6/CheatSheet.md
diff --git a/googlemock/docs/v1_6/CookBook.md b/third_party/googletest/googlemock/docs/v1_6/CookBook.md
similarity index 100%
rename from googlemock/docs/v1_6/CookBook.md
rename to third_party/googletest/googlemock/docs/v1_6/CookBook.md
diff --git a/googlemock/docs/v1_6/Documentation.md b/third_party/googletest/googlemock/docs/v1_6/Documentation.md
similarity index 100%
rename from googlemock/docs/v1_6/Documentation.md
rename to third_party/googletest/googlemock/docs/v1_6/Documentation.md
diff --git a/googlemock/docs/v1_6/ForDummies.md b/third_party/googletest/googlemock/docs/v1_6/ForDummies.md
similarity index 100%
rename from googlemock/docs/v1_6/ForDummies.md
rename to third_party/googletest/googlemock/docs/v1_6/ForDummies.md
diff --git a/googlemock/docs/v1_6/FrequentlyAskedQuestions.md b/third_party/googletest/googlemock/docs/v1_6/FrequentlyAskedQuestions.md
similarity index 100%
rename from googlemock/docs/v1_6/FrequentlyAskedQuestions.md
rename to third_party/googletest/googlemock/docs/v1_6/FrequentlyAskedQuestions.md
diff --git a/googlemock/docs/v1_7/CheatSheet.md b/third_party/googletest/googlemock/docs/v1_7/CheatSheet.md
similarity index 100%
rename from googlemock/docs/v1_7/CheatSheet.md
rename to third_party/googletest/googlemock/docs/v1_7/CheatSheet.md
diff --git a/googlemock/docs/v1_7/CookBook.md b/third_party/googletest/googlemock/docs/v1_7/CookBook.md
similarity index 100%
rename from googlemock/docs/v1_7/CookBook.md
rename to third_party/googletest/googlemock/docs/v1_7/CookBook.md
diff --git a/googlemock/docs/v1_7/Documentation.md b/third_party/googletest/googlemock/docs/v1_7/Documentation.md
similarity index 100%
rename from googlemock/docs/v1_7/Documentation.md
rename to third_party/googletest/googlemock/docs/v1_7/Documentation.md
diff --git a/googlemock/docs/v1_7/ForDummies.md b/third_party/googletest/googlemock/docs/v1_7/ForDummies.md
similarity index 100%
rename from googlemock/docs/v1_7/ForDummies.md
rename to third_party/googletest/googlemock/docs/v1_7/ForDummies.md
diff --git a/googlemock/docs/v1_7/FrequentlyAskedQuestions.md b/third_party/googletest/googlemock/docs/v1_7/FrequentlyAskedQuestions.md
similarity index 100%
rename from googlemock/docs/v1_7/FrequentlyAskedQuestions.md
rename to third_party/googletest/googlemock/docs/v1_7/FrequentlyAskedQuestions.md
diff --git a/googlemock/include/gmock/gmock-actions.h b/third_party/googletest/googlemock/include/gmock/gmock-actions.h
similarity index 100%
rename from googlemock/include/gmock/gmock-actions.h
rename to third_party/googletest/googlemock/include/gmock/gmock-actions.h
diff --git a/googlemock/include/gmock/gmock-cardinalities.h b/third_party/googletest/googlemock/include/gmock/gmock-cardinalities.h
similarity index 100%
rename from googlemock/include/gmock/gmock-cardinalities.h
rename to third_party/googletest/googlemock/include/gmock/gmock-cardinalities.h
diff --git a/googlemock/include/gmock/gmock-generated-actions.h b/third_party/googletest/googlemock/include/gmock/gmock-generated-actions.h
similarity index 100%
rename from googlemock/include/gmock/gmock-generated-actions.h
rename to third_party/googletest/googlemock/include/gmock/gmock-generated-actions.h
diff --git a/googlemock/include/gmock/gmock-generated-actions.h.pump b/third_party/googletest/googlemock/include/gmock/gmock-generated-actions.h.pump
similarity index 100%
rename from googlemock/include/gmock/gmock-generated-actions.h.pump
rename to third_party/googletest/googlemock/include/gmock/gmock-generated-actions.h.pump
diff --git a/googlemock/include/gmock/gmock-generated-function-mockers.h b/third_party/googletest/googlemock/include/gmock/gmock-generated-function-mockers.h
similarity index 100%
rename from googlemock/include/gmock/gmock-generated-function-mockers.h
rename to third_party/googletest/googlemock/include/gmock/gmock-generated-function-mockers.h
diff --git a/googlemock/include/gmock/gmock-generated-function-mockers.h.pump b/third_party/googletest/googlemock/include/gmock/gmock-generated-function-mockers.h.pump
similarity index 100%
rename from googlemock/include/gmock/gmock-generated-function-mockers.h.pump
rename to third_party/googletest/googlemock/include/gmock/gmock-generated-function-mockers.h.pump
diff --git a/googlemock/include/gmock/gmock-generated-matchers.h b/third_party/googletest/googlemock/include/gmock/gmock-generated-matchers.h
similarity index 100%
rename from googlemock/include/gmock/gmock-generated-matchers.h
rename to third_party/googletest/googlemock/include/gmock/gmock-generated-matchers.h
diff --git a/googlemock/include/gmock/gmock-generated-matchers.h.pump b/third_party/googletest/googlemock/include/gmock/gmock-generated-matchers.h.pump
similarity index 100%
rename from googlemock/include/gmock/gmock-generated-matchers.h.pump
rename to third_party/googletest/googlemock/include/gmock/gmock-generated-matchers.h.pump
diff --git a/googlemock/include/gmock/gmock-generated-nice-strict.h b/third_party/googletest/googlemock/include/gmock/gmock-generated-nice-strict.h
similarity index 100%
rename from googlemock/include/gmock/gmock-generated-nice-strict.h
rename to third_party/googletest/googlemock/include/gmock/gmock-generated-nice-strict.h
diff --git a/googlemock/include/gmock/gmock-generated-nice-strict.h.pump b/third_party/googletest/googlemock/include/gmock/gmock-generated-nice-strict.h.pump
similarity index 100%
rename from googlemock/include/gmock/gmock-generated-nice-strict.h.pump
rename to third_party/googletest/googlemock/include/gmock/gmock-generated-nice-strict.h.pump
diff --git a/googlemock/include/gmock/gmock-matchers.h b/third_party/googletest/googlemock/include/gmock/gmock-matchers.h
similarity index 100%
rename from googlemock/include/gmock/gmock-matchers.h
rename to third_party/googletest/googlemock/include/gmock/gmock-matchers.h
diff --git a/googlemock/include/gmock/gmock-more-actions.h b/third_party/googletest/googlemock/include/gmock/gmock-more-actions.h
similarity index 100%
rename from googlemock/include/gmock/gmock-more-actions.h
rename to third_party/googletest/googlemock/include/gmock/gmock-more-actions.h
diff --git a/googlemock/include/gmock/gmock-more-matchers.h b/third_party/googletest/googlemock/include/gmock/gmock-more-matchers.h
similarity index 100%
rename from googlemock/include/gmock/gmock-more-matchers.h
rename to third_party/googletest/googlemock/include/gmock/gmock-more-matchers.h
diff --git a/googlemock/include/gmock/gmock-spec-builders.h b/third_party/googletest/googlemock/include/gmock/gmock-spec-builders.h
similarity index 100%
rename from googlemock/include/gmock/gmock-spec-builders.h
rename to third_party/googletest/googlemock/include/gmock/gmock-spec-builders.h
diff --git a/googlemock/include/gmock/gmock.h b/third_party/googletest/googlemock/include/gmock/gmock.h
similarity index 100%
rename from googlemock/include/gmock/gmock.h
rename to third_party/googletest/googlemock/include/gmock/gmock.h
diff --git a/googlemock/include/gmock/internal/custom/gmock-generated-actions.h b/third_party/googletest/googlemock/include/gmock/internal/custom/gmock-generated-actions.h
similarity index 100%
rename from googlemock/include/gmock/internal/custom/gmock-generated-actions.h
rename to third_party/googletest/googlemock/include/gmock/internal/custom/gmock-generated-actions.h
diff --git a/googlemock/include/gmock/internal/custom/gmock-generated-actions.h.pump b/third_party/googletest/googlemock/include/gmock/internal/custom/gmock-generated-actions.h.pump
similarity index 100%
rename from googlemock/include/gmock/internal/custom/gmock-generated-actions.h.pump
rename to third_party/googletest/googlemock/include/gmock/internal/custom/gmock-generated-actions.h.pump
diff --git a/googlemock/include/gmock/internal/custom/gmock-matchers.h b/third_party/googletest/googlemock/include/gmock/internal/custom/gmock-matchers.h
similarity index 100%
rename from googlemock/include/gmock/internal/custom/gmock-matchers.h
rename to third_party/googletest/googlemock/include/gmock/internal/custom/gmock-matchers.h
diff --git a/googlemock/include/gmock/internal/custom/gmock-port.h b/third_party/googletest/googlemock/include/gmock/internal/custom/gmock-port.h
similarity index 100%
rename from googlemock/include/gmock/internal/custom/gmock-port.h
rename to third_party/googletest/googlemock/include/gmock/internal/custom/gmock-port.h
diff --git a/googlemock/include/gmock/internal/gmock-generated-internal-utils.h b/third_party/googletest/googlemock/include/gmock/internal/gmock-generated-internal-utils.h
similarity index 100%
rename from googlemock/include/gmock/internal/gmock-generated-internal-utils.h
rename to third_party/googletest/googlemock/include/gmock/internal/gmock-generated-internal-utils.h
diff --git a/googlemock/include/gmock/internal/gmock-generated-internal-utils.h.pump b/third_party/googletest/googlemock/include/gmock/internal/gmock-generated-internal-utils.h.pump
similarity index 100%
rename from googlemock/include/gmock/internal/gmock-generated-internal-utils.h.pump
rename to third_party/googletest/googlemock/include/gmock/internal/gmock-generated-internal-utils.h.pump
diff --git a/googlemock/include/gmock/internal/gmock-internal-utils.h b/third_party/googletest/googlemock/include/gmock/internal/gmock-internal-utils.h
similarity index 100%
rename from googlemock/include/gmock/internal/gmock-internal-utils.h
rename to third_party/googletest/googlemock/include/gmock/internal/gmock-internal-utils.h
diff --git a/googlemock/include/gmock/internal/gmock-port.h b/third_party/googletest/googlemock/include/gmock/internal/gmock-port.h
similarity index 100%
rename from googlemock/include/gmock/internal/gmock-port.h
rename to third_party/googletest/googlemock/include/gmock/internal/gmock-port.h
diff --git a/googlemock/make/Makefile b/third_party/googletest/googlemock/make/Makefile
similarity index 100%
rename from googlemock/make/Makefile
rename to third_party/googletest/googlemock/make/Makefile
diff --git a/googlemock/msvc/2005/gmock.sln b/third_party/googletest/googlemock/msvc/2005/gmock.sln
similarity index 100%
rename from googlemock/msvc/2005/gmock.sln
rename to third_party/googletest/googlemock/msvc/2005/gmock.sln
diff --git a/googlemock/msvc/2005/gmock.vcproj b/third_party/googletest/googlemock/msvc/2005/gmock.vcproj
similarity index 100%
rename from googlemock/msvc/2005/gmock.vcproj
rename to third_party/googletest/googlemock/msvc/2005/gmock.vcproj
diff --git a/googlemock/msvc/2005/gmock_config.vsprops b/third_party/googletest/googlemock/msvc/2005/gmock_config.vsprops
similarity index 100%
rename from googlemock/msvc/2005/gmock_config.vsprops
rename to third_party/googletest/googlemock/msvc/2005/gmock_config.vsprops
diff --git a/googlemock/msvc/2005/gmock_main.vcproj b/third_party/googletest/googlemock/msvc/2005/gmock_main.vcproj
similarity index 100%
rename from googlemock/msvc/2005/gmock_main.vcproj
rename to third_party/googletest/googlemock/msvc/2005/gmock_main.vcproj
diff --git a/googlemock/msvc/2005/gmock_test.vcproj b/third_party/googletest/googlemock/msvc/2005/gmock_test.vcproj
similarity index 100%
rename from googlemock/msvc/2005/gmock_test.vcproj
rename to third_party/googletest/googlemock/msvc/2005/gmock_test.vcproj
diff --git a/googlemock/msvc/2010/gmock.sln b/third_party/googletest/googlemock/msvc/2010/gmock.sln
similarity index 100%
rename from googlemock/msvc/2010/gmock.sln
rename to third_party/googletest/googlemock/msvc/2010/gmock.sln
diff --git a/googlemock/msvc/2010/gmock.vcxproj b/third_party/googletest/googlemock/msvc/2010/gmock.vcxproj
similarity index 100%
rename from googlemock/msvc/2010/gmock.vcxproj
rename to third_party/googletest/googlemock/msvc/2010/gmock.vcxproj
diff --git a/googlemock/msvc/2010/gmock_config.props b/third_party/googletest/googlemock/msvc/2010/gmock_config.props
similarity index 100%
rename from googlemock/msvc/2010/gmock_config.props
rename to third_party/googletest/googlemock/msvc/2010/gmock_config.props
diff --git a/googlemock/msvc/2010/gmock_main.vcxproj b/third_party/googletest/googlemock/msvc/2010/gmock_main.vcxproj
similarity index 100%
rename from googlemock/msvc/2010/gmock_main.vcxproj
rename to third_party/googletest/googlemock/msvc/2010/gmock_main.vcxproj
diff --git a/googlemock/msvc/2010/gmock_test.vcxproj b/third_party/googletest/googlemock/msvc/2010/gmock_test.vcxproj
similarity index 100%
rename from googlemock/msvc/2010/gmock_test.vcxproj
rename to third_party/googletest/googlemock/msvc/2010/gmock_test.vcxproj
diff --git a/googlemock/scripts/fuse_gmock_files.py b/third_party/googletest/googlemock/scripts/fuse_gmock_files.py
similarity index 100%
rename from googlemock/scripts/fuse_gmock_files.py
rename to third_party/googletest/googlemock/scripts/fuse_gmock_files.py
diff --git a/googlemock/scripts/generator/LICENSE b/third_party/googletest/googlemock/scripts/generator/LICENSE
similarity index 100%
rename from googlemock/scripts/generator/LICENSE
rename to third_party/googletest/googlemock/scripts/generator/LICENSE
diff --git a/googlemock/scripts/generator/README b/third_party/googletest/googlemock/scripts/generator/README
similarity index 100%
rename from googlemock/scripts/generator/README
rename to third_party/googletest/googlemock/scripts/generator/README
diff --git a/googlemock/scripts/generator/README.cppclean b/third_party/googletest/googlemock/scripts/generator/README.cppclean
similarity index 100%
rename from googlemock/scripts/generator/README.cppclean
rename to third_party/googletest/googlemock/scripts/generator/README.cppclean
diff --git a/googlemock/scripts/generator/cpp/__init__.py b/third_party/googletest/googlemock/scripts/generator/cpp/__init__.py
similarity index 100%
rename from googlemock/scripts/generator/cpp/__init__.py
rename to third_party/googletest/googlemock/scripts/generator/cpp/__init__.py
diff --git a/googlemock/scripts/generator/cpp/ast.py b/third_party/googletest/googlemock/scripts/generator/cpp/ast.py
similarity index 100%
rename from googlemock/scripts/generator/cpp/ast.py
rename to third_party/googletest/googlemock/scripts/generator/cpp/ast.py
diff --git a/googlemock/scripts/generator/cpp/gmock_class.py b/third_party/googletest/googlemock/scripts/generator/cpp/gmock_class.py
similarity index 100%
rename from googlemock/scripts/generator/cpp/gmock_class.py
rename to third_party/googletest/googlemock/scripts/generator/cpp/gmock_class.py
diff --git a/googlemock/scripts/generator/cpp/gmock_class_test.py b/third_party/googletest/googlemock/scripts/generator/cpp/gmock_class_test.py
similarity index 100%
rename from googlemock/scripts/generator/cpp/gmock_class_test.py
rename to third_party/googletest/googlemock/scripts/generator/cpp/gmock_class_test.py
diff --git a/googlemock/scripts/generator/cpp/keywords.py b/third_party/googletest/googlemock/scripts/generator/cpp/keywords.py
similarity index 100%
rename from googlemock/scripts/generator/cpp/keywords.py
rename to third_party/googletest/googlemock/scripts/generator/cpp/keywords.py
diff --git a/googlemock/scripts/generator/cpp/tokenize.py b/third_party/googletest/googlemock/scripts/generator/cpp/tokenize.py
similarity index 100%
rename from googlemock/scripts/generator/cpp/tokenize.py
rename to third_party/googletest/googlemock/scripts/generator/cpp/tokenize.py
diff --git a/googlemock/scripts/generator/cpp/utils.py b/third_party/googletest/googlemock/scripts/generator/cpp/utils.py
similarity index 100%
rename from googlemock/scripts/generator/cpp/utils.py
rename to third_party/googletest/googlemock/scripts/generator/cpp/utils.py
diff --git a/googlemock/scripts/generator/gmock_gen.py b/third_party/googletest/googlemock/scripts/generator/gmock_gen.py
similarity index 100%
rename from googlemock/scripts/generator/gmock_gen.py
rename to third_party/googletest/googlemock/scripts/generator/gmock_gen.py
diff --git a/googlemock/scripts/gmock-config.in b/third_party/googletest/googlemock/scripts/gmock-config.in
similarity index 100%
rename from googlemock/scripts/gmock-config.in
rename to third_party/googletest/googlemock/scripts/gmock-config.in
diff --git a/googlemock/scripts/gmock_doctor.py b/third_party/googletest/googlemock/scripts/gmock_doctor.py
similarity index 100%
rename from googlemock/scripts/gmock_doctor.py
rename to third_party/googletest/googlemock/scripts/gmock_doctor.py
diff --git a/googlemock/scripts/upload.py b/third_party/googletest/googlemock/scripts/upload.py
similarity index 100%
rename from googlemock/scripts/upload.py
rename to third_party/googletest/googlemock/scripts/upload.py
diff --git a/googlemock/scripts/upload_gmock.py b/third_party/googletest/googlemock/scripts/upload_gmock.py
similarity index 100%
rename from googlemock/scripts/upload_gmock.py
rename to third_party/googletest/googlemock/scripts/upload_gmock.py
diff --git a/googlemock/src/gmock-all.cc b/third_party/googletest/googlemock/src/gmock-all.cc
similarity index 100%
rename from googlemock/src/gmock-all.cc
rename to third_party/googletest/googlemock/src/gmock-all.cc
diff --git a/googlemock/src/gmock-cardinalities.cc b/third_party/googletest/googlemock/src/gmock-cardinalities.cc
similarity index 100%
rename from googlemock/src/gmock-cardinalities.cc
rename to third_party/googletest/googlemock/src/gmock-cardinalities.cc
diff --git a/googlemock/src/gmock-internal-utils.cc b/third_party/googletest/googlemock/src/gmock-internal-utils.cc
similarity index 100%
rename from googlemock/src/gmock-internal-utils.cc
rename to third_party/googletest/googlemock/src/gmock-internal-utils.cc
diff --git a/googlemock/src/gmock-matchers.cc b/third_party/googletest/googlemock/src/gmock-matchers.cc
similarity index 100%
rename from googlemock/src/gmock-matchers.cc
rename to third_party/googletest/googlemock/src/gmock-matchers.cc
diff --git a/googlemock/src/gmock-spec-builders.cc b/third_party/googletest/googlemock/src/gmock-spec-builders.cc
similarity index 100%
rename from googlemock/src/gmock-spec-builders.cc
rename to third_party/googletest/googlemock/src/gmock-spec-builders.cc
diff --git a/googlemock/src/gmock.cc b/third_party/googletest/googlemock/src/gmock.cc
similarity index 100%
rename from googlemock/src/gmock.cc
rename to third_party/googletest/googlemock/src/gmock.cc
diff --git a/googlemock/src/gmock_main.cc b/third_party/googletest/googlemock/src/gmock_main.cc
similarity index 100%
rename from googlemock/src/gmock_main.cc
rename to third_party/googletest/googlemock/src/gmock_main.cc
diff --git a/googlemock/test/gmock-actions_test.cc b/third_party/googletest/googlemock/test/gmock-actions_test.cc
similarity index 100%
rename from googlemock/test/gmock-actions_test.cc
rename to third_party/googletest/googlemock/test/gmock-actions_test.cc
diff --git a/googlemock/test/gmock-cardinalities_test.cc b/third_party/googletest/googlemock/test/gmock-cardinalities_test.cc
similarity index 100%
rename from googlemock/test/gmock-cardinalities_test.cc
rename to third_party/googletest/googlemock/test/gmock-cardinalities_test.cc
diff --git a/googlemock/test/gmock-generated-actions_test.cc b/third_party/googletest/googlemock/test/gmock-generated-actions_test.cc
similarity index 100%
rename from googlemock/test/gmock-generated-actions_test.cc
rename to third_party/googletest/googlemock/test/gmock-generated-actions_test.cc
diff --git a/googlemock/test/gmock-generated-function-mockers_test.cc b/third_party/googletest/googlemock/test/gmock-generated-function-mockers_test.cc
similarity index 100%
rename from googlemock/test/gmock-generated-function-mockers_test.cc
rename to third_party/googletest/googlemock/test/gmock-generated-function-mockers_test.cc
diff --git a/googlemock/test/gmock-generated-internal-utils_test.cc b/third_party/googletest/googlemock/test/gmock-generated-internal-utils_test.cc
similarity index 100%
rename from googlemock/test/gmock-generated-internal-utils_test.cc
rename to third_party/googletest/googlemock/test/gmock-generated-internal-utils_test.cc
diff --git a/googlemock/test/gmock-generated-matchers_test.cc b/third_party/googletest/googlemock/test/gmock-generated-matchers_test.cc
similarity index 100%
rename from googlemock/test/gmock-generated-matchers_test.cc
rename to third_party/googletest/googlemock/test/gmock-generated-matchers_test.cc
diff --git a/googlemock/test/gmock-internal-utils_test.cc b/third_party/googletest/googlemock/test/gmock-internal-utils_test.cc
similarity index 100%
rename from googlemock/test/gmock-internal-utils_test.cc
rename to third_party/googletest/googlemock/test/gmock-internal-utils_test.cc
diff --git a/googlemock/test/gmock-matchers_test.cc b/third_party/googletest/googlemock/test/gmock-matchers_test.cc
similarity index 100%
rename from googlemock/test/gmock-matchers_test.cc
rename to third_party/googletest/googlemock/test/gmock-matchers_test.cc
diff --git a/googlemock/test/gmock-more-actions_test.cc b/third_party/googletest/googlemock/test/gmock-more-actions_test.cc
similarity index 100%
rename from googlemock/test/gmock-more-actions_test.cc
rename to third_party/googletest/googlemock/test/gmock-more-actions_test.cc
diff --git a/googlemock/test/gmock-nice-strict_test.cc b/third_party/googletest/googlemock/test/gmock-nice-strict_test.cc
similarity index 100%
rename from googlemock/test/gmock-nice-strict_test.cc
rename to third_party/googletest/googlemock/test/gmock-nice-strict_test.cc
diff --git a/googlemock/test/gmock-port_test.cc b/third_party/googletest/googlemock/test/gmock-port_test.cc
similarity index 100%
rename from googlemock/test/gmock-port_test.cc
rename to third_party/googletest/googlemock/test/gmock-port_test.cc
diff --git a/googlemock/test/gmock-spec-builders_test.cc b/third_party/googletest/googlemock/test/gmock-spec-builders_test.cc
similarity index 100%
rename from googlemock/test/gmock-spec-builders_test.cc
rename to third_party/googletest/googlemock/test/gmock-spec-builders_test.cc
diff --git a/googlemock/test/gmock_all_test.cc b/third_party/googletest/googlemock/test/gmock_all_test.cc
similarity index 100%
rename from googlemock/test/gmock_all_test.cc
rename to third_party/googletest/googlemock/test/gmock_all_test.cc
diff --git a/googlemock/test/gmock_ex_test.cc b/third_party/googletest/googlemock/test/gmock_ex_test.cc
similarity index 100%
rename from googlemock/test/gmock_ex_test.cc
rename to third_party/googletest/googlemock/test/gmock_ex_test.cc
diff --git a/googlemock/test/gmock_leak_test.py b/third_party/googletest/googlemock/test/gmock_leak_test.py
similarity index 100%
rename from googlemock/test/gmock_leak_test.py
rename to third_party/googletest/googlemock/test/gmock_leak_test.py
diff --git a/googlemock/test/gmock_leak_test_.cc b/third_party/googletest/googlemock/test/gmock_leak_test_.cc
similarity index 100%
rename from googlemock/test/gmock_leak_test_.cc
rename to third_party/googletest/googlemock/test/gmock_leak_test_.cc
diff --git a/googlemock/test/gmock_link2_test.cc b/third_party/googletest/googlemock/test/gmock_link2_test.cc
similarity index 100%
rename from googlemock/test/gmock_link2_test.cc
rename to third_party/googletest/googlemock/test/gmock_link2_test.cc
diff --git a/googlemock/test/gmock_link_test.cc b/third_party/googletest/googlemock/test/gmock_link_test.cc
similarity index 100%
rename from googlemock/test/gmock_link_test.cc
rename to third_party/googletest/googlemock/test/gmock_link_test.cc
diff --git a/googlemock/test/gmock_link_test.h b/third_party/googletest/googlemock/test/gmock_link_test.h
similarity index 100%
rename from googlemock/test/gmock_link_test.h
rename to third_party/googletest/googlemock/test/gmock_link_test.h
diff --git a/googlemock/test/gmock_output_test.py b/third_party/googletest/googlemock/test/gmock_output_test.py
similarity index 100%
rename from googlemock/test/gmock_output_test.py
rename to third_party/googletest/googlemock/test/gmock_output_test.py
diff --git a/googlemock/test/gmock_output_test_.cc b/third_party/googletest/googlemock/test/gmock_output_test_.cc
similarity index 100%
rename from googlemock/test/gmock_output_test_.cc
rename to third_party/googletest/googlemock/test/gmock_output_test_.cc
diff --git a/googlemock/test/gmock_output_test_golden.txt b/third_party/googletest/googlemock/test/gmock_output_test_golden.txt
similarity index 100%
rename from googlemock/test/gmock_output_test_golden.txt
rename to third_party/googletest/googlemock/test/gmock_output_test_golden.txt
diff --git a/googlemock/test/gmock_stress_test.cc b/third_party/googletest/googlemock/test/gmock_stress_test.cc
similarity index 100%
rename from googlemock/test/gmock_stress_test.cc
rename to third_party/googletest/googlemock/test/gmock_stress_test.cc
diff --git a/googlemock/test/gmock_test.cc b/third_party/googletest/googlemock/test/gmock_test.cc
similarity index 100%
rename from googlemock/test/gmock_test.cc
rename to third_party/googletest/googlemock/test/gmock_test.cc
diff --git a/googlemock/test/gmock_test_utils.py b/third_party/googletest/googlemock/test/gmock_test_utils.py
similarity index 100%
rename from googlemock/test/gmock_test_utils.py
rename to third_party/googletest/googlemock/test/gmock_test_utils.py
diff --git a/googletest/CHANGES b/third_party/googletest/googletest/CHANGES
similarity index 100%
rename from googletest/CHANGES
rename to third_party/googletest/googletest/CHANGES
diff --git a/googletest/CMakeLists.txt b/third_party/googletest/googletest/CMakeLists.txt
similarity index 100%
rename from googletest/CMakeLists.txt
rename to third_party/googletest/googletest/CMakeLists.txt
diff --git a/googletest/CONTRIBUTORS b/third_party/googletest/googletest/CONTRIBUTORS
similarity index 100%
rename from googletest/CONTRIBUTORS
rename to third_party/googletest/googletest/CONTRIBUTORS
diff --git a/googletest/LICENSE b/third_party/googletest/googletest/LICENSE
similarity index 100%
rename from googletest/LICENSE
rename to third_party/googletest/googletest/LICENSE
diff --git a/googletest/Makefile.am b/third_party/googletest/googletest/Makefile.am
similarity index 100%
rename from googletest/Makefile.am
rename to third_party/googletest/googletest/Makefile.am
diff --git a/googletest/README.md b/third_party/googletest/googletest/README.md
similarity index 100%
rename from googletest/README.md
rename to third_party/googletest/googletest/README.md
diff --git a/googletest/build-aux/.keep b/third_party/googletest/googletest/build-aux/.keep
similarity index 100%
rename from googletest/build-aux/.keep
rename to third_party/googletest/googletest/build-aux/.keep
diff --git a/googletest/cmake/internal_utils.cmake b/third_party/googletest/googletest/cmake/internal_utils.cmake
similarity index 100%
rename from googletest/cmake/internal_utils.cmake
rename to third_party/googletest/googletest/cmake/internal_utils.cmake
diff --git a/googletest/codegear/gtest.cbproj b/third_party/googletest/googletest/codegear/gtest.cbproj
similarity index 100%
rename from googletest/codegear/gtest.cbproj
rename to third_party/googletest/googletest/codegear/gtest.cbproj
diff --git a/googletest/codegear/gtest.groupproj b/third_party/googletest/googletest/codegear/gtest.groupproj
similarity index 100%
rename from googletest/codegear/gtest.groupproj
rename to third_party/googletest/googletest/codegear/gtest.groupproj
diff --git a/googletest/codegear/gtest_all.cc b/third_party/googletest/googletest/codegear/gtest_all.cc
similarity index 100%
rename from googletest/codegear/gtest_all.cc
rename to third_party/googletest/googletest/codegear/gtest_all.cc
diff --git a/googletest/codegear/gtest_link.cc b/third_party/googletest/googletest/codegear/gtest_link.cc
similarity index 100%
rename from googletest/codegear/gtest_link.cc
rename to third_party/googletest/googletest/codegear/gtest_link.cc
diff --git a/googletest/codegear/gtest_main.cbproj b/third_party/googletest/googletest/codegear/gtest_main.cbproj
similarity index 100%
rename from googletest/codegear/gtest_main.cbproj
rename to third_party/googletest/googletest/codegear/gtest_main.cbproj
diff --git a/googletest/codegear/gtest_unittest.cbproj b/third_party/googletest/googletest/codegear/gtest_unittest.cbproj
similarity index 100%
rename from googletest/codegear/gtest_unittest.cbproj
rename to third_party/googletest/googletest/codegear/gtest_unittest.cbproj
diff --git a/googletest/configure.ac b/third_party/googletest/googletest/configure.ac
similarity index 100%
rename from googletest/configure.ac
rename to third_party/googletest/googletest/configure.ac
diff --git a/googletest/docs/AdvancedGuide.md b/third_party/googletest/googletest/docs/AdvancedGuide.md
similarity index 100%
rename from googletest/docs/AdvancedGuide.md
rename to third_party/googletest/googletest/docs/AdvancedGuide.md
diff --git a/googletest/docs/DevGuide.md b/third_party/googletest/googletest/docs/DevGuide.md
similarity index 100%
rename from googletest/docs/DevGuide.md
rename to third_party/googletest/googletest/docs/DevGuide.md
diff --git a/googletest/docs/Documentation.md b/third_party/googletest/googletest/docs/Documentation.md
similarity index 100%
rename from googletest/docs/Documentation.md
rename to third_party/googletest/googletest/docs/Documentation.md
diff --git a/googletest/docs/FAQ.md b/third_party/googletest/googletest/docs/FAQ.md
similarity index 100%
rename from googletest/docs/FAQ.md
rename to third_party/googletest/googletest/docs/FAQ.md
diff --git a/googletest/docs/Primer.md b/third_party/googletest/googletest/docs/Primer.md
similarity index 100%
rename from googletest/docs/Primer.md
rename to third_party/googletest/googletest/docs/Primer.md
diff --git a/googletest/docs/PumpManual.md b/third_party/googletest/googletest/docs/PumpManual.md
similarity index 100%
rename from googletest/docs/PumpManual.md
rename to third_party/googletest/googletest/docs/PumpManual.md
diff --git a/googletest/docs/Samples.md b/third_party/googletest/googletest/docs/Samples.md
similarity index 100%
rename from googletest/docs/Samples.md
rename to third_party/googletest/googletest/docs/Samples.md
diff --git a/googletest/docs/V1_5_AdvancedGuide.md b/third_party/googletest/googletest/docs/V1_5_AdvancedGuide.md
similarity index 100%
rename from googletest/docs/V1_5_AdvancedGuide.md
rename to third_party/googletest/googletest/docs/V1_5_AdvancedGuide.md
diff --git a/googletest/docs/V1_5_Documentation.md b/third_party/googletest/googletest/docs/V1_5_Documentation.md
similarity index 100%
rename from googletest/docs/V1_5_Documentation.md
rename to third_party/googletest/googletest/docs/V1_5_Documentation.md
diff --git a/googletest/docs/V1_5_FAQ.md b/third_party/googletest/googletest/docs/V1_5_FAQ.md
similarity index 100%
rename from googletest/docs/V1_5_FAQ.md
rename to third_party/googletest/googletest/docs/V1_5_FAQ.md
diff --git a/googletest/docs/V1_5_Primer.md b/third_party/googletest/googletest/docs/V1_5_Primer.md
similarity index 100%
rename from googletest/docs/V1_5_Primer.md
rename to third_party/googletest/googletest/docs/V1_5_Primer.md
diff --git a/googletest/docs/V1_5_PumpManual.md b/third_party/googletest/googletest/docs/V1_5_PumpManual.md
similarity index 100%
rename from googletest/docs/V1_5_PumpManual.md
rename to third_party/googletest/googletest/docs/V1_5_PumpManual.md
diff --git a/googletest/docs/V1_5_XcodeGuide.md b/third_party/googletest/googletest/docs/V1_5_XcodeGuide.md
similarity index 100%
rename from googletest/docs/V1_5_XcodeGuide.md
rename to third_party/googletest/googletest/docs/V1_5_XcodeGuide.md
diff --git a/googletest/docs/V1_6_AdvancedGuide.md b/third_party/googletest/googletest/docs/V1_6_AdvancedGuide.md
similarity index 100%
rename from googletest/docs/V1_6_AdvancedGuide.md
rename to third_party/googletest/googletest/docs/V1_6_AdvancedGuide.md
diff --git a/googletest/docs/V1_6_Documentation.md b/third_party/googletest/googletest/docs/V1_6_Documentation.md
similarity index 100%
rename from googletest/docs/V1_6_Documentation.md
rename to third_party/googletest/googletest/docs/V1_6_Documentation.md
diff --git a/googletest/docs/V1_6_FAQ.md b/third_party/googletest/googletest/docs/V1_6_FAQ.md
similarity index 100%
rename from googletest/docs/V1_6_FAQ.md
rename to third_party/googletest/googletest/docs/V1_6_FAQ.md
diff --git a/googletest/docs/V1_6_Primer.md b/third_party/googletest/googletest/docs/V1_6_Primer.md
similarity index 100%
rename from googletest/docs/V1_6_Primer.md
rename to third_party/googletest/googletest/docs/V1_6_Primer.md
diff --git a/googletest/docs/V1_6_PumpManual.md b/third_party/googletest/googletest/docs/V1_6_PumpManual.md
similarity index 100%
rename from googletest/docs/V1_6_PumpManual.md
rename to third_party/googletest/googletest/docs/V1_6_PumpManual.md
diff --git a/googletest/docs/V1_6_Samples.md b/third_party/googletest/googletest/docs/V1_6_Samples.md
similarity index 100%
rename from googletest/docs/V1_6_Samples.md
rename to third_party/googletest/googletest/docs/V1_6_Samples.md
diff --git a/googletest/docs/V1_6_XcodeGuide.md b/third_party/googletest/googletest/docs/V1_6_XcodeGuide.md
similarity index 100%
rename from googletest/docs/V1_6_XcodeGuide.md
rename to third_party/googletest/googletest/docs/V1_6_XcodeGuide.md
diff --git a/googletest/docs/V1_7_AdvancedGuide.md b/third_party/googletest/googletest/docs/V1_7_AdvancedGuide.md
similarity index 100%
rename from googletest/docs/V1_7_AdvancedGuide.md
rename to third_party/googletest/googletest/docs/V1_7_AdvancedGuide.md
diff --git a/googletest/docs/V1_7_Documentation.md b/third_party/googletest/googletest/docs/V1_7_Documentation.md
similarity index 100%
rename from googletest/docs/V1_7_Documentation.md
rename to third_party/googletest/googletest/docs/V1_7_Documentation.md
diff --git a/googletest/docs/V1_7_FAQ.md b/third_party/googletest/googletest/docs/V1_7_FAQ.md
similarity index 100%
rename from googletest/docs/V1_7_FAQ.md
rename to third_party/googletest/googletest/docs/V1_7_FAQ.md
diff --git a/googletest/docs/V1_7_Primer.md b/third_party/googletest/googletest/docs/V1_7_Primer.md
similarity index 100%
rename from googletest/docs/V1_7_Primer.md
rename to third_party/googletest/googletest/docs/V1_7_Primer.md
diff --git a/googletest/docs/V1_7_PumpManual.md b/third_party/googletest/googletest/docs/V1_7_PumpManual.md
similarity index 100%
rename from googletest/docs/V1_7_PumpManual.md
rename to third_party/googletest/googletest/docs/V1_7_PumpManual.md
diff --git a/googletest/docs/V1_7_Samples.md b/third_party/googletest/googletest/docs/V1_7_Samples.md
similarity index 100%
rename from googletest/docs/V1_7_Samples.md
rename to third_party/googletest/googletest/docs/V1_7_Samples.md
diff --git a/googletest/docs/V1_7_XcodeGuide.md b/third_party/googletest/googletest/docs/V1_7_XcodeGuide.md
similarity index 100%
rename from googletest/docs/V1_7_XcodeGuide.md
rename to third_party/googletest/googletest/docs/V1_7_XcodeGuide.md
diff --git a/googletest/docs/XcodeGuide.md b/third_party/googletest/googletest/docs/XcodeGuide.md
similarity index 100%
rename from googletest/docs/XcodeGuide.md
rename to third_party/googletest/googletest/docs/XcodeGuide.md
diff --git a/googletest/include/gtest/gtest-death-test.h b/third_party/googletest/googletest/include/gtest/gtest-death-test.h
similarity index 100%
rename from googletest/include/gtest/gtest-death-test.h
rename to third_party/googletest/googletest/include/gtest/gtest-death-test.h
diff --git a/googletest/include/gtest/gtest-message.h b/third_party/googletest/googletest/include/gtest/gtest-message.h
similarity index 100%
rename from googletest/include/gtest/gtest-message.h
rename to third_party/googletest/googletest/include/gtest/gtest-message.h
diff --git a/googletest/include/gtest/gtest-param-test.h b/third_party/googletest/googletest/include/gtest/gtest-param-test.h
similarity index 100%
rename from googletest/include/gtest/gtest-param-test.h
rename to third_party/googletest/googletest/include/gtest/gtest-param-test.h
diff --git a/googletest/include/gtest/gtest-param-test.h.pump b/third_party/googletest/googletest/include/gtest/gtest-param-test.h.pump
similarity index 100%
rename from googletest/include/gtest/gtest-param-test.h.pump
rename to third_party/googletest/googletest/include/gtest/gtest-param-test.h.pump
diff --git a/googletest/include/gtest/gtest-printers.h b/third_party/googletest/googletest/include/gtest/gtest-printers.h
similarity index 100%
rename from googletest/include/gtest/gtest-printers.h
rename to third_party/googletest/googletest/include/gtest/gtest-printers.h
diff --git a/googletest/include/gtest/gtest-spi.h b/third_party/googletest/googletest/include/gtest/gtest-spi.h
similarity index 100%
rename from googletest/include/gtest/gtest-spi.h
rename to third_party/googletest/googletest/include/gtest/gtest-spi.h
diff --git a/googletest/include/gtest/gtest-test-part.h b/third_party/googletest/googletest/include/gtest/gtest-test-part.h
similarity index 100%
rename from googletest/include/gtest/gtest-test-part.h
rename to third_party/googletest/googletest/include/gtest/gtest-test-part.h
diff --git a/googletest/include/gtest/gtest-typed-test.h b/third_party/googletest/googletest/include/gtest/gtest-typed-test.h
similarity index 100%
rename from googletest/include/gtest/gtest-typed-test.h
rename to third_party/googletest/googletest/include/gtest/gtest-typed-test.h
diff --git a/googletest/include/gtest/gtest.h b/third_party/googletest/googletest/include/gtest/gtest.h
similarity index 100%
rename from googletest/include/gtest/gtest.h
rename to third_party/googletest/googletest/include/gtest/gtest.h
diff --git a/googletest/include/gtest/gtest_pred_impl.h b/third_party/googletest/googletest/include/gtest/gtest_pred_impl.h
similarity index 100%
rename from googletest/include/gtest/gtest_pred_impl.h
rename to third_party/googletest/googletest/include/gtest/gtest_pred_impl.h
diff --git a/googletest/include/gtest/gtest_prod.h b/third_party/googletest/googletest/include/gtest/gtest_prod.h
similarity index 100%
rename from googletest/include/gtest/gtest_prod.h
rename to third_party/googletest/googletest/include/gtest/gtest_prod.h
diff --git a/googletest/include/gtest/internal/custom/gtest-port.h b/third_party/googletest/googletest/include/gtest/internal/custom/gtest-port.h
similarity index 100%
rename from googletest/include/gtest/internal/custom/gtest-port.h
rename to third_party/googletest/googletest/include/gtest/internal/custom/gtest-port.h
diff --git a/googletest/include/gtest/internal/custom/gtest-printers.h b/third_party/googletest/googletest/include/gtest/internal/custom/gtest-printers.h
similarity index 100%
rename from googletest/include/gtest/internal/custom/gtest-printers.h
rename to third_party/googletest/googletest/include/gtest/internal/custom/gtest-printers.h
diff --git a/googletest/include/gtest/internal/custom/gtest.h b/third_party/googletest/googletest/include/gtest/internal/custom/gtest.h
similarity index 100%
rename from googletest/include/gtest/internal/custom/gtest.h
rename to third_party/googletest/googletest/include/gtest/internal/custom/gtest.h
diff --git a/googletest/include/gtest/internal/gtest-death-test-internal.h b/third_party/googletest/googletest/include/gtest/internal/gtest-death-test-internal.h
similarity index 100%
rename from googletest/include/gtest/internal/gtest-death-test-internal.h
rename to third_party/googletest/googletest/include/gtest/internal/gtest-death-test-internal.h
diff --git a/googletest/include/gtest/internal/gtest-filepath.h b/third_party/googletest/googletest/include/gtest/internal/gtest-filepath.h
similarity index 100%
rename from googletest/include/gtest/internal/gtest-filepath.h
rename to third_party/googletest/googletest/include/gtest/internal/gtest-filepath.h
diff --git a/googletest/include/gtest/internal/gtest-internal.h b/third_party/googletest/googletest/include/gtest/internal/gtest-internal.h
similarity index 100%
rename from googletest/include/gtest/internal/gtest-internal.h
rename to third_party/googletest/googletest/include/gtest/internal/gtest-internal.h
diff --git a/googletest/include/gtest/internal/gtest-linked_ptr.h b/third_party/googletest/googletest/include/gtest/internal/gtest-linked_ptr.h
similarity index 100%
rename from googletest/include/gtest/internal/gtest-linked_ptr.h
rename to third_party/googletest/googletest/include/gtest/internal/gtest-linked_ptr.h
diff --git a/googletest/include/gtest/internal/gtest-param-util-generated.h b/third_party/googletest/googletest/include/gtest/internal/gtest-param-util-generated.h
similarity index 100%
rename from googletest/include/gtest/internal/gtest-param-util-generated.h
rename to third_party/googletest/googletest/include/gtest/internal/gtest-param-util-generated.h
diff --git a/googletest/include/gtest/internal/gtest-param-util-generated.h.pump b/third_party/googletest/googletest/include/gtest/internal/gtest-param-util-generated.h.pump
similarity index 100%
rename from googletest/include/gtest/internal/gtest-param-util-generated.h.pump
rename to third_party/googletest/googletest/include/gtest/internal/gtest-param-util-generated.h.pump
diff --git a/googletest/include/gtest/internal/gtest-param-util.h b/third_party/googletest/googletest/include/gtest/internal/gtest-param-util.h
similarity index 100%
rename from googletest/include/gtest/internal/gtest-param-util.h
rename to third_party/googletest/googletest/include/gtest/internal/gtest-param-util.h
diff --git a/googletest/include/gtest/internal/gtest-port-arch.h b/third_party/googletest/googletest/include/gtest/internal/gtest-port-arch.h
similarity index 100%
rename from googletest/include/gtest/internal/gtest-port-arch.h
rename to third_party/googletest/googletest/include/gtest/internal/gtest-port-arch.h
diff --git a/googletest/include/gtest/internal/gtest-port.h b/third_party/googletest/googletest/include/gtest/internal/gtest-port.h
similarity index 100%
rename from googletest/include/gtest/internal/gtest-port.h
rename to third_party/googletest/googletest/include/gtest/internal/gtest-port.h
diff --git a/googletest/include/gtest/internal/gtest-string.h b/third_party/googletest/googletest/include/gtest/internal/gtest-string.h
similarity index 100%
rename from googletest/include/gtest/internal/gtest-string.h
rename to third_party/googletest/googletest/include/gtest/internal/gtest-string.h
diff --git a/googletest/include/gtest/internal/gtest-tuple.h b/third_party/googletest/googletest/include/gtest/internal/gtest-tuple.h
similarity index 100%
rename from googletest/include/gtest/internal/gtest-tuple.h
rename to third_party/googletest/googletest/include/gtest/internal/gtest-tuple.h
diff --git a/googletest/include/gtest/internal/gtest-tuple.h.pump b/third_party/googletest/googletest/include/gtest/internal/gtest-tuple.h.pump
similarity index 100%
rename from googletest/include/gtest/internal/gtest-tuple.h.pump
rename to third_party/googletest/googletest/include/gtest/internal/gtest-tuple.h.pump
diff --git a/googletest/include/gtest/internal/gtest-type-util.h b/third_party/googletest/googletest/include/gtest/internal/gtest-type-util.h
similarity index 100%
rename from googletest/include/gtest/internal/gtest-type-util.h
rename to third_party/googletest/googletest/include/gtest/internal/gtest-type-util.h
diff --git a/googletest/include/gtest/internal/gtest-type-util.h.pump b/third_party/googletest/googletest/include/gtest/internal/gtest-type-util.h.pump
similarity index 100%
rename from googletest/include/gtest/internal/gtest-type-util.h.pump
rename to third_party/googletest/googletest/include/gtest/internal/gtest-type-util.h.pump
diff --git a/googletest/m4/acx_pthread.m4 b/third_party/googletest/googletest/m4/acx_pthread.m4
similarity index 100%
rename from googletest/m4/acx_pthread.m4
rename to third_party/googletest/googletest/m4/acx_pthread.m4
diff --git a/googletest/m4/gtest.m4 b/third_party/googletest/googletest/m4/gtest.m4
similarity index 100%
rename from googletest/m4/gtest.m4
rename to third_party/googletest/googletest/m4/gtest.m4
diff --git a/googletest/make/Makefile b/third_party/googletest/googletest/make/Makefile
similarity index 100%
rename from googletest/make/Makefile
rename to third_party/googletest/googletest/make/Makefile
diff --git a/googletest/msvc/gtest-md.sln b/third_party/googletest/googletest/msvc/gtest-md.sln
similarity index 100%
rename from googletest/msvc/gtest-md.sln
rename to third_party/googletest/googletest/msvc/gtest-md.sln
diff --git a/googletest/msvc/gtest-md.vcproj b/third_party/googletest/googletest/msvc/gtest-md.vcproj
similarity index 100%
rename from googletest/msvc/gtest-md.vcproj
rename to third_party/googletest/googletest/msvc/gtest-md.vcproj
diff --git a/googletest/msvc/gtest.sln b/third_party/googletest/googletest/msvc/gtest.sln
similarity index 100%
rename from googletest/msvc/gtest.sln
rename to third_party/googletest/googletest/msvc/gtest.sln
diff --git a/googletest/msvc/gtest.vcproj b/third_party/googletest/googletest/msvc/gtest.vcproj
similarity index 100%
rename from googletest/msvc/gtest.vcproj
rename to third_party/googletest/googletest/msvc/gtest.vcproj
diff --git a/googletest/msvc/gtest_main-md.vcproj b/third_party/googletest/googletest/msvc/gtest_main-md.vcproj
similarity index 100%
rename from googletest/msvc/gtest_main-md.vcproj
rename to third_party/googletest/googletest/msvc/gtest_main-md.vcproj
diff --git a/googletest/msvc/gtest_main.vcproj b/third_party/googletest/googletest/msvc/gtest_main.vcproj
similarity index 100%
rename from googletest/msvc/gtest_main.vcproj
rename to third_party/googletest/googletest/msvc/gtest_main.vcproj
diff --git a/googletest/msvc/gtest_prod_test-md.vcproj b/third_party/googletest/googletest/msvc/gtest_prod_test-md.vcproj
similarity index 100%
rename from googletest/msvc/gtest_prod_test-md.vcproj
rename to third_party/googletest/googletest/msvc/gtest_prod_test-md.vcproj
diff --git a/googletest/msvc/gtest_prod_test.vcproj b/third_party/googletest/googletest/msvc/gtest_prod_test.vcproj
similarity index 100%
rename from googletest/msvc/gtest_prod_test.vcproj
rename to third_party/googletest/googletest/msvc/gtest_prod_test.vcproj
diff --git a/googletest/msvc/gtest_unittest-md.vcproj b/third_party/googletest/googletest/msvc/gtest_unittest-md.vcproj
similarity index 100%
rename from googletest/msvc/gtest_unittest-md.vcproj
rename to third_party/googletest/googletest/msvc/gtest_unittest-md.vcproj
diff --git a/googletest/msvc/gtest_unittest.vcproj b/third_party/googletest/googletest/msvc/gtest_unittest.vcproj
similarity index 100%
rename from googletest/msvc/gtest_unittest.vcproj
rename to third_party/googletest/googletest/msvc/gtest_unittest.vcproj
diff --git a/googletest/samples/prime_tables.h b/third_party/googletest/googletest/samples/prime_tables.h
similarity index 100%
rename from googletest/samples/prime_tables.h
rename to third_party/googletest/googletest/samples/prime_tables.h
diff --git a/googletest/samples/sample1.cc b/third_party/googletest/googletest/samples/sample1.cc
similarity index 100%
rename from googletest/samples/sample1.cc
rename to third_party/googletest/googletest/samples/sample1.cc
diff --git a/googletest/samples/sample1.h b/third_party/googletest/googletest/samples/sample1.h
similarity index 100%
rename from googletest/samples/sample1.h
rename to third_party/googletest/googletest/samples/sample1.h
diff --git a/googletest/samples/sample10_unittest.cc b/third_party/googletest/googletest/samples/sample10_unittest.cc
similarity index 100%
rename from googletest/samples/sample10_unittest.cc
rename to third_party/googletest/googletest/samples/sample10_unittest.cc
diff --git a/googletest/samples/sample1_unittest.cc b/third_party/googletest/googletest/samples/sample1_unittest.cc
similarity index 100%
rename from googletest/samples/sample1_unittest.cc
rename to third_party/googletest/googletest/samples/sample1_unittest.cc
diff --git a/googletest/samples/sample2.cc b/third_party/googletest/googletest/samples/sample2.cc
similarity index 100%
rename from googletest/samples/sample2.cc
rename to third_party/googletest/googletest/samples/sample2.cc
diff --git a/googletest/samples/sample2.h b/third_party/googletest/googletest/samples/sample2.h
similarity index 100%
rename from googletest/samples/sample2.h
rename to third_party/googletest/googletest/samples/sample2.h
diff --git a/googletest/samples/sample2_unittest.cc b/third_party/googletest/googletest/samples/sample2_unittest.cc
similarity index 100%
rename from googletest/samples/sample2_unittest.cc
rename to third_party/googletest/googletest/samples/sample2_unittest.cc
diff --git a/googletest/samples/sample3-inl.h b/third_party/googletest/googletest/samples/sample3-inl.h
similarity index 100%
rename from googletest/samples/sample3-inl.h
rename to third_party/googletest/googletest/samples/sample3-inl.h
diff --git a/googletest/samples/sample3_unittest.cc b/third_party/googletest/googletest/samples/sample3_unittest.cc
similarity index 100%
rename from googletest/samples/sample3_unittest.cc
rename to third_party/googletest/googletest/samples/sample3_unittest.cc
diff --git a/googletest/samples/sample4.cc b/third_party/googletest/googletest/samples/sample4.cc
similarity index 100%
rename from googletest/samples/sample4.cc
rename to third_party/googletest/googletest/samples/sample4.cc
diff --git a/googletest/samples/sample4.h b/third_party/googletest/googletest/samples/sample4.h
similarity index 100%
rename from googletest/samples/sample4.h
rename to third_party/googletest/googletest/samples/sample4.h
diff --git a/googletest/samples/sample4_unittest.cc b/third_party/googletest/googletest/samples/sample4_unittest.cc
similarity index 100%
rename from googletest/samples/sample4_unittest.cc
rename to third_party/googletest/googletest/samples/sample4_unittest.cc
diff --git a/googletest/samples/sample5_unittest.cc b/third_party/googletest/googletest/samples/sample5_unittest.cc
similarity index 100%
rename from googletest/samples/sample5_unittest.cc
rename to third_party/googletest/googletest/samples/sample5_unittest.cc
diff --git a/googletest/samples/sample6_unittest.cc b/third_party/googletest/googletest/samples/sample6_unittest.cc
similarity index 100%
rename from googletest/samples/sample6_unittest.cc
rename to third_party/googletest/googletest/samples/sample6_unittest.cc
diff --git a/googletest/samples/sample7_unittest.cc b/third_party/googletest/googletest/samples/sample7_unittest.cc
similarity index 100%
rename from googletest/samples/sample7_unittest.cc
rename to third_party/googletest/googletest/samples/sample7_unittest.cc
diff --git a/googletest/samples/sample8_unittest.cc b/third_party/googletest/googletest/samples/sample8_unittest.cc
similarity index 100%
rename from googletest/samples/sample8_unittest.cc
rename to third_party/googletest/googletest/samples/sample8_unittest.cc
diff --git a/googletest/samples/sample9_unittest.cc b/third_party/googletest/googletest/samples/sample9_unittest.cc
similarity index 100%
rename from googletest/samples/sample9_unittest.cc
rename to third_party/googletest/googletest/samples/sample9_unittest.cc
diff --git a/googletest/scripts/common.py b/third_party/googletest/googletest/scripts/common.py
similarity index 100%
rename from googletest/scripts/common.py
rename to third_party/googletest/googletest/scripts/common.py
diff --git a/googletest/scripts/fuse_gtest_files.py b/third_party/googletest/googletest/scripts/fuse_gtest_files.py
similarity index 100%
rename from googletest/scripts/fuse_gtest_files.py
rename to third_party/googletest/googletest/scripts/fuse_gtest_files.py
diff --git a/googletest/scripts/gen_gtest_pred_impl.py b/third_party/googletest/googletest/scripts/gen_gtest_pred_impl.py
similarity index 100%
rename from googletest/scripts/gen_gtest_pred_impl.py
rename to third_party/googletest/googletest/scripts/gen_gtest_pred_impl.py
diff --git a/googletest/scripts/gtest-config.in b/third_party/googletest/googletest/scripts/gtest-config.in
similarity index 100%
rename from googletest/scripts/gtest-config.in
rename to third_party/googletest/googletest/scripts/gtest-config.in
diff --git a/googletest/scripts/pump.py b/third_party/googletest/googletest/scripts/pump.py
similarity index 100%
rename from googletest/scripts/pump.py
rename to third_party/googletest/googletest/scripts/pump.py
diff --git a/googletest/scripts/release_docs.py b/third_party/googletest/googletest/scripts/release_docs.py
similarity index 100%
rename from googletest/scripts/release_docs.py
rename to third_party/googletest/googletest/scripts/release_docs.py
diff --git a/googletest/scripts/test/Makefile b/third_party/googletest/googletest/scripts/test/Makefile
similarity index 100%
rename from googletest/scripts/test/Makefile
rename to third_party/googletest/googletest/scripts/test/Makefile
diff --git a/googletest/scripts/upload.py b/third_party/googletest/googletest/scripts/upload.py
similarity index 100%
rename from googletest/scripts/upload.py
rename to third_party/googletest/googletest/scripts/upload.py
diff --git a/googletest/scripts/upload_gtest.py b/third_party/googletest/googletest/scripts/upload_gtest.py
similarity index 100%
rename from googletest/scripts/upload_gtest.py
rename to third_party/googletest/googletest/scripts/upload_gtest.py
diff --git a/googletest/src/gtest-all.cc b/third_party/googletest/googletest/src/gtest-all.cc
similarity index 100%
rename from googletest/src/gtest-all.cc
rename to third_party/googletest/googletest/src/gtest-all.cc
diff --git a/googletest/src/gtest-death-test.cc b/third_party/googletest/googletest/src/gtest-death-test.cc
similarity index 100%
rename from googletest/src/gtest-death-test.cc
rename to third_party/googletest/googletest/src/gtest-death-test.cc
diff --git a/googletest/src/gtest-filepath.cc b/third_party/googletest/googletest/src/gtest-filepath.cc
similarity index 100%
rename from googletest/src/gtest-filepath.cc
rename to third_party/googletest/googletest/src/gtest-filepath.cc
diff --git a/googletest/src/gtest-internal-inl.h b/third_party/googletest/googletest/src/gtest-internal-inl.h
similarity index 100%
rename from googletest/src/gtest-internal-inl.h
rename to third_party/googletest/googletest/src/gtest-internal-inl.h
diff --git a/googletest/src/gtest-port.cc b/third_party/googletest/googletest/src/gtest-port.cc
similarity index 100%
rename from googletest/src/gtest-port.cc
rename to third_party/googletest/googletest/src/gtest-port.cc
diff --git a/googletest/src/gtest-printers.cc b/third_party/googletest/googletest/src/gtest-printers.cc
similarity index 100%
rename from googletest/src/gtest-printers.cc
rename to third_party/googletest/googletest/src/gtest-printers.cc
diff --git a/googletest/src/gtest-test-part.cc b/third_party/googletest/googletest/src/gtest-test-part.cc
similarity index 100%
rename from googletest/src/gtest-test-part.cc
rename to third_party/googletest/googletest/src/gtest-test-part.cc
diff --git a/googletest/src/gtest-typed-test.cc b/third_party/googletest/googletest/src/gtest-typed-test.cc
similarity index 100%
rename from googletest/src/gtest-typed-test.cc
rename to third_party/googletest/googletest/src/gtest-typed-test.cc
diff --git a/googletest/src/gtest.cc b/third_party/googletest/googletest/src/gtest.cc
similarity index 100%
rename from googletest/src/gtest.cc
rename to third_party/googletest/googletest/src/gtest.cc
diff --git a/googletest/src/gtest_main.cc b/third_party/googletest/googletest/src/gtest_main.cc
similarity index 100%
rename from googletest/src/gtest_main.cc
rename to third_party/googletest/googletest/src/gtest_main.cc
diff --git a/googletest/test/gtest-death-test_ex_test.cc b/third_party/googletest/googletest/test/gtest-death-test_ex_test.cc
similarity index 100%
rename from googletest/test/gtest-death-test_ex_test.cc
rename to third_party/googletest/googletest/test/gtest-death-test_ex_test.cc
diff --git a/googletest/test/gtest-death-test_test.cc b/third_party/googletest/googletest/test/gtest-death-test_test.cc
similarity index 100%
rename from googletest/test/gtest-death-test_test.cc
rename to third_party/googletest/googletest/test/gtest-death-test_test.cc
diff --git a/googletest/test/gtest-filepath_test.cc b/third_party/googletest/googletest/test/gtest-filepath_test.cc
similarity index 100%
rename from googletest/test/gtest-filepath_test.cc
rename to third_party/googletest/googletest/test/gtest-filepath_test.cc
diff --git a/googletest/test/gtest-linked_ptr_test.cc b/third_party/googletest/googletest/test/gtest-linked_ptr_test.cc
similarity index 100%
rename from googletest/test/gtest-linked_ptr_test.cc
rename to third_party/googletest/googletest/test/gtest-linked_ptr_test.cc
diff --git a/googletest/test/gtest-listener_test.cc b/third_party/googletest/googletest/test/gtest-listener_test.cc
similarity index 100%
rename from googletest/test/gtest-listener_test.cc
rename to third_party/googletest/googletest/test/gtest-listener_test.cc
diff --git a/googletest/test/gtest-message_test.cc b/third_party/googletest/googletest/test/gtest-message_test.cc
similarity index 100%
rename from googletest/test/gtest-message_test.cc
rename to third_party/googletest/googletest/test/gtest-message_test.cc
diff --git a/googletest/test/gtest-options_test.cc b/third_party/googletest/googletest/test/gtest-options_test.cc
similarity index 100%
rename from googletest/test/gtest-options_test.cc
rename to third_party/googletest/googletest/test/gtest-options_test.cc
diff --git a/googletest/test/gtest-param-test2_test.cc b/third_party/googletest/googletest/test/gtest-param-test2_test.cc
similarity index 100%
rename from googletest/test/gtest-param-test2_test.cc
rename to third_party/googletest/googletest/test/gtest-param-test2_test.cc
diff --git a/googletest/test/gtest-param-test_test.cc b/third_party/googletest/googletest/test/gtest-param-test_test.cc
similarity index 100%
rename from googletest/test/gtest-param-test_test.cc
rename to third_party/googletest/googletest/test/gtest-param-test_test.cc
diff --git a/googletest/test/gtest-param-test_test.h b/third_party/googletest/googletest/test/gtest-param-test_test.h
similarity index 100%
rename from googletest/test/gtest-param-test_test.h
rename to third_party/googletest/googletest/test/gtest-param-test_test.h
diff --git a/googletest/test/gtest-port_test.cc b/third_party/googletest/googletest/test/gtest-port_test.cc
similarity index 100%
rename from googletest/test/gtest-port_test.cc
rename to third_party/googletest/googletest/test/gtest-port_test.cc
diff --git a/googletest/test/gtest-printers_test.cc b/third_party/googletest/googletest/test/gtest-printers_test.cc
similarity index 100%
rename from googletest/test/gtest-printers_test.cc
rename to third_party/googletest/googletest/test/gtest-printers_test.cc
diff --git a/googletest/test/gtest-test-part_test.cc b/third_party/googletest/googletest/test/gtest-test-part_test.cc
similarity index 100%
rename from googletest/test/gtest-test-part_test.cc
rename to third_party/googletest/googletest/test/gtest-test-part_test.cc
diff --git a/googletest/test/gtest-tuple_test.cc b/third_party/googletest/googletest/test/gtest-tuple_test.cc
similarity index 100%
rename from googletest/test/gtest-tuple_test.cc
rename to third_party/googletest/googletest/test/gtest-tuple_test.cc
diff --git a/googletest/test/gtest-typed-test2_test.cc b/third_party/googletest/googletest/test/gtest-typed-test2_test.cc
similarity index 100%
rename from googletest/test/gtest-typed-test2_test.cc
rename to third_party/googletest/googletest/test/gtest-typed-test2_test.cc
diff --git a/googletest/test/gtest-typed-test_test.cc b/third_party/googletest/googletest/test/gtest-typed-test_test.cc
similarity index 100%
rename from googletest/test/gtest-typed-test_test.cc
rename to third_party/googletest/googletest/test/gtest-typed-test_test.cc
diff --git a/googletest/test/gtest-typed-test_test.h b/third_party/googletest/googletest/test/gtest-typed-test_test.h
similarity index 100%
rename from googletest/test/gtest-typed-test_test.h
rename to third_party/googletest/googletest/test/gtest-typed-test_test.h
diff --git a/googletest/test/gtest-unittest-api_test.cc b/third_party/googletest/googletest/test/gtest-unittest-api_test.cc
similarity index 100%
rename from googletest/test/gtest-unittest-api_test.cc
rename to third_party/googletest/googletest/test/gtest-unittest-api_test.cc
diff --git a/googletest/test/gtest_all_test.cc b/third_party/googletest/googletest/test/gtest_all_test.cc
similarity index 100%
rename from googletest/test/gtest_all_test.cc
rename to third_party/googletest/googletest/test/gtest_all_test.cc
diff --git a/googletest/test/gtest_break_on_failure_unittest.py b/third_party/googletest/googletest/test/gtest_break_on_failure_unittest.py
similarity index 100%
rename from googletest/test/gtest_break_on_failure_unittest.py
rename to third_party/googletest/googletest/test/gtest_break_on_failure_unittest.py
diff --git a/googletest/test/gtest_break_on_failure_unittest_.cc b/third_party/googletest/googletest/test/gtest_break_on_failure_unittest_.cc
similarity index 100%
rename from googletest/test/gtest_break_on_failure_unittest_.cc
rename to third_party/googletest/googletest/test/gtest_break_on_failure_unittest_.cc
diff --git a/googletest/test/gtest_catch_exceptions_test.py b/third_party/googletest/googletest/test/gtest_catch_exceptions_test.py
similarity index 100%
rename from googletest/test/gtest_catch_exceptions_test.py
rename to third_party/googletest/googletest/test/gtest_catch_exceptions_test.py
diff --git a/googletest/test/gtest_catch_exceptions_test_.cc b/third_party/googletest/googletest/test/gtest_catch_exceptions_test_.cc
similarity index 100%
rename from googletest/test/gtest_catch_exceptions_test_.cc
rename to third_party/googletest/googletest/test/gtest_catch_exceptions_test_.cc
diff --git a/googletest/test/gtest_color_test.py b/third_party/googletest/googletest/test/gtest_color_test.py
similarity index 100%
rename from googletest/test/gtest_color_test.py
rename to third_party/googletest/googletest/test/gtest_color_test.py
diff --git a/googletest/test/gtest_color_test_.cc b/third_party/googletest/googletest/test/gtest_color_test_.cc
similarity index 100%
rename from googletest/test/gtest_color_test_.cc
rename to third_party/googletest/googletest/test/gtest_color_test_.cc
diff --git a/googletest/test/gtest_env_var_test.py b/third_party/googletest/googletest/test/gtest_env_var_test.py
similarity index 100%
rename from googletest/test/gtest_env_var_test.py
rename to third_party/googletest/googletest/test/gtest_env_var_test.py
diff --git a/googletest/test/gtest_env_var_test_.cc b/third_party/googletest/googletest/test/gtest_env_var_test_.cc
similarity index 100%
rename from googletest/test/gtest_env_var_test_.cc
rename to third_party/googletest/googletest/test/gtest_env_var_test_.cc
diff --git a/googletest/test/gtest_environment_test.cc b/third_party/googletest/googletest/test/gtest_environment_test.cc
similarity index 100%
rename from googletest/test/gtest_environment_test.cc
rename to third_party/googletest/googletest/test/gtest_environment_test.cc
diff --git a/googletest/test/gtest_filter_unittest.py b/third_party/googletest/googletest/test/gtest_filter_unittest.py
similarity index 100%
rename from googletest/test/gtest_filter_unittest.py
rename to third_party/googletest/googletest/test/gtest_filter_unittest.py
diff --git a/googletest/test/gtest_filter_unittest_.cc b/third_party/googletest/googletest/test/gtest_filter_unittest_.cc
similarity index 100%
rename from googletest/test/gtest_filter_unittest_.cc
rename to third_party/googletest/googletest/test/gtest_filter_unittest_.cc
diff --git a/googletest/test/gtest_help_test.py b/third_party/googletest/googletest/test/gtest_help_test.py
similarity index 100%
rename from googletest/test/gtest_help_test.py
rename to third_party/googletest/googletest/test/gtest_help_test.py
diff --git a/googletest/test/gtest_help_test_.cc b/third_party/googletest/googletest/test/gtest_help_test_.cc
similarity index 100%
rename from googletest/test/gtest_help_test_.cc
rename to third_party/googletest/googletest/test/gtest_help_test_.cc
diff --git a/googletest/test/gtest_list_tests_unittest.py b/third_party/googletest/googletest/test/gtest_list_tests_unittest.py
similarity index 100%
rename from googletest/test/gtest_list_tests_unittest.py
rename to third_party/googletest/googletest/test/gtest_list_tests_unittest.py
diff --git a/googletest/test/gtest_list_tests_unittest_.cc b/third_party/googletest/googletest/test/gtest_list_tests_unittest_.cc
similarity index 100%
rename from googletest/test/gtest_list_tests_unittest_.cc
rename to third_party/googletest/googletest/test/gtest_list_tests_unittest_.cc
diff --git a/googletest/test/gtest_main_unittest.cc b/third_party/googletest/googletest/test/gtest_main_unittest.cc
similarity index 100%
rename from googletest/test/gtest_main_unittest.cc
rename to third_party/googletest/googletest/test/gtest_main_unittest.cc
diff --git a/googletest/test/gtest_no_test_unittest.cc b/third_party/googletest/googletest/test/gtest_no_test_unittest.cc
similarity index 100%
rename from googletest/test/gtest_no_test_unittest.cc
rename to third_party/googletest/googletest/test/gtest_no_test_unittest.cc
diff --git a/googletest/test/gtest_output_test.py b/third_party/googletest/googletest/test/gtest_output_test.py
similarity index 100%
rename from googletest/test/gtest_output_test.py
rename to third_party/googletest/googletest/test/gtest_output_test.py
diff --git a/googletest/test/gtest_output_test_.cc b/third_party/googletest/googletest/test/gtest_output_test_.cc
similarity index 100%
rename from googletest/test/gtest_output_test_.cc
rename to third_party/googletest/googletest/test/gtest_output_test_.cc
diff --git a/googletest/test/gtest_output_test_golden_lin.txt b/third_party/googletest/googletest/test/gtest_output_test_golden_lin.txt
similarity index 100%
rename from googletest/test/gtest_output_test_golden_lin.txt
rename to third_party/googletest/googletest/test/gtest_output_test_golden_lin.txt
diff --git a/googletest/test/gtest_pred_impl_unittest.cc b/third_party/googletest/googletest/test/gtest_pred_impl_unittest.cc
similarity index 100%
rename from googletest/test/gtest_pred_impl_unittest.cc
rename to third_party/googletest/googletest/test/gtest_pred_impl_unittest.cc
diff --git a/googletest/test/gtest_premature_exit_test.cc b/third_party/googletest/googletest/test/gtest_premature_exit_test.cc
similarity index 100%
rename from googletest/test/gtest_premature_exit_test.cc
rename to third_party/googletest/googletest/test/gtest_premature_exit_test.cc
diff --git a/googletest/test/gtest_prod_test.cc b/third_party/googletest/googletest/test/gtest_prod_test.cc
similarity index 100%
rename from googletest/test/gtest_prod_test.cc
rename to third_party/googletest/googletest/test/gtest_prod_test.cc
diff --git a/googletest/test/gtest_repeat_test.cc b/third_party/googletest/googletest/test/gtest_repeat_test.cc
similarity index 100%
rename from googletest/test/gtest_repeat_test.cc
rename to third_party/googletest/googletest/test/gtest_repeat_test.cc
diff --git a/googletest/test/gtest_shuffle_test.py b/third_party/googletest/googletest/test/gtest_shuffle_test.py
similarity index 100%
rename from googletest/test/gtest_shuffle_test.py
rename to third_party/googletest/googletest/test/gtest_shuffle_test.py
diff --git a/googletest/test/gtest_shuffle_test_.cc b/third_party/googletest/googletest/test/gtest_shuffle_test_.cc
similarity index 100%
rename from googletest/test/gtest_shuffle_test_.cc
rename to third_party/googletest/googletest/test/gtest_shuffle_test_.cc
diff --git a/googletest/test/gtest_sole_header_test.cc b/third_party/googletest/googletest/test/gtest_sole_header_test.cc
similarity index 100%
rename from googletest/test/gtest_sole_header_test.cc
rename to third_party/googletest/googletest/test/gtest_sole_header_test.cc
diff --git a/googletest/test/gtest_stress_test.cc b/third_party/googletest/googletest/test/gtest_stress_test.cc
similarity index 100%
rename from googletest/test/gtest_stress_test.cc
rename to third_party/googletest/googletest/test/gtest_stress_test.cc
diff --git a/googletest/test/gtest_test_utils.py b/third_party/googletest/googletest/test/gtest_test_utils.py
similarity index 100%
rename from googletest/test/gtest_test_utils.py
rename to third_party/googletest/googletest/test/gtest_test_utils.py
diff --git a/googletest/test/gtest_throw_on_failure_ex_test.cc b/third_party/googletest/googletest/test/gtest_throw_on_failure_ex_test.cc
similarity index 100%
rename from googletest/test/gtest_throw_on_failure_ex_test.cc
rename to third_party/googletest/googletest/test/gtest_throw_on_failure_ex_test.cc
diff --git a/googletest/test/gtest_throw_on_failure_test.py b/third_party/googletest/googletest/test/gtest_throw_on_failure_test.py
similarity index 100%
rename from googletest/test/gtest_throw_on_failure_test.py
rename to third_party/googletest/googletest/test/gtest_throw_on_failure_test.py
diff --git a/googletest/test/gtest_throw_on_failure_test_.cc b/third_party/googletest/googletest/test/gtest_throw_on_failure_test_.cc
similarity index 100%
rename from googletest/test/gtest_throw_on_failure_test_.cc
rename to third_party/googletest/googletest/test/gtest_throw_on_failure_test_.cc
diff --git a/googletest/test/gtest_uninitialized_test.py b/third_party/googletest/googletest/test/gtest_uninitialized_test.py
similarity index 100%
rename from googletest/test/gtest_uninitialized_test.py
rename to third_party/googletest/googletest/test/gtest_uninitialized_test.py
diff --git a/googletest/test/gtest_uninitialized_test_.cc b/third_party/googletest/googletest/test/gtest_uninitialized_test_.cc
similarity index 100%
rename from googletest/test/gtest_uninitialized_test_.cc
rename to third_party/googletest/googletest/test/gtest_uninitialized_test_.cc
diff --git a/googletest/test/gtest_unittest.cc b/third_party/googletest/googletest/test/gtest_unittest.cc
similarity index 100%
rename from googletest/test/gtest_unittest.cc
rename to third_party/googletest/googletest/test/gtest_unittest.cc
diff --git a/googletest/test/gtest_xml_outfile1_test_.cc b/third_party/googletest/googletest/test/gtest_xml_outfile1_test_.cc
similarity index 100%
rename from googletest/test/gtest_xml_outfile1_test_.cc
rename to third_party/googletest/googletest/test/gtest_xml_outfile1_test_.cc
diff --git a/googletest/test/gtest_xml_outfile2_test_.cc b/third_party/googletest/googletest/test/gtest_xml_outfile2_test_.cc
similarity index 100%
rename from googletest/test/gtest_xml_outfile2_test_.cc
rename to third_party/googletest/googletest/test/gtest_xml_outfile2_test_.cc
diff --git a/googletest/test/gtest_xml_outfiles_test.py b/third_party/googletest/googletest/test/gtest_xml_outfiles_test.py
similarity index 100%
rename from googletest/test/gtest_xml_outfiles_test.py
rename to third_party/googletest/googletest/test/gtest_xml_outfiles_test.py
diff --git a/googletest/test/gtest_xml_output_unittest.py b/third_party/googletest/googletest/test/gtest_xml_output_unittest.py
similarity index 100%
rename from googletest/test/gtest_xml_output_unittest.py
rename to third_party/googletest/googletest/test/gtest_xml_output_unittest.py
diff --git a/googletest/test/gtest_xml_output_unittest_.cc b/third_party/googletest/googletest/test/gtest_xml_output_unittest_.cc
similarity index 100%
rename from googletest/test/gtest_xml_output_unittest_.cc
rename to third_party/googletest/googletest/test/gtest_xml_output_unittest_.cc
diff --git a/googletest/test/gtest_xml_test_utils.py b/third_party/googletest/googletest/test/gtest_xml_test_utils.py
similarity index 100%
rename from googletest/test/gtest_xml_test_utils.py
rename to third_party/googletest/googletest/test/gtest_xml_test_utils.py
diff --git a/googletest/test/production.cc b/third_party/googletest/googletest/test/production.cc
similarity index 100%
rename from googletest/test/production.cc
rename to third_party/googletest/googletest/test/production.cc
diff --git a/googletest/test/production.h b/third_party/googletest/googletest/test/production.h
similarity index 100%
rename from googletest/test/production.h
rename to third_party/googletest/googletest/test/production.h
diff --git a/googletest/xcode/Config/DebugProject.xcconfig b/third_party/googletest/googletest/xcode/Config/DebugProject.xcconfig
similarity index 100%
rename from googletest/xcode/Config/DebugProject.xcconfig
rename to third_party/googletest/googletest/xcode/Config/DebugProject.xcconfig
diff --git a/googletest/xcode/Config/FrameworkTarget.xcconfig b/third_party/googletest/googletest/xcode/Config/FrameworkTarget.xcconfig
similarity index 100%
rename from googletest/xcode/Config/FrameworkTarget.xcconfig
rename to third_party/googletest/googletest/xcode/Config/FrameworkTarget.xcconfig
diff --git a/googletest/xcode/Config/General.xcconfig b/third_party/googletest/googletest/xcode/Config/General.xcconfig
similarity index 100%
rename from googletest/xcode/Config/General.xcconfig
rename to third_party/googletest/googletest/xcode/Config/General.xcconfig
diff --git a/googletest/xcode/Config/ReleaseProject.xcconfig b/third_party/googletest/googletest/xcode/Config/ReleaseProject.xcconfig
similarity index 100%
rename from googletest/xcode/Config/ReleaseProject.xcconfig
rename to third_party/googletest/googletest/xcode/Config/ReleaseProject.xcconfig
diff --git a/googletest/xcode/Config/StaticLibraryTarget.xcconfig b/third_party/googletest/googletest/xcode/Config/StaticLibraryTarget.xcconfig
similarity index 100%
rename from googletest/xcode/Config/StaticLibraryTarget.xcconfig
rename to third_party/googletest/googletest/xcode/Config/StaticLibraryTarget.xcconfig
diff --git a/googletest/xcode/Config/TestTarget.xcconfig b/third_party/googletest/googletest/xcode/Config/TestTarget.xcconfig
similarity index 100%
rename from googletest/xcode/Config/TestTarget.xcconfig
rename to third_party/googletest/googletest/xcode/Config/TestTarget.xcconfig
diff --git a/googletest/xcode/Resources/Info.plist b/third_party/googletest/googletest/xcode/Resources/Info.plist
similarity index 100%
rename from googletest/xcode/Resources/Info.plist
rename to third_party/googletest/googletest/xcode/Resources/Info.plist
diff --git a/googletest/xcode/Samples/FrameworkSample/Info.plist b/third_party/googletest/googletest/xcode/Samples/FrameworkSample/Info.plist
similarity index 100%
rename from googletest/xcode/Samples/FrameworkSample/Info.plist
rename to third_party/googletest/googletest/xcode/Samples/FrameworkSample/Info.plist
diff --git a/googletest/xcode/Samples/FrameworkSample/WidgetFramework.xcodeproj/project.pbxproj b/third_party/googletest/googletest/xcode/Samples/FrameworkSample/WidgetFramework.xcodeproj/project.pbxproj
similarity index 100%
rename from googletest/xcode/Samples/FrameworkSample/WidgetFramework.xcodeproj/project.pbxproj
rename to third_party/googletest/googletest/xcode/Samples/FrameworkSample/WidgetFramework.xcodeproj/project.pbxproj
diff --git a/googletest/xcode/Samples/FrameworkSample/runtests.sh b/third_party/googletest/googletest/xcode/Samples/FrameworkSample/runtests.sh
similarity index 100%
rename from googletest/xcode/Samples/FrameworkSample/runtests.sh
rename to third_party/googletest/googletest/xcode/Samples/FrameworkSample/runtests.sh
diff --git a/googletest/xcode/Samples/FrameworkSample/widget.cc b/third_party/googletest/googletest/xcode/Samples/FrameworkSample/widget.cc
similarity index 100%
rename from googletest/xcode/Samples/FrameworkSample/widget.cc
rename to third_party/googletest/googletest/xcode/Samples/FrameworkSample/widget.cc
diff --git a/googletest/xcode/Samples/FrameworkSample/widget.h b/third_party/googletest/googletest/xcode/Samples/FrameworkSample/widget.h
similarity index 100%
rename from googletest/xcode/Samples/FrameworkSample/widget.h
rename to third_party/googletest/googletest/xcode/Samples/FrameworkSample/widget.h
diff --git a/googletest/xcode/Samples/FrameworkSample/widget_test.cc b/third_party/googletest/googletest/xcode/Samples/FrameworkSample/widget_test.cc
similarity index 100%
rename from googletest/xcode/Samples/FrameworkSample/widget_test.cc
rename to third_party/googletest/googletest/xcode/Samples/FrameworkSample/widget_test.cc
diff --git a/googletest/xcode/Scripts/runtests.sh b/third_party/googletest/googletest/xcode/Scripts/runtests.sh
similarity index 100%
rename from googletest/xcode/Scripts/runtests.sh
rename to third_party/googletest/googletest/xcode/Scripts/runtests.sh
diff --git a/googletest/xcode/Scripts/versiongenerate.py b/third_party/googletest/googletest/xcode/Scripts/versiongenerate.py
similarity index 100%
rename from googletest/xcode/Scripts/versiongenerate.py
rename to third_party/googletest/googletest/xcode/Scripts/versiongenerate.py
diff --git a/googletest/xcode/gtest.xcodeproj/project.pbxproj b/third_party/googletest/googletest/xcode/gtest.xcodeproj/project.pbxproj
similarity index 100%
rename from googletest/xcode/gtest.xcodeproj/project.pbxproj
rename to third_party/googletest/googletest/xcode/gtest.xcodeproj/project.pbxproj
diff --git a/travis.sh b/third_party/googletest/travis.sh
similarity index 100%
rename from travis.sh
rename to third_party/googletest/travis.sh
diff --git a/vision/BinaryServer.cpp b/vision/BinaryServer.cpp
new file mode 100644
index 0000000..c21a8b3
--- /dev/null
+++ b/vision/BinaryServer.cpp
@@ -0,0 +1,129 @@
+#include "vision/BinaryServer.h"
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <errno.h>
+#include <string.h>
+#include <vector>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+
+#include "aos/externals/libjpeg/include/jpeglib.h"
+#include "aos/linux_code/camera/Buffers.h"
+#include "aos/common/time.h"
+
+namespace frc971 {
+namespace vision {
+
+static void echo_read_cb(struct bufferevent *bev, void * /*ctx*/) {
+ struct evbuffer *input = bufferevent_get_input(bev);
+ struct evbuffer *output = bufferevent_get_output(bev);
+
+ size_t len = evbuffer_get_length(input);
+ char *data;
+ data = (char *)malloc(len);
+ evbuffer_copyout(input, data, len);
+
+ printf("we got some data: %s\n", data);
+
+ evbuffer_add_buffer(output, input);
+}
+
+void BinaryServer::ErrorEvent(struct bufferevent *bev, short events) {
+ if (events & BEV_EVENT_ERROR) perror("Error from bufferevent");
+ if (events & (BEV_EVENT_EOF | BEV_EVENT_ERROR)) {
+ have_id = false;
+ bufferevent_free(bev);
+ }
+}
+
+void BinaryServer::Accept(struct evconnlistener *listener, evutil_socket_t fd,
+ struct sockaddr * /*address*/, int /*socklen*/) {
+ struct event_base *base = evconnlistener_get_base(listener);
+ if (!have_id) {
+ struct bufferevent *bev =
+ bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
+ _output = bufferevent_get_output(bev);
+ _bufev = bev;
+ have_id = true;
+ int no_delay_flag = 1;
+ setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &no_delay_flag,
+ sizeof(no_delay_flag));
+
+ bufferevent_setcb(bev, echo_read_cb, NULL, StaticErrorEvent, this);
+
+ bufferevent_enable(bev, EV_READ | EV_WRITE);
+ }
+}
+static void accept_error_cb(struct evconnlistener *listener, void * /*ctx*/) {
+ struct event_base *base = evconnlistener_get_base(listener);
+ int err = EVUTIL_SOCKET_ERROR();
+ fprintf(stderr, "Got an error %d (%s) on the listener. "
+ "Shutting down.\n",
+ err, evutil_socket_error_to_string(err));
+
+ event_base_loopexit(base, NULL);
+}
+
+void BinaryServer::StartServer(uint16_t port) {
+ _fd = socket(AF_INET, SOCK_STREAM, 0);
+ struct sockaddr_in sin;
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(port);
+ sin.sin_addr.s_addr = inet_addr("0.0.0.0");
+
+ listener = evconnlistener_new_bind(
+ _base, StaticAccept, this, LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, -1,
+ (struct sockaddr *)(void *)&sin, sizeof(sin));
+
+ if (!listener) {
+ fprintf(stderr, "%s:%d: Couldn't create listener\n", __FILE__, __LINE__);
+ exit(-1);
+ }
+
+ evconnlistener_set_error_cb(listener, accept_error_cb);
+}
+
+void BinaryServer::Notify(int fd, short /*what*/) {
+ char notes[4096];
+ int count = read(fd, notes, 4096);
+ if (count == 0) {
+ close(fd);
+ fprintf(stderr, "%s:%d: Error No cheeze from OpenCV task!!!\n", __FILE__,
+ __LINE__);
+ exit(-1);
+ }
+ printf("notified!: %d\n", count);
+ if (have_id) {
+ printf("got someone to read my stuff!\n");
+ char *binary_data;
+ size_t len;
+ if (_notify->GetData(&binary_data, &len)) {
+ printf("here is for sending\n");
+ evbuffer_add_reference(_output, binary_data, len,
+ PacketNotifier::StaticDataSent, _notify);
+ printf("this is how sending went %d\n",
+ bufferevent_flush(_bufev, EV_WRITE, BEV_FLUSH));
+ }
+ }
+}
+
+// Constructor
+BinaryServer::BinaryServer(uint16_t port,
+ frc971::vision::PacketNotifier *notify)
+ : _base(event_base_new()) {
+ have_id = false;
+ StartServer(port);
+ _notify = notify;
+ frame_notify = event_new(_base, notify->RecieverFD(), EV_READ | EV_PERSIST,
+ StaticNotify, this);
+ event_add(frame_notify, NULL);
+ event_base_dispatch(_base);
+}
+
+} // namespace vision
+} // namespace frc971
diff --git a/vision/BinaryServer.h b/vision/BinaryServer.h
new file mode 100644
index 0000000..f33ee21
--- /dev/null
+++ b/vision/BinaryServer.h
@@ -0,0 +1,57 @@
+#ifndef FRC971_VISION_BINARY_SERVER_H_
+#define FRC971_VISION_BINARY_SERVER_H_
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include "event2/buffer.h"
+#include "event2/event.h"
+#include "event2/listener.h"
+#include "event2/bufferevent.h"
+
+#include "aos/common/mutex.h"
+
+#include "vision/PacketNotifier.h"
+
+namespace frc971 {
+namespace vision {
+
+/* This runs the libevent loop and interfaces in with the sockets provided from the PacketNotifier
+ * to allow a secondary process to focus primarily on doing processing and then feeding this task.
+ */
+class BinaryServer {
+ public:
+ BinaryServer(uint16_t port,PacketNotifier *notify);
+
+
+ void StartServer(uint16_t port);
+ private:
+ event_base *const _base;
+ int _fd;
+ PacketNotifier *_notify;
+ bool have_id;
+ int _client_fd;
+ struct event *frame_notify;
+ struct evbuffer *_output;
+ struct bufferevent *_bufev;
+ struct evconnlistener *listener;
+ void Accept(evconnlistener *listener, evutil_socket_t fd,
+ struct sockaddr* /*address*/, int /*socklen*/);
+ void Notify(int /*fd*/, short /*what*/);
+ void ErrorEvent(struct bufferevent *bev,short events);
+ static void StaticAccept(evconnlistener *listener, evutil_socket_t fd,
+ struct sockaddr *address, int socklen,void *self){
+ ((BinaryServer *)(self))->Accept(listener, fd, address, socklen);
+ }
+ static void StaticNotify(int fd, short what, void *self){
+ ((BinaryServer *)(self))->Notify(fd, what);
+ }
+ static void StaticErrorEvent(struct bufferevent *bev,short events,void *self){
+ ((BinaryServer *)(self))->ErrorEvent(bev,events);
+ }
+};
+
+} // namespace vision
+} // namespace frc971
+
+#endif // FRC971_VISION_BINARY_SERVER_H_
diff --git a/vision/CameraProcessor.cpp b/vision/CameraProcessor.cpp
new file mode 100644
index 0000000..856e12b
--- /dev/null
+++ b/vision/CameraProcessor.cpp
@@ -0,0 +1,466 @@
+#include "vision/CameraProcessor.h"
+#include "aos/common/logging/logging.h"
+
+//#define LOCAL_DEBUG 1
+
+// create a new target
+Target::Target(std::vector<cv::Point> new_contour,
+ std::vector<cv::Point> new_raw_contour,
+ FullRect new_rect, int new_weight, bool _is_90) {
+ this_contour = new_contour;
+ raw_contour = new_raw_contour;
+ rect = new_rect;
+ weight = new_weight;
+ height = getHeight(_is_90);
+}
+
+// calculate distance to target
+double Target::getHeight(bool is_90) {
+ // The 780.3296 is at full resolution 640x480, and we need
+ // to scale back to 320x240
+ //static const double cam_l = 780.3296 / 2.0;
+ ////static const double cam_d = 20.78096;
+ double height;
+ if (is_90) {
+ height = ((rect.ul.x - rect.ur.x) +
+ (rect.bl.x - rect.br.x)) / 2.0;
+ } else {
+ height = ((rect.ur.y + rect.ul.y) -
+ (rect.br.y + rect.bl.y)) / 2.0;
+ }
+ //return cam_l * 12.0 / height;
+ return height;
+}
+
+void Target::refineTarget() {
+ printf("okay refined\n");
+}
+
+FullRect::FullRect() {
+ ur.x = -1;
+ ur.y = -1;
+ ul.x = -1;
+ ul.y = -1;
+ br.x = -1;
+ br.y = -1;
+ bl.x = -1;
+ bl.y = -1;
+}
+
+// turns a contour into easier to access structure
+FullRect calcFullRect(std::vector<cv::Point> *contour){
+ FullRect rect;
+ for(int i=0; i<4; i++){
+ cv::Point2f pt = (*contour)[i];
+ rect.centroid.x += pt.x;
+ rect.centroid.y += pt.y;
+ }
+ rect.centroid.x /= 4;
+ rect.centroid.y /= 4;
+ for(int i=0; i<4; i++){
+ cv::Point2f pt = (*contour)[i];
+ if(pt.y > rect.centroid.y ){
+ if(pt.x > rect.centroid.x){
+ if (rect.ul.x < 0) {
+ rect.ul = pt;
+ } else {
+ rect.ur = pt;
+ }
+ }else{
+ if (rect.ur.x < 0) {
+ rect.ur = pt;
+ } else {
+ rect.ul = pt;
+ }
+ }
+ if (rect.ul.x > 0 && rect.ur.x > 0) {
+ // both are set, so if we got it wrong correct it here
+ if (rect.ul.x > rect.ur.x) {
+ pt = rect.ur;
+ rect.ur = rect.ul;
+ rect.ul = pt;
+ }
+ }
+ }else{
+ if(pt.x > rect.centroid.x){
+ if (rect.bl.x < 0) {
+ rect.bl = pt;
+ } else {
+ rect.br = pt;
+ }
+ }else{
+ if (rect.br.x < 0) {
+ rect.br = pt;
+ } else {
+ rect.bl = pt;
+ }
+ }
+ if (rect.bl.x > 0 && rect.br.x > 0) {
+ // both are set, so if we got it wrong correct it here
+ if (rect.bl.x > rect.br.x) {
+ pt = rect.br;
+ rect.br = rect.bl;
+ rect.bl = pt;
+ }
+ }
+ }
+ }
+ return rect;
+}
+
+// quickly remove targets that do not fit a very broad set of constraints
+bool cullObvious(FullRect rect, double outside_size){
+ // first check that could see a target this size
+ // Calculated from dave's simulation, shloud be 850 and 72000 if filled
+ if((outside_size < 500) || (outside_size > 90000)){
+ return false;
+ }
+ // Targets on the edge are at best inaccurate.
+ // In this case, we just want to point the right way,
+ // so this is no longer a valid assumption.
+ /*if( rect.ur.x < 2 || rect.ur.y < 2 || rect.ur.x > 637 || rect.ur.y > 477 ||
+ rect.ul.x < 2 || rect.ul.y < 2 || rect.ul.x > 637 || rect.ul.y > 477 ||
+ rect.br.x < 2 || rect.br.y < 2 || rect.br.x > 637 || rect.br.y > 477 ||
+ rect.bl.x < 2 || rect.bl.y < 2 || rect.bl.x > 637 || rect.bl.y > 477){
+ return false;
+ }*/
+ // make sure the sides are close to the right ratio of a rect
+ // some really odd shapes get confusing
+ double ratio = norm(rect.ur-rect.ul)/norm(rect.br-rect.bl);
+ if( ratio < .7 || ratio > 1.4 ) {
+ return false;
+ }
+ ratio = norm(rect.ur-rect.br)/norm(rect.ul-rect.bl);
+ if( ratio < .7 || ratio > 1.4 ) {
+ return false;
+ }
+
+ return true;
+}
+
+// sum over values between these two points and normalize
+// see Bresenham's Line Algorithm for the logic of moving
+// over all the pixels between these two points.
+double ProcessorData::calcHistComponent(
+ cv::Point2i start,
+ cv::Point2i end,
+ cv::Mat thresh_img){
+ int dx = abs(end.x - start.x);
+ int dy = abs(end.y - start.y);
+ int sx = (start.x < end.x) ? 1 : -1;
+ int sy = (start.y < end.y) ? 1 : -1;
+ int error = dx-dy;
+
+ int total = 0;
+ int value = 0;
+ int total_error;
+#if LOCAL_DEBUG
+ IplImage gd = *global_display;
+#endif
+ IplImage ti = thresh_img;
+ while(1){
+ total++;
+
+ uchar* ptr = (uchar*) (ti.imageData + start.y * ti.widthStep + start.x);
+ if((int) *ptr) value++;
+ // draw line checked
+#if LOCAL_DEBUG
+ uchar* ptr2 = (uchar*) (gd.imageData + start.y * gd.widthStep + start.x*3);
+ *ptr2++ = 0;
+ *ptr2++ = 255;
+ *ptr2 = 0;
+#endif
+ if(start.x == end.x && start.y == end.y) break;
+ total_error = 2 * error;
+ if(total_error > -dy){
+ error -= dy;
+ start.x += sx;
+ }
+ if(total_error < dx){
+ error += dx;
+ start.y += sy;
+ }
+ }
+ return (double)value/(double)total;
+}
+
+// just a distance function
+double chiSquared(int length, double* histA, double* histB){
+ double sum = 0;
+ for(int i=0; i<length;i++){
+ double diff = *(histB+i) - *(histA+i);
+ sum += (diff * diff) / *(histA+i);
+ }
+ return sum;
+}
+// euclidiean dist function
+double L2_dist(int length, double* histA, double* histB){
+ double sum = 0;
+ for(int i=0; i<length;i++){
+ double diff = *(histB+i) - *(histA+i);
+ sum += (diff * diff);
+ }
+ return sqrt(sum);
+}
+
+// calc and compare the histograms to the desired
+double ProcessorData::checkHistogram(FullRect rect, cv::Mat thresh_img){
+ // found horiz histogram
+ double hist_lr[HIST_SIZE];
+ // step size along left edge
+ cv::Point2f delta_left = (rect.ul - rect.bl)*HIST_SIZE_F;
+ // step size along right edge
+ cv::Point2f delta_right = (rect.ur - rect.br)*HIST_SIZE_F;
+ // sum each left to right line for the histogram
+ for(int i=0; i<HIST_SIZE; i++){
+ hist_lr[i] = calcHistComponent(rect.bl+i*delta_left,
+ rect.br+i*delta_right,thresh_img);
+ }
+ double check_vert = L2_dist(HIST_SIZE, vert_hist, hist_lr);
+ // found vert histogram
+ double hist_ub[HIST_SIZE];
+ // step size along bottom edge
+ cv::Point2f delta_bottom = (rect.bl - rect.br)*HIST_SIZE_F;
+ // step size along top edge
+ cv::Point2f delta_top = (rect.ul - rect.ur)*HIST_SIZE_F;
+ // sum each top to bottom line for the histogram
+ for(int i=0; i<HIST_SIZE; i++){
+ hist_ub[i] = calcHistComponent(rect.ur+i*delta_top,
+ rect.br+i*delta_bottom,thresh_img);
+ }
+ double check_horz = L2_dist(HIST_SIZE, horz_hist, hist_ub);
+
+ // average the two distances
+ double check = (check_vert + check_horz)/2.0;
+ return check;
+}
+
+// return smallest
+template<class T> inline T Min3(T x, T y, T z) {
+ return y <= z ? (x <= y ? x : y)
+ : (x <= z ? x : z);
+}
+
+// return largest
+template<class T> inline T Max3(T x, T y, T z) {
+ return y >= z ? (x >= y ? x : y)
+ : (x >= z ? x : z);
+}
+
+// transforms the contour
+void makeConvex(std::vector<cv::Point> *contour){
+ std::vector<cv::Point2i> hull;
+ convexHull(*contour, hull, false);
+ *contour = hull;
+}
+
+// basic init
+ProcessorData::ProcessorData(int width, int height, bool is_90_) {
+ is_90 = is_90_;
+ // series b images from nasa
+ h1=79; s1=53; v1=82;
+ h2=200; s2=255; v2=255;
+ // For images from Jerry
+ //h1=79; s1=0; v1=11;
+ //h2=160; s2=255; v2=255;
+ img_width = width;
+ img_height = height;
+ buffer_size = img_height * img_width * 3;
+#if LOCAL_DEBUG
+ global_display = cvCreateImage(cvSize(width, height),
+ IPL_DEPTH_8U, 3);
+#endif
+ grey_image = cvCreateImage(cvSize(width, height),
+ IPL_DEPTH_8U, 1);
+ grey_mat = new cv::Mat(grey_image);
+
+ // calculate a desired histogram before we start
+ int j = 0;
+ for(double i=0; j<HIST_SIZE; i+=HIST_SIZE_F){
+ if (is_90) {
+ if(i < 2.0/12.0 || i > (1.0-2.0/12.0) ) horz_hist[j] = 1;
+ else horz_hist[j] = 0.10;
+ if(i < 2.0/24.0 || i > (1.0-2.0/24.0) ) vert_hist[j] = 1;
+ else vert_hist[j] = 4.0/24.0;
+ } else {
+ if(i < 2.0/12.0 || i > (1.0-2.0/12.0) ) vert_hist[j] = 1;
+ else vert_hist[j] = 0.10;
+ if(i < 2.0/24.0 || i > (1.0-2.0/24.0) ) horz_hist[j] = 1;
+ else horz_hist[j] = 4.0/24.0;
+ }
+ j++;
+ }
+
+ src_header_image = cvCreateImage(cvSize(width, height),
+ IPL_DEPTH_8U, 3);
+}
+
+// throw stuff away
+ProcessorData::~ProcessorData() {
+ cvReleaseImage(&grey_image);
+ cvReleaseImage(&src_header_image);
+ delete(grey_mat);
+}
+
+// reset processor data freeing as little as possible.
+void ProcessorData::clear() {
+ target_list.clear();
+ contour_pairs.clear();
+ hierarchy.clear();
+}
+
+
+// r,g,b values are from 0 to 255
+// h = [0,255], s = [0,255], v = [0,255]
+// if s == 0, then h = 0 (undefined)
+void ProcessorData::RGBtoHSV(uchar r, uchar g, uchar b,
+ uchar *h, uchar *s, uchar *v ) {
+ uchar min, max, delta;
+ min = Min3( r, g, b );
+ max = Max3( r, g, b );
+ *v = max;
+ delta = max - min;
+ if (max == 0 || delta == 0) {
+ *s = 0;
+ *h = 0;
+ return;
+ }
+ *s = (255 * long(delta))/max;
+ if (max == r) {
+ *h = 0 + 43*(g - b)/(delta);
+ } else if (max == g) {
+ *h = 85 + 43*(b - r)/(delta);
+ } else {
+ *h = 171 + 43*(r - g)/(delta);
+ }
+}
+
+// keep anything that is in our HVS wedge
+// by far the longest running function in this code
+void ProcessorData::threshold(uchar* buffer) {
+#if LOCAL_DEBUG
+ uchar * draw_buffer = (uchar *) global_display->imageData;
+#endif
+ for (int i = 0, j = 0; i < (buffer_size); i+= 3, j++) {
+ uchar r = buffer[i + 2];
+ uchar g = buffer[i + 1];
+ uchar b = buffer[i + 0];
+ uchar h, s, v;
+
+ RGBtoHSV(r, g, b, &h, &s, &v);
+
+ if (g > 128) {
+#if LOCAL_DEBUG
+ draw_buffer[i + 0] = 120;
+ draw_buffer[i + 1] = 80;
+ draw_buffer[i + 2] = 70;
+#endif
+ grey_image->imageData[j] = 255;
+ } else if (h == 0 && s == 0 && v >= v1 && v <= v2) {
+ // Value thresholds invalid pixels.
+#if LOCAL_DEBUG
+ draw_buffer[i + 0] = 200;
+ draw_buffer[i + 1] = 50;
+ draw_buffer[i + 2] = 100;
+#endif
+ grey_image->imageData[j] = 255;
+ } else if (h >= h1 && h <= h2 && v >= v1 &&
+ v <= v2 && s >= s1 && s <= s2){
+ // HSV Thresholded image.
+#if LOCAL_DEBUG
+ draw_buffer[i + 0] = 255;
+ draw_buffer[i + 1] = 0;
+ draw_buffer[i + 2] = 0;
+#endif
+ grey_image->imageData[j] = 255;
+ } else {
+ // Display the unmodified image.
+#if LOCAL_DEBUG
+ draw_buffer[i + 0] = buffer[i + 0];
+ draw_buffer[i + 1] = buffer[i + 1];
+ draw_buffer[i + 2] = buffer[i + 2];
+#endif
+ grey_image->imageData[j] = 0;
+ }
+
+ }
+
+}
+
+// run find contours and try to make them squares
+void ProcessorData::getContours() {
+ std::vector<std::vector<cv::Point> > raw_contours;
+ //cv::findContours(*grey_mat, raw_contours, hierarchy, CV_RETR_LIST,
+ // CV_CHAIN_APPROX_SIMPLE);
+ cv::findContours(*grey_mat, raw_contours, hierarchy, CV_RETR_EXTERNAL,
+ CV_CHAIN_APPROX_SIMPLE);
+#if LOCAL_DEBUG
+ cv::Mat global_mat(global_display);
+ cv::Scalar color(255,0,0);
+ drawContours(global_mat,raw_contours,-1,color,1);
+
+ std::vector<std::vector<cv::Point> > p_contours;
+#endif
+ if (!raw_contours.empty()) {
+ std::vector<std::vector<cv::Point2i> >::iterator contour_it;
+ for (contour_it=raw_contours.begin();
+ contour_it != raw_contours.end();
+ contour_it++) {
+ // make the contours convex
+ makeConvex(&(*contour_it));
+ std::vector<cv::Point> contour;
+ //then make them rectangle
+ approxPolyDP(*contour_it, contour, 9, true);
+ // stick the raw and processed contours together
+ std::pair<std::vector<cv::Point>,
+ std::vector<cv::Point> > a_pair(
+ *contour_it, contour);
+#if LOCAL_DEBUG
+ p_contours.push_back(contour);
+#endif
+ // and put them in a list
+ contour_pairs.push_back(a_pair);
+ }
+ }
+#if LOCAL_DEBUG
+ cv::Scalar color2(0,0,255);
+ drawContours(global_mat,p_contours,-1,color2,CV_FILLED);
+#endif
+}
+
+// filter the contours down to a list of targets
+void ProcessorData::filterToTargets() {
+ std::vector<std::pair<
+ std::vector<cv::Point>,
+ std::vector<cv::Point> > >::iterator contour_it;
+ for(contour_it=contour_pairs.begin();
+ contour_it != contour_pairs.end();
+ contour_it++){
+ double check = 0;
+ std::vector<cv::Point> raw_contour = std::get<0>(*contour_it);
+ std::vector<cv::Point> contour = std::get<1>(*contour_it);
+ FullRect rect = calcFullRect(&contour);
+ if(contour.size() == 4 &&
+ cullObvious(rect, contourArea(contour)) &&
+ (check = checkHistogram(rect, *grey_mat)) <= HIST_MATCH){
+ // now we have a target, try to improve the square
+#if LOCAL_DEBUG
+ /* printf("________\n");
+ printf("\tcont= %d raw= %d\n",
+ (int)contour.size(), (int)raw_contour.size());
+ std::vector<cv::Point2i>::iterator point_it;
+ for(point_it=raw_contour.begin();
+ point_it != raw_contour.end(); point_it++){
+ printf("(%d,%d)", point_it->x, point_it->y);
+ }
+ printf("\n");*/
+#endif
+ target_list.push_back(Target(contour,
+ raw_contour, rect, check, is_90));
+ }
+ if (contour.size() == 4 && cullObvious(rect, contourArea(contour))) {
+ LOG(DEBUG, "check= %.2f\n", check);
+ }
+ }
+}
+
diff --git a/vision/CameraProcessor.h b/vision/CameraProcessor.h
new file mode 100644
index 0000000..096defb
--- /dev/null
+++ b/vision/CameraProcessor.h
@@ -0,0 +1,88 @@
+#ifndef VISION_CAMERA_PROCESSOR_H_
+#define VISION_CAMERA_PROCESSOR_H_
+
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <utility>
+#include <vector>
+
+#include "opencv2/imgproc/imgproc.hpp"
+
+// an over described geometric representation of a rectangle
+class FullRect {
+ public:
+ FullRect();
+ cv::Point2f ur; // upper right
+ cv::Point2f ul; // upper left
+ cv::Point2f br; // bottom right
+ cv::Point2f bl; // bottom_left
+ cv::Point2f centroid; //centroid
+};
+
+// All data needed once a target is found
+class Target {
+ public:
+ Target(std::vector<cv::Point> new_contour,
+ std::vector<cv::Point> new_raw_contour,
+ FullRect new_rect, int new_weight, bool _is_90);
+ void refineTarget();
+ double getHeight(bool is_90);
+ FullRect rect; // geometric representation of the target
+ std::vector<cv::Point> this_contour; // opencv contour of target
+ std::vector<cv::Point> raw_contour; // opencv contour of target
+ double height; // top target to robot
+ double weight; // confidence in this target
+};
+
+// main class for processing image data. All relavent data should be
+// accessible through this structure.
+class ProcessorData {
+ public:
+ ProcessorData(int width, int height, bool is_90_);
+ ~ProcessorData();
+ void RGBtoHSV(uchar r, uchar g, uchar b,
+ uchar *h, uchar *s, uchar *v);
+ void threshold(uchar* buffer);
+ void getContours();
+ void filterToTargets();
+ void clear();
+ //protected:
+ int img_width; // all images should be this width
+ int img_height; // and this height
+ bool is_90;
+ int buffer_size; // width * height * 3
+ IplImage *grey_image; // thresholded image
+ cv::Mat *grey_mat; // Matrix representaion (just a header)
+ std::vector<std::pair<std::vector<cv::Point>,
+ std::vector<cv::Point> > > contour_pairs;
+ //std::vector<std::vector<cv::Point> > contours; // filtered contours
+ //yystd::vector<std::vector<cv::Point> > raw_contours; //original contours
+ std::vector<cv::Vec4i> hierarchy; // ordering on contours
+ cv::MemStorage g_storage; // opencv storage
+ static const int HIST_SIZE = 20; // dimension of histogram
+ // ie number of scan lines
+ static constexpr double HIST_SIZE_F = 1.0/20.0; // step size
+ // should be 1/HIST_SIZE
+ double vert_hist[HIST_SIZE]; // desired vertical histogram
+ double horz_hist[HIST_SIZE]; // desired horizontal histogram
+ // defines the minimum dist for a match
+ static constexpr double HIST_MATCH = 1.9;
+ double calcHistComponent(
+ cv::Point2i start,
+ cv::Point2i end,
+ cv::Mat thresh_img);
+ double checkHistogram(
+ FullRect rect,
+ cv::Mat thresh_img);
+ public:
+ int h1, s1, v1, h2, s2, v2; // HSV min and max
+ // must be public for tuning
+ IplImage * global_display;
+
+ IplImage *src_header_image; // header for main image
+ std::vector<Target> target_list; // list of found targets
+};
+
+#endif // VISION_CAMERA_PROCESSOR_H_
diff --git a/vision/GoalMaster.cpp b/vision/GoalMaster.cpp
new file mode 100644
index 0000000..8bf1aba
--- /dev/null
+++ b/vision/GoalMaster.cpp
@@ -0,0 +1,61 @@
+#include "math.h"
+
+#include "aos/common/time.h"
+#include "aos/linux_code/init.h"
+#include "aos/common/logging/logging.h"
+
+#include "frc971/queues/GyroAngle.q.h"
+#include "frc971/queues/CameraTarget.q.h"
+
+#include "vision/RingBuffer.h"
+#include "vision/SensorProcessor.h"
+
+using ::frc971::vision::RingBuffer;
+using ::frc971::sensors::gyro;
+using ::frc971::vision::targets;
+using ::frc971::vision::target_angle;
+using ::frc971::kPixelsToMeters;
+using ::frc971::kMetersToShooterSpeeds;
+using ::frc971::kMetersToShooterAngles;
+using ::frc971::interpolate;
+
+int main() {
+ //RingBuffer< ::aos::time::Time, double> buff;
+ ::aos::InitNRT();
+ while (true) {
+ //gyro.FetchNextBlocking();
+ //buff.Sample(gyro->sent_time, gyro->angle);
+ if (targets.FetchNext()) {
+ /*::aos::time::Time stamp = ::aos::time::Time::InNS(targets->timestamp);
+ double angle_goal =
+ buff.ValueAt(stamp) -
+ M_PI / 2.0 * targets->percent_azimuth_off_center / 2.0;
+ printf("%g ",angle_goal);
+ printf("%g\n",gyro->angle);*/
+
+ double meters = interpolate(
+ sizeof(kPixelsToMeters) / sizeof(kPixelsToMeters[0]),
+ kPixelsToMeters,
+ targets->percent_elevation_off_center);
+ const double shooter_speed = interpolate(
+ sizeof(kMetersToShooterSpeeds) / sizeof(kMetersToShooterSpeeds[0]),
+ kMetersToShooterSpeeds,
+ meters);
+ const double shooter_angle = interpolate(
+ sizeof(kMetersToShooterAngles) / sizeof(kMetersToShooterAngles[0]),
+ kMetersToShooterAngles,
+ meters);
+
+ LOG(DEBUG, "%+f=> think target is %f meters away Speed %f Angle %f\n",
+ targets->percent_elevation_off_center,
+ meters, shooter_speed, shooter_angle);
+
+ target_angle.MakeWithBuilder()
+ /*.target_angle(angle_goal)*/
+ .shooter_speed(shooter_speed)
+ .shooter_angle(shooter_angle)
+ .Send();
+ }
+ }
+ ::aos::Cleanup();
+}
diff --git a/vision/JPEGRoutines.cpp b/vision/JPEGRoutines.cpp
new file mode 100644
index 0000000..e3dc254
--- /dev/null
+++ b/vision/JPEGRoutines.cpp
@@ -0,0 +1,192 @@
+#include "vision/JPEGRoutines.h"
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <errno.h>
+#include <string.h>
+
+#include "aos/common/time.h"
+
+#include "vision/OpenCVWorkTask.h"
+
+namespace frc971 {
+namespace vision {
+
+/* This is also adapted from libjpeg to be used on decompression tables rather than
+ * compression tables as it was origionally intended
+ */
+void decompress_add_huff_table (j_decompress_ptr cinfo,
+ JHUFF_TBL **htblptr, const UINT8 *bits, const UINT8 *val)
+/* Define a Huffman table */
+{
+ int nsymbols, len;
+
+ if (*htblptr == NULL)
+ *htblptr = jpeg_alloc_huff_table((j_common_ptr) cinfo);
+
+ /* Copy the number-of-symbols-of-each-code-length counts */
+ memcpy((*htblptr)->bits, bits, sizeof((*htblptr)->bits));
+
+ /* Validate the counts. We do this here mainly so we can copy the right
+ * number of symbols from the val[] array, without risking marching off
+ * the end of memory. jchuff.c will do a more thorough test later.
+ */
+ nsymbols = 0;
+ for (len = 1; len <= 16; len++)
+ nsymbols += bits[len];
+ if (nsymbols < 1 || nsymbols > 256){
+ fprintf(stderr,"%s:%d: Error, bad huffman table",__FILE__,__LINE__);
+ exit(-1);
+ }
+
+ memcpy((*htblptr)->huffval, val, nsymbols * sizeof(uint8_t));
+
+}
+
+/* standard_huff_tables is taken from libjpeg compression stuff
+ * and is here used to set up the same tables in the decompression structure.
+ */
+void standard_huff_tables (j_decompress_ptr cinfo)
+ /* Set up the standard Huffman tables (cf. JPEG standard section K.3) */
+ /* IMPORTANT: these are only valid for 8-bit data precision! */
+{
+ static const UINT8 bits_dc_luminance[17] =
+ { /* 0-base */ 0, 0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0 };
+ static const UINT8 val_dc_luminance[] =
+ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 };
+
+ static const UINT8 bits_dc_chrominance[17] =
+ { /* 0-base */ 0, 0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 };
+ static const UINT8 val_dc_chrominance[] =
+ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 };
+
+ static const UINT8 bits_ac_luminance[17] =
+ { /* 0-base */ 0, 0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 0x7d };
+ static const UINT8 val_ac_luminance[] =
+ { 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12,
+ 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07,
+ 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08,
+ 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0,
+ 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16,
+ 0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28,
+ 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
+ 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
+ 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
+ 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
+ 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
+ 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
+ 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98,
+ 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
+ 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6,
+ 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5,
+ 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4,
+ 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2,
+ 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea,
+ 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
+ 0xf9, 0xfa };
+
+ static const UINT8 bits_ac_chrominance[17] =
+ { /* 0-base */ 0, 0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 0x77 };
+ static const UINT8 val_ac_chrominance[] =
+ { 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21,
+ 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71,
+ 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91,
+ 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0,
+ 0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34,
+ 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26,
+ 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38,
+ 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
+ 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
+ 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
+ 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
+ 0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
+ 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96,
+ 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5,
+ 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4,
+ 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3,
+ 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2,
+ 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda,
+ 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9,
+ 0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
+ 0xf9, 0xfa };
+
+ decompress_add_huff_table(cinfo, &cinfo->dc_huff_tbl_ptrs[0],
+ bits_dc_luminance, val_dc_luminance);
+ decompress_add_huff_table(cinfo, &cinfo->ac_huff_tbl_ptrs[0],
+ bits_ac_luminance, val_ac_luminance);
+ decompress_add_huff_table(cinfo, &cinfo->dc_huff_tbl_ptrs[1],
+ bits_dc_chrominance, val_dc_chrominance);
+ decompress_add_huff_table(cinfo, &cinfo->ac_huff_tbl_ptrs[1],
+ bits_ac_chrominance, val_ac_chrominance);
+}
+
+
+
+
+void process_jpeg(unsigned char *out,unsigned char *image,size_t size){
+ struct jpeg_decompress_struct cinfo;
+ struct jpeg_error_mgr jerr;
+
+ static aos::time::Time timestamp_old = aos::time::Time::Now();
+ //aos::time::Time timestamp_start = aos::time::Time::Now();
+
+ cinfo.err = jpeg_std_error( &jerr );
+ cinfo.out_color_space = JCS_RGB;
+ jpeg_create_decompress( &cinfo );
+ //jpeg_stdio_src( &cinfo, infile );
+ jpeg_mem_src(&cinfo,image,size);
+
+ jpeg_read_header( &cinfo, TRUE );
+ standard_huff_tables (&cinfo);
+
+
+// printf( "JPEG File Information: \n" );
+// printf( "Image width and height: %d pixels and %d pixels.\n", cinfo.image_width, cinfo.image_height );
+// printf( "Color components per pixel: %d.\n", cinfo.num_components );
+// printf( "Color space: %d.\n", cinfo.jpeg_color_space );
+ //printf("JpegDecompressed\n");
+
+ jpeg_start_decompress( &cinfo );
+
+ int offset = 0;
+ int step = cinfo.num_components * cinfo.image_width;
+ unsigned char *buffers[cinfo.image_height];
+ for (int i = cinfo.image_height - 1; i >= 0; --i) {
+ buffers[i] = &out[offset];
+ offset += step;
+ }
+
+ while( cinfo.output_scanline < cinfo.image_height )
+ {
+ jpeg_read_scanlines(&cinfo, &buffers[cinfo.output_scanline],
+ cinfo.image_height - cinfo.output_scanline);
+ }
+
+ /* This used to do BGR to RGB conversions inline */
+/*
+ for (int i = 0; i < (int)(cinfo.image_height * cinfo.image_width * 3); i+= 3) {
+ uint8_t b = out[i + 0];
+ uint8_t r = out[i + 2];
+ out[i + 0] = r;
+ out[i + 2] = b;
+ }
+*/
+ jpeg_finish_decompress( &cinfo );
+ jpeg_destroy_decompress( &cinfo );
+
+ aos::time::Time timestamp_end = aos::time::Time::Now();
+
+ //double jpeg_part = ((timestamp_end - timestamp_start).nsec()) / 1000000000.0;
+ //double longer_part = ((timestamp_end - timestamp_old).nsec()) / 1000000000.0;
+
+ //printf("%g %g\n",jpeg_part / longer_part,1.0 / longer_part);
+
+ timestamp_old = timestamp_end;
+
+}
+
+} // namespace vision
+} // namespace frc971
+
diff --git a/vision/JPEGRoutines.h b/vision/JPEGRoutines.h
new file mode 100644
index 0000000..c3b8e13
--- /dev/null
+++ b/vision/JPEGRoutines.h
@@ -0,0 +1,17 @@
+// for jpeglib.h
+#include <stdio.h>
+
+#include "libjpeg/include/jpeglib.h"
+
+namespace frc971 {
+namespace vision {
+
+void decompress_add_huff_table (j_decompress_ptr cinfo,
+ JHUFF_TBL **htblptr, const UINT8 *bits, const UINT8 *val);
+
+void standard_huff_tables (j_decompress_ptr cinfo);
+
+void process_jpeg(unsigned char *out,unsigned char *image,size_t size);
+
+} // namespace vision
+} // namespace frc971
diff --git a/vision/OpenCVWorkTask.cpp b/vision/OpenCVWorkTask.cpp
new file mode 100644
index 0000000..982d670
--- /dev/null
+++ b/vision/OpenCVWorkTask.cpp
@@ -0,0 +1,139 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <errno.h>
+#include <string.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+
+#include <vector>
+#include <iostream>
+
+#include "libjpeg/include/jpeglib.h"
+
+#include "aos/common/time.h"
+#include "aos/linux_code/camera/Buffers.h"
+#include "aos/linux_code/init.h"
+#include "aos/common/logging/logging.h"
+
+#include "vision/OpenCVWorkTask.h"
+#include "vision/CameraProcessor.h"
+#include "vision/JPEGRoutines.h"
+
+
+namespace frc971 {
+namespace vision {
+
+} // namespace vision
+} // namespace frc971
+
+namespace {
+void SaveImageToFile(IplImage *image, const char *filename) {
+ FILE *file = fopen(filename, "w");
+
+ fputs("P3\n320 240\n255\n", file);
+ ::cv::Mat img(image);
+ for (int i = 0; i < img.rows; i++) {
+ for (int j = 0; j < img.cols; j++) {
+ // You can now access the pixel value with cv::Vec3b
+ fprintf(file, "%d %d %d ",
+ img.at<cv::Vec3b>(i,j)[0],
+ img.at<cv::Vec3b>(i,j)[1],
+ img.at<cv::Vec3b>(i,j)[2]);
+
+ }
+ fputs("\n", file);
+ }
+ fclose(file);
+}
+}
+
+#include "frc971/queues/CameraTarget.q.h"
+using frc971::vision::targets;
+
+void sender_main(){
+ ::aos::camera::Buffers buffers;
+ CvSize size;
+ size.width = ::aos::camera::Buffers::Buffers::kWidth;
+ size.height = ::aos::camera::Buffers::Buffers::kHeight;
+ unsigned char buffer[::aos::camera::Buffers::Buffers::kWidth *
+ ::aos::camera::Buffers::Buffers::kHeight * 3];
+
+ // create processing object
+ ProcessorData processor(size.width, size.height, false);
+
+ printf("started sender main\n");
+ LOG(INFO, "Camera server started\n");
+ while(true){
+ //usleep(7500);
+ size_t data_size;
+ timeval timestamp_timeval;
+ LOG(DEBUG, "getting new image\n");
+ const void *image = buffers.GetNext(
+ true, &data_size, ×tamp_timeval, NULL);
+ ::aos::time::Time timestamp(timestamp_timeval);
+
+ LOG(DEBUG, "Got new image\n");
+ frc971::vision::process_jpeg(
+ buffer, static_cast<unsigned char *>(const_cast<void *>(image)),
+ data_size);
+
+ // build the headers on top of the buffer
+ cvSetData(processor.src_header_image,
+ buffer,
+ ::aos::camera::Buffers::Buffers::kWidth * 3);
+
+ // Reset.
+ processor.clear();
+ // transform the buffer into targets
+ processor.threshold(buffer);
+
+ processor.getContours();
+ processor.filterToTargets();
+
+ // could be used for debug ie drawContours
+ //std::vector<std::vector<cv::Point> > target_contours;
+ //std::vector<std::vector<cv::Point> > best_contours;
+ std::vector<Target>::iterator target_it;
+ Target *best_target = NULL;
+ // run through the targets
+ LOG(DEBUG, "Found %u targets\n", processor.target_list.size());
+ for(target_it = processor.target_list.begin();
+ target_it != processor.target_list.end(); target_it++){
+ //target_contours.push_back(*(target_it->this_contour));
+ // select the highest target
+ if (best_target == NULL) {
+ best_target = &*target_it;
+ } else {
+ if (target_it->height < best_target->height) {
+ best_target = &*target_it;
+ }
+ }
+ }
+ // if we found one then send it on
+ if (best_target != NULL) {
+ LOG(DEBUG, "Target is %f\n", best_target->rect.centroid.x);
+ targets.MakeWithBuilder()
+ .percent_azimuth_off_center(
+ best_target->rect.centroid.y / (double)::aos::camera::Buffers::kHeight - 0.5)
+ .percent_elevation_off_center(
+ best_target->rect.centroid.x / (double)::aos::camera::Buffers::kWidth - 0.5)
+ .timestamp(timestamp.ToNSec())
+ .Send();
+ }
+ //static int counter = 0;
+ //if (++counter > 2) {
+ //break;
+ //}
+ }
+}
+
+
+int main(int /*argc*/, char** /*argv*/){
+ ::aos::InitNRT();
+ sender_main();
+ ::aos::Cleanup();
+}
+
diff --git a/vision/OpenCVWorkTask.h b/vision/OpenCVWorkTask.h
new file mode 100644
index 0000000..78bda5d
--- /dev/null
+++ b/vision/OpenCVWorkTask.h
@@ -0,0 +1,20 @@
+#ifndef VISION_OPENCV_WORK_TASK_H_
+#define VISION_OPENCV_WORK_TASK_H_
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include "event2/buffer.h"
+#include "event2/event.h"
+#include "event2/listener.h"
+#include "event2/bufferevent.h"
+
+#include "aos/common/mutex.h"
+
+namespace frc971 {
+namespace vision {
+
+} // namespace vision
+} // namespace frc971
+
+#endif // VISION_OPENCV_WORK_TASK_H_
diff --git a/vision/PacketNotifier.cpp b/vision/PacketNotifier.cpp
new file mode 100644
index 0000000..d12872e
--- /dev/null
+++ b/vision/PacketNotifier.cpp
@@ -0,0 +1,79 @@
+#include "vision/PacketNotifier.h"
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <errno.h>
+#include <string.h>
+
+namespace frc971 {
+namespace vision {
+
+void PacketNotifier::RegisterSender(){
+ close(fd[1]);
+}
+void PacketNotifier::RegisterReciever(){
+ close(fd[0]);
+}
+PacketNotifier *PacketNotifier::MMap(size_t data_size){
+ PacketNotifier *data;
+ data = (PacketNotifier *)mmap(NULL, ((data_size * 3 + 4095 + sizeof(PacketNotifier)) / 4096) * 4096,
+ PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
+ data->data_size = data_size;
+ socketpair(PF_LOCAL, SOCK_STREAM, 0, data->fd);
+ for(int i = 0;i < 3;i++){
+ data->buffs[i] = (uint8_t *)data + (sizeof(PacketNotifier) + i * data_size);
+ }
+ void *place = &(data->mutex);
+ *((int *)place) = 0; // The Mutex class needs a placement new operator. (you know, keep the masses happy);
+ data->in_flight = false;
+ data->to_send = -1;
+ data->sending = -1;
+ return data;
+}
+void *PacketNotifier::GetBuffer(){
+ mutex.Lock();
+ for(int i = 0; i < 3 ; i++){ //search for open spot.
+ if(i != sending && i != to_send){ //open
+ filling = i;
+ mutex.Unlock();
+ //printf("leasing out to fill buff # %d\n",i);
+ return buffs[i];
+ }
+ }
+ mutex.Unlock();
+ //printf("Error in the fabric of the universe\n");
+ exit(-42);
+}
+void PacketNotifier::Notify(){
+ mutex.Lock();
+ to_send = filling;
+ filling = -1;
+ mutex.Unlock();
+ // wall error
+ if(write(fd[0],"\n",1)) {}
+}
+
+void PacketNotifier::DataSent(const void * /*data*/, size_t /*datalen*/){
+ //printf("packet_sent: %d; fill: %d; to_send: %d \n",sending,filling,to_send);
+ mutex.Lock();
+ sending = -1;
+ mutex.Unlock();
+ in_flight = false;
+}
+bool PacketNotifier::GetData(char **place_to_put,size_t *length){
+ if(in_flight) return false;
+ mutex.Lock();
+ *length = data_size;
+ *place_to_put = (char *)buffs[to_send];
+ sending = to_send;
+ to_send = -1;
+ mutex.Unlock();
+ in_flight = true;
+ return true;
+}
+
+} // namespace vision
+} // namespace frc971
+
diff --git a/vision/PacketNotifier.h b/vision/PacketNotifier.h
new file mode 100644
index 0000000..7aff7fa
--- /dev/null
+++ b/vision/PacketNotifier.h
@@ -0,0 +1,45 @@
+#ifndef FRC971_VISION_PACKET_NOTIFIER_H_
+#define FRC971_VISION_PACKET_NOTIFIER_H_
+#include "event2/buffer.h"
+#include "event2/event.h"
+#include "event2/listener.h"
+#include "event2/bufferevent.h"
+#include "aos/common/mutex.h"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+namespace frc971 {
+namespace vision {
+
+/* This class lives in shared memory (using an anonomous mmap) to transfer data between
+ * the server process and the image processing process.
+ */
+struct PacketNotifier{
+ aos::Mutex mutex;
+ int fd[2];
+ //3 things can be happening:
+ //something waiting to be sent, something sending, and something getting filled (decompressed to)
+ void *buffs[3];
+ int to_send;
+ int filling;
+ int sending;
+ bool in_flight;
+ size_t data_size;
+ void Notify();
+ void RegisterSender();
+ void RegisterReciever();
+ int RecieverFD(){ return fd[1]; }
+ static PacketNotifier *MMap(size_t data_size);
+ void DataSent(const void * /*data*/, size_t /*datalen*/);
+ void *GetBuffer();
+ static void StaticDataSent(const void *data, size_t datalen, void *self){
+ ((PacketNotifier *)(self))->DataSent(data,datalen);
+ }
+ bool GetData(char **place_to_put,size_t *length);
+};
+} // namespace vision
+} // namespace frc971
+
+
+#endif //FRC971_VISION_PACKET_NOTIFIER_H_
diff --git a/vision/RingBuffer.h b/vision/RingBuffer.h
new file mode 100644
index 0000000..f333f29
--- /dev/null
+++ b/vision/RingBuffer.h
@@ -0,0 +1,67 @@
+#ifndef VISION_RINGBUFFER_H_
+#define VISION_RINGBUFFER_H_
+
+#include <stdio.h>
+#include <stdlib.h>
+
+namespace frc971 {
+namespace vision {
+
+template<class T,class V>
+class RingBuffer{
+ //record class to hold sampes
+ class Samples{
+ public:
+ T time;
+ V value;
+ };
+ Samples *samples;
+ int current_;
+ int wraps_;
+ V value_at(int index){
+ return samples[index & 255].value;
+ }
+ T time_at(int index){
+ return samples[index & 255].time;
+ }
+ public:
+ RingBuffer(){
+ current_ = 0;
+ wraps_ = 0;
+ samples = (Samples *)malloc(sizeof(Samples) * 256);
+ }
+ // Adds samples into the ringbuffer.
+ void Sample(T time,V val){
+ current_ += 1;
+ wraps_ += current_ / 256;
+ current_ = current_ % 256;
+ samples[current_].time = time;
+ samples[current_].value = val;
+ }
+ // Binary Search to find and interpolate the values.
+ V ValueAt(T time){
+ int start = current_ - 255;
+ int end = current_;
+ if(start < 0 && !wraps_){
+ start = 0;
+ }
+ int max = end;
+ int min = start;
+ while(end - start > 1){
+ int mid = (start + end) / 2;
+ Samples check = samples[mid & 255];
+ if(check.time < time){
+ start = mid;
+ min = mid;
+ }else{
+ max = mid;
+ end = mid;
+ }
+ }
+ return value_at(min) + (value_at(max) - value_at(min)) *
+ ((time - time_at(min)).ToSeconds()/(time_at(max) - time_at(min)).ToSeconds());
+ }
+};
+}; // vision
+}; // frc971
+#endif // VISION_RINGBUFFER_H_
diff --git a/vision/SensorProcessor.cpp b/vision/SensorProcessor.cpp
new file mode 100644
index 0000000..2060956
--- /dev/null
+++ b/vision/SensorProcessor.cpp
@@ -0,0 +1,53 @@
+#include "vision/SensorProcessor.h"
+
+#include <stdio.h>
+
+namespace frc971 {
+
+// give a set of x -> fx pairs find a range for our value
+// then interpolate between and return an interpolated fx.
+// If the value is off the end just extend the line to
+// meet our point. If something does go wrong (and it
+// never should) it will return -1.
+double interpolate(int num_interp_vals,
+ const Interpolation *interp, double value) {
+ double dy;
+ double dx;
+ double a;
+ double intercept;
+ //printf("for val %.1f\n", value);
+ if (value < interp[0].x) {
+ // if closer than nearest
+ dy = interp[1].fx - interp[0].fx;
+ dx = interp[1].x - interp[0].x;
+ a = value - interp[0].x;
+ intercept = interp[0].fx;
+ //printf("LESS THAN\n");
+ } else if (value > interp[num_interp_vals-1].x){
+ // if further than furthest
+ dy = interp[num_interp_vals-1].fx - interp[num_interp_vals-2].fx;
+ dx = interp[num_interp_vals-1].x - interp[num_interp_vals-2].x;
+ a = value - interp[num_interp_vals-2].x;
+ intercept = interp[num_interp_vals-2].fx;
+ //printf("GT THAN\n");
+ } else {
+ //printf("gh0\n");
+ // scan for range
+ for(int i=0; i<num_interp_vals-1; i++){
+ if(value >= interp[i].x && value <= interp[i+1].x){
+ // printf("(%.1f,%.1f)=(%.1f,%.1f)\n",
+ // interp[i].x, interp[i+1].x,
+ // interp[i].fx, interp[i+1].fx);
+ double lambda =
+ (value - interp[i].x)/(interp[i+1].x - interp[i].x);
+ return (1-lambda)*interp[i].fx + lambda*interp[i+1].fx;
+ }
+ }
+ // this should maybe be an assert
+ return -1;
+ }
+
+ return ( (dy/dx)*a + intercept );
+}
+
+} // namespace frc971
diff --git a/vision/SensorProcessor.h b/vision/SensorProcessor.h
new file mode 100644
index 0000000..daf24be
--- /dev/null
+++ b/vision/SensorProcessor.h
@@ -0,0 +1,45 @@
+#ifndef VISION_SENSOR_PROCESSOR_H_
+#define VISION_SENSOR_PROCESSOR_H_
+
+namespace frc971 {
+
+// struct maps a single point x to to a value f of x
+typedef struct {
+ double x;
+ double fx;
+} Interpolation;
+
+static const Interpolation kPixelsToMeters[] = {
+ {-0.050781, 4.7498},
+ {-0.0375, 4.318},
+ {0.028125, 3.9878},
+ {0.080469, 3.51},
+ {0.126563, 3.1496},
+ {0.131, 2.9972},
+ {0.144, 2.921},
+ {0.196, 3.2258},
+ // Below here is junk because it starts coming off of the tower base.
+ {0.296875, 2.667},
+ {0.351562, 2.3876},
+};
+
+// Must be in reverse order in meters.
+static const Interpolation kMetersToShooterSpeeds[] = {
+ {2.0, 375.0},
+ {3.0, 360.0},
+ {4.5, 375.0},
+};
+
+static const Interpolation kMetersToShooterAngles[] = {
+ {3.0, 0.68},
+ {3.7, 0.635},
+ {4.15, 0.58},
+ {5.0, 0.51},
+};
+
+double interpolate(int num_interp_vals,
+ const Interpolation *interp, double value);
+
+} // namespace frc971
+
+#endif // VISION_SENSOR_PROCESSOR_H_
diff --git a/vision/bmp_stream/bmp.rb b/vision/bmp_stream/bmp.rb
new file mode 100644
index 0000000..122c9de
--- /dev/null
+++ b/vision/bmp_stream/bmp.rb
@@ -0,0 +1,11 @@
+class BMPHeader
+ def initialize(width,height)
+ @width = width
+ @height = height
+ end
+ def text()
+ size = @width * @height * 3
+ return ["BM",size + 54,"\0\0\0\0",54,40,@width,
+ @height,1,24,0,size,2835,2835,0,0].pack("a2Ia4IIIIssIIIIII")
+ end
+end
diff --git a/vision/bmp_stream/bmp_dump.rb b/vision/bmp_stream/bmp_dump.rb
new file mode 100644
index 0000000..6c5133c
--- /dev/null
+++ b/vision/bmp_stream/bmp_dump.rb
@@ -0,0 +1,45 @@
+require "socket"
+
+require "bmp"
+sock = TCPSocket.new(ARGV[0] || "fitpc",8020)
+$width = 640 / 2
+$height = 480 / 2
+$header = BMPHeader.new($width,$height).text()
+
+
+
+require "rubygems"
+require "gtk2"
+
+
+$image = Gtk::Image.new()
+$window = Gtk::Window.new()
+$window.add($image)
+$window.show_all()
+$window.signal_connect("delete-event") { Gtk.main_quit}
+
+loader = Gdk::PixbufLoader.new
+loader.write($header)
+data = sock.read($width * $height * 3)
+loader.last_write(data)
+loader.close
+$image.pixbuf = loader.pixbuf
+$oldtime = Time.now
+i = 0
+Gtk.idle_add do
+ loader = Gdk::PixbufLoader.new
+ loader.write($header)
+ data = sock.read($width * $height * 3)
+# (640 * 480).times do |i| #BGR -> RGB
+# b,g,r = data[i * 3 + 0],data[i * 3 + 1],data[i * 3 + 2]
+# data[i * 3 + 0],data[i * 3 + 1],data[i * 3 + 2] = r,g,b
+# end
+ loader.last_write(data)
+ loader.close
+ new_time = Time.now()
+ puts 1 / (new_time - $oldtime)
+ $oldtime = new_time
+ $image.pixbuf = loader.pixbuf
+end
+
+Gtk.main()
diff --git a/vision/bmp_stream/bmp_stream_from_file.rb b/vision/bmp_stream/bmp_stream_from_file.rb
new file mode 100644
index 0000000..3324484
--- /dev/null
+++ b/vision/bmp_stream/bmp_stream_from_file.rb
@@ -0,0 +1,43 @@
+require "socket"
+$header = File.open("header.bmp") { |f| f.read(54)}
+
+
+sock = File.open("output.bmp_stream")
+#TCPSocket.new(ARGV[0] || "fitpc",8020)
+
+
+
+require "rubygems"
+require "gtk2"
+
+
+$image = Gtk::Image.new()
+$window = Gtk::Window.new()
+$window.add($image)
+$window.show_all()
+$window.signal_connect("delete-event") { Gtk.main_quit}
+
+loader = Gdk::PixbufLoader.new
+loader.write($header)
+data = sock.read(320 * 240 * 3)
+loader.last_write(data)
+loader.close
+$image.pixbuf = loader.pixbuf
+$oldtime = Time.now
+Gtk.timeout_add(1000) do
+ loader = Gdk::PixbufLoader.new
+ loader.write($header)
+ data = sock.read(320 * 240 * 3)
+# (640 * 480).times do |i| #BGR -> RGB
+# b,g,r = data[i * 3 + 0],data[i * 3 + 1],data[i * 3 + 2]
+# data[i * 3 + 0],data[i * 3 + 1],data[i * 3 + 2] = r,g,b
+# end
+ loader.last_write(data)
+ loader.close
+ new_time = Time.now()
+ puts 1.0 / (new_time - $oldtime)
+ $oldtime = new_time
+ $image.pixbuf = loader.pixbuf
+end
+
+Gtk.main()
diff --git a/vision/bmp_stream/bmp_stream_to_file.rb b/vision/bmp_stream/bmp_stream_to_file.rb
new file mode 100644
index 0000000..962bb2d
--- /dev/null
+++ b/vision/bmp_stream/bmp_stream_to_file.rb
@@ -0,0 +1,20 @@
+require "socket"
+$header = File.open("header.bmp") { |f| f.read(54)}
+
+
+sock = TCPSocket.new(ARGV[0] || "fitpc",8020)
+
+
+
+require "rubygems"
+require "gtk2"
+
+
+
+File.open("output.bmp_stream","w+") do |file|
+ 400.times do |i|
+ data = sock.read(320 * 240 * 3)
+ file.print(data)
+ puts "frame: #{i}"
+ end
+end
diff --git a/vision/tests/FieldDBGCamProc.cpp b/vision/tests/FieldDBGCamProc.cpp
new file mode 100644
index 0000000..f23b541
--- /dev/null
+++ b/vision/tests/FieldDBGCamProc.cpp
@@ -0,0 +1,210 @@
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <vector>
+
+#include "../CameraProcessor.h"
+#include "../SensorProcessor.h"
+
+#include "opencv2/highgui/highgui.hpp"
+
+const int num_names = 39;
+
+
+static const bool USE_ROTATED = true;
+static const int use_width = 320;
+static const int use_height = 240;
+const char * image_names[num_names] = {
+ "NASA_bmp/img26_series_b_side_204_e65.jpg",
+ "NASA_bmp/img19_series_b_side_110_e65.jpg",
+ "NASA_bmp/img12_series_b_center_224_e65.jpg",
+ "NASA_bmp/img23_series_b_side_101_e65.jpg",
+ "NASA_bmp/img15_series_b_side_230_e65.jpg",
+ "NASA_bmp/img10_series_b_center_203_e65.jpg",
+ "NASA_bmp/img11_series_b_center_203_e65.jpg",
+ "NASA_bmp/img13_series_b_center_260_e65.jpg",
+ "NASA_bmp/img14_series_b_center_251_e65.jpg",
+ "NASA_bmp/img16_series_b_side_196_e65.jpg",
+ "NASA_bmp/img17_series_b_side_160_e65.jpg",
+ "NASA_bmp/img18_series_b_side_140_e65.jpg",
+ "NASA_bmp/img1_center_200_e65.jpg",
+ "NASA_bmp/img20_series_b_side_114_e65.jpg",
+ "NASA_bmp/img21_series_b_side_137_e65.jpg",
+ "NASA_bmp/img22_center field_e10.jpg",
+ "NASA_bmp/img22_dog Center Field_e10.jpg",
+ "NASA_bmp/img22_series_b_side_150_e65.jpg",
+ "NASA_bmp/img23_center field_e10.jpg",
+ "NASA_bmp/img24_center field_e10.jpg",
+ "NASA_bmp/img24_series_b_side_104_e65.jpg",
+ "NASA_bmp/img25_series_b_side_195_e65.jpg",
+ "NASA_bmp/img27_series_b_side_192_e65.jpg",
+ "NASA_bmp/img28_series_b_side_192_e65.jpg",
+ "NASA_bmp/img29_series_b_side_186_e65.jpg",
+ "NASA_bmp/img2_center_207_e65.jpg",
+ "NASA_bmp/img30_series_b_side_177_e65.jpg",
+ "NASA_bmp/img31_series_b_side_176_e65.jpg",
+ "NASA_bmp/img32_series_b_side_212_e65.jpg",
+ "NASA_bmp/img33_series_b_side_251_e65.jpg",
+ "NASA_bmp/img34_series_b_side_272_e65.jpg",
+ "NASA_bmp/img35_series_b_side_23+219_e65.jpg",
+ "NASA_bmp/img3_center_180_e65.jpg",
+ "NASA_bmp/img4_series_b_center_106_e65.jpg",
+ "NASA_bmp/img5_series_b_center_122_e65.jpg",
+ "NASA_bmp/img6_series_b_center_145_e65.jpg",
+ "NASA_bmp/img7_series_b_center_174_e65.jpg",
+ "NASA_bmp/img8_series_b_center_196_e65.jpg",
+ "NASA_bmp/img9_series_b_center_201_e65.jpg"};
+
+const char * WINDOW_NAME = "Treshhold Window";
+const char * WINDOW_NAME2 = "Target Window";
+
+
+static void onMouse( int event, int x, int y, int, void* userData ) {
+ if( event != CV_EVENT_LBUTTONDOWN ) return;
+ ProcessorData *proc = (ProcessorData *) userData;
+ IplImage *image = proc->src_header_image;
+ uchar b = *((uchar*) (image->imageData + y*image->widthStep + 3*x));
+ uchar g = *((uchar*) (image->imageData + y*image->widthStep + 3*(x+1)));
+ uchar r = *((uchar*) (image->imageData + y*image->widthStep + 3*(x+2)));
+
+ uchar h=0;
+ uchar s=0;
+ uchar v=0;
+ proc->RGBtoHSV(r, g, b, &h, &s, &v);
+
+
+ *((uchar*) (image->imageData + y*image->widthStep + 3*x)) = 128;
+ *((uchar*) (image->imageData + y*image->widthStep + 3*(x+1))) = 128;
+ *((uchar*) (image->imageData + y*image->widthStep + 3*(x+2))) = 255;
+
+ cv::Mat src(image);
+ //cv::imshow("test", src);
+
+ printf("got click (%d,%d)= <%d,%d,%d> -- [%d,%d,%d]\n",
+ x, y, r, g, b, h, s, v);
+}
+
+
+int main( int argc, char *argv[] ){
+ ProcessorData processor(use_width, use_height, USE_ROTATED);
+ int img_cycle = 0;
+ int thresh = 100;
+
+ cvStartWindowThread();
+
+ cvNamedWindow ("cnt", CV_WINDOW_AUTOSIZE);
+ cvNamedWindow ("GLOBAL", CV_WINDOW_AUTOSIZE);
+ //cvNamedWindow ("Grey Img", CV_WINDOW_AUTOSIZE);
+ //cvNamedWindow ("test", CV_WINDOW_AUTOSIZE);
+ cvNamedWindow (WINDOW_NAME2, CV_WINDOW_AUTOSIZE);
+ cvNamedWindow (WINDOW_NAME, CV_WINDOW_AUTOSIZE);
+
+ cvMoveWindow(WINDOW_NAME,0,0);
+ cvMoveWindow("GLOBAL",325,0);
+ cvMoveWindow(WINDOW_NAME2,650,0);
+ //cvMoveWindow("Grey Img", 0, 275);
+ //cvMoveWindow("test", 325, 275);
+ cvMoveWindow("cnt",1100,100);
+ //Creating the trackbars
+ cvCreateTrackbar("H1","cnt",&processor.h1,360,0);
+ cvCreateTrackbar("H2","cnt",&processor.h2,360,0);
+ cvCreateTrackbar("S1","cnt",&processor.s1,255,0);
+ cvCreateTrackbar("S2","cnt",&processor.s2,255,0);
+ cvCreateTrackbar("V1","cnt",&processor.v1,255,0);
+ cvCreateTrackbar("V2","cnt",&processor.v2,255,0);
+
+ while (img_cycle >= 0) {
+ processor.clear();
+ printf("%d = %s\n", img_cycle, image_names[img_cycle]);
+ processor.src_header_image = cvLoadImage(image_names[img_cycle]);
+ cvCopy(processor.src_header_image, processor.global_display);
+
+ cv::setMouseCallback( WINDOW_NAME2, onMouse,
+ (void *)&processor );
+
+ cv::Mat global_mat(processor.global_display);
+ cv::Mat src_mat(processor.src_header_image);
+
+ // These lines are the vision processing, the rest of main
+ // is just fluff
+ processor.threshold((uchar *)
+ processor.src_header_image->imageData);
+ processor.getContours();
+ processor.filterToTargets();
+
+ if(!processor.target_list.empty()){
+ std::vector<std::vector<cv::Point> > target_contours;
+ std::vector<std::vector<cv::Point> > best_contours;
+ std::vector<std::vector<cv::Point> > raw_contours;
+ std::vector<Target>::iterator target_it;
+ Target *best_target = NULL;
+ int i = 0;
+ for(target_it = processor.target_list.begin();
+ target_it != processor.target_list.end(); target_it++){
+ target_contours.push_back(target_it->this_contour);
+ raw_contours.push_back(target_it->raw_contour);
+ printf("%d: h=%.1f, interp=%.1f, <x,y>=<%.1f,%.1f>\n",
+ i++, target_it->height,
+ interpolate(4, &pixel_to_dist[0], target_it->rect.centroid.x),
+ target_it->rect.centroid.x, target_it->rect.centroid.y);
+ if (best_target == NULL) {
+ best_target = &*target_it;
+ } else {
+ if (target_it->height > best_target->height) {
+ best_target = &*target_it;
+ }
+ /* if (processor.is_90) {
+ if (target_it->rect.centroid.x > best_target->rect.centroid.x) {
+ best_target = &*target_it;
+ }
+ } else {
+ if (target_it->rect.centroid.y < best_target->rect.centroid.y) {
+ best_target = &*target_it;
+ }
+ }*/
+ }
+ }
+ best_contours.push_back(best_target->this_contour);
+ //drawContours(global_mat,target_contours,-1,color,CV_FILLED);
+ cv::imshow(WINDOW_NAME, src_mat);
+ //cv::imshow("Grey Img", *processor.grey_mat);
+ cv::Scalar color(0,0,255);
+ cv::drawContours( src_mat, target_contours, -1, color, CV_FILLED );
+ cv::Scalar color2(128,0,255);
+ cv::drawContours( src_mat, best_contours, -1, color2, CV_FILLED );
+ cv::Scalar color3(0,255,0);
+ cv::drawContours( src_mat, raw_contours, -1, color3, 1 );
+ }
+ //cv::Mat grey_mat(grey_image);
+ //cv::imshow(WINDOW_NAME2, grey_mat);
+ cv::imshow("GLOBAL", global_mat);
+ cv::imshow(WINDOW_NAME2, src_mat);
+ char key = cvWaitKey(3000);
+ switch (key) {
+ case ' ':
+ img_cycle++;
+ img_cycle = img_cycle % num_names;
+ printf("%c %d= %s\n", key, img_cycle, image_names[img_cycle]);
+ break;
+ case 'g':
+ thresh++;
+ thresh = (thresh % 255);
+ printf("+ thresh= %d\n", thresh);
+ break;
+ case 'G':
+ thresh--;
+ thresh = (thresh % 255);
+ printf("- thresh= %d\n", thresh);
+ break;
+ case 'q':
+ img_cycle = -1;
+ break;
+ default:
+ break;
+ }
+ //redraw image cuz we drew all over it
+ }
+
+ cvDestroyWindow(WINDOW_NAME);
+}
+
diff --git a/vision/vision.gyp b/vision/vision.gyp
new file mode 100644
index 0000000..81e5562
--- /dev/null
+++ b/vision/vision.gyp
@@ -0,0 +1,38 @@
+{
+ 'targets': [
+ {
+ 'target_name': 'OpenCVWorkTask',
+ 'type': 'executable',
+ 'sources': [
+ 'OpenCVWorkTask.cpp',
+ 'CameraProcessor.cpp',
+ #'BinaryServer.cpp',
+ #'PacketNotifier.cpp',
+ 'JPEGRoutines.cpp',
+ ],
+ 'dependencies': [
+ '<(AOS)/linux_code/linux_code.gyp:init',
+ '<(AOS)/common/common.gyp:time',
+ '<(EXTERNALS):libevent',
+ '<(EXTERNALS):libjpeg',
+ '<(EXTERNALS):opencv',
+ '<(AOS)/linux_code/camera/camera.gyp:buffers',
+ '<(DEPTH)/frc971/queues/queues.gyp:queues',
+ ],
+ },
+ {
+ 'target_name': 'GoalMaster',
+ 'type': 'executable',
+ 'sources': [
+ 'GoalMaster.cpp',
+ 'SensorProcessor.cpp',
+ ],
+ 'dependencies': [
+ '<(AOS)/linux_code/linux_code.gyp:init',
+ '<(AOS)/common/common.gyp:time',
+ '<(DEPTH)/frc971/queues/queues.gyp:queues',
+ '<(AOS)/build/aos.gyp:logging',
+ ],
+ },
+ ],
+}
diff --git a/y2014/actors/actors.gyp b/y2014/actors/actors.gyp
new file mode 100644
index 0000000..48a03d0
--- /dev/null
+++ b/y2014/actors/actors.gyp
@@ -0,0 +1,110 @@
+{
+ 'targets': [
+ {
+ 'target_name': 'binaries',
+ 'type': 'none',
+ 'dependencies': [
+ 'drivetrain_action',
+ 'shoot_action',
+ ],
+ },
+ {
+ 'target_name': 'shoot_action_queue',
+ 'type': 'static_library',
+ 'sources': ['shoot_action.q'],
+ 'variables': {
+ 'header_path': 'y2014/actors',
+ },
+ 'dependencies': [
+ '<(AOS)/common/actions/actions.gyp:action_queue',
+ ],
+ 'export_dependent_settings': [
+ '<(AOS)/common/actions/actions.gyp:action_queue',
+ ],
+ 'includes': ['../../aos/build/queues.gypi'],
+ },
+ {
+ 'target_name': 'shoot_action_lib',
+ 'type': 'static_library',
+ 'sources': [
+ 'shoot_actor.cc',
+ ],
+ 'dependencies': [
+ 'shoot_action_queue',
+ '<(AOS)/common/actions/actions.gyp:action_lib',
+ '<(DEPTH)/y2014/queues/queues.gyp:profile_params',
+ '<(AOS)/build/aos.gyp:logging',
+ '<(DEPTH)/y2014/control_loops/shooter/shooter.gyp:shooter_queue',
+ '<(DEPTH)/y2014/control_loops/claw/claw.gyp:claw_queue',
+ '<(DEPTH)/y2014/control_loops/drivetrain/drivetrain.gyp:drivetrain_queue',
+ '<(DEPTH)/y2014/y2014.gyp:constants',
+ ],
+ 'export_dependent_settings': [
+ 'shoot_action_queue',
+ '<(AOS)/common/actions/actions.gyp:action_lib',
+ ],
+ },
+ {
+ 'target_name': 'shoot_action',
+ 'type': 'executable',
+ 'sources': [
+ 'shoot_actor_main.cc',
+ ],
+ 'dependencies': [
+ '<(AOS)/linux_code/linux_code.gyp:init',
+ 'shoot_action_lib',
+ 'shoot_action_queue',
+ ],
+ },
+ {
+ 'target_name': 'drivetrain_action_queue',
+ 'type': 'static_library',
+ 'sources': ['drivetrain_action.q'],
+ 'variables': {
+ 'header_path': 'y2014/actors',
+ },
+ 'dependencies': [
+ '<(AOS)/common/actions/actions.gyp:action_queue',
+ ],
+ 'export_dependent_settings': [
+ '<(AOS)/common/actions/actions.gyp:action_queue',
+ ],
+ 'includes': ['../../aos/build/queues.gypi'],
+ },
+ {
+ 'target_name': 'drivetrain_action_lib',
+ 'type': 'static_library',
+ 'sources': [
+ 'drivetrain_actor.cc',
+ ],
+ 'dependencies': [
+ 'drivetrain_action_queue',
+ '<(DEPTH)/y2014/y2014.gyp:constants',
+ '<(AOS)/common/common.gyp:time',
+ '<(AOS)/common/util/util.gyp:phased_loop',
+ '<(AOS)/build/aos.gyp:logging',
+ '<(AOS)/common/actions/actions.gyp:action_lib',
+ '<(AOS)/common/logging/logging.gyp:queue_logging',
+ '<(EXTERNALS):eigen',
+ '<(AOS)/common/util/util.gyp:trapezoid_profile',
+ '<(DEPTH)/y2014/control_loops/drivetrain/drivetrain.gyp:drivetrain_queue',
+ ],
+ 'export_dependent_settings': [
+ '<(AOS)/common/actions/actions.gyp:action_lib',
+ 'drivetrain_action_queue',
+ ],
+ },
+ {
+ 'target_name': 'drivetrain_action',
+ 'type': 'executable',
+ 'sources': [
+ 'drivetrain_actor_main.cc',
+ ],
+ 'dependencies': [
+ '<(AOS)/linux_code/linux_code.gyp:init',
+ 'drivetrain_action_lib',
+ 'drivetrain_action_queue',
+ ],
+ },
+ ],
+}
diff --git a/y2014/actors/drivetrain_action.q b/y2014/actors/drivetrain_action.q
new file mode 100644
index 0000000..9ad55d3
--- /dev/null
+++ b/y2014/actors/drivetrain_action.q
@@ -0,0 +1,29 @@
+package frc971.actors;
+
+import "aos/common/actions/actions.q";
+
+// Parameters to send with start.
+struct DrivetrainActionParams {
+ double left_initial_position;
+ double right_initial_position;
+ double y_offset;
+ double theta_offset;
+ double maximum_velocity;
+ double maximum_acceleration;
+ double maximum_turn_velocity;
+ double maximum_turn_acceleration;
+};
+
+queue_group DrivetrainActionQueueGroup {
+ implements aos.common.actions.ActionQueueGroup;
+
+ message Goal {
+ uint32_t run;
+ DrivetrainActionParams params;
+ };
+
+ queue Goal goal;
+ queue aos.common.actions.Status status;
+};
+
+queue_group DrivetrainActionQueueGroup drivetrain_action;
diff --git a/y2014/actors/drivetrain_actor.cc b/y2014/actors/drivetrain_actor.cc
new file mode 100644
index 0000000..3cf86bd
--- /dev/null
+++ b/y2014/actors/drivetrain_actor.cc
@@ -0,0 +1,168 @@
+#include "y2014/actors/drivetrain_actor.h"
+
+#include <functional>
+#include <numeric>
+
+#include <Eigen/Dense>
+
+#include "aos/common/util/phased_loop.h"
+#include "aos/common/logging/logging.h"
+#include "aos/common/util/trapezoid_profile.h"
+#include "aos/common/commonmath.h"
+#include "aos/common/time.h"
+
+#include "y2014/actors/drivetrain_actor.h"
+#include "y2014/constants.h"
+#include "y2014/control_loops/drivetrain/drivetrain.q.h"
+
+namespace frc971 {
+namespace actors {
+
+DrivetrainActor::DrivetrainActor(actors::DrivetrainActionQueueGroup* s)
+ : aos::common::actions::ActorBase<actors::DrivetrainActionQueueGroup>(s) {}
+
+bool DrivetrainActor::RunAction(const actors::DrivetrainActionParams ¶ms) {
+ static const auto K = constants::GetValues().make_drivetrain_loop().K();
+
+ const double yoffset = params.y_offset;
+ const double turn_offset =
+ params.theta_offset * constants::GetValues().turn_width / 2.0;
+ LOG(INFO, "Going to move %f and turn %f\n", yoffset, turn_offset);
+
+ // Measured conversion to get the distance right.
+ ::aos::util::TrapezoidProfile profile(::aos::time::Time::InMS(5));
+ ::aos::util::TrapezoidProfile turn_profile(::aos::time::Time::InMS(5));
+ const double goal_velocity = 0.0;
+ const double epsilon = 0.01;
+ ::Eigen::Matrix<double, 2, 1> left_goal_state, right_goal_state;
+
+ profile.set_maximum_acceleration(params.maximum_acceleration);
+ profile.set_maximum_velocity(params.maximum_velocity);
+ turn_profile.set_maximum_acceleration(params.maximum_turn_acceleration *
+ constants::GetValues().turn_width /
+ 2.0);
+ turn_profile.set_maximum_velocity(params.maximum_turn_velocity *
+ constants::GetValues().turn_width / 2.0);
+
+ while (true) {
+ ::aos::time::PhasedLoopXMS(10, 5000);
+
+ control_loops::drivetrain_queue.status.FetchLatest();
+ if (control_loops::drivetrain_queue.status.get()) {
+ const auto& status = *control_loops::drivetrain_queue.status;
+ if (::std::abs(status.uncapped_left_voltage -
+ status.uncapped_right_voltage) > 24) {
+ LOG(DEBUG, "spinning in place\n");
+ // They're more than 24V apart, so stop moving forwards and let it deal
+ // with spinning first.
+ profile.SetGoal(
+ (status.filtered_left_position + status.filtered_right_position -
+ params.left_initial_position - params.right_initial_position) /
+ 2.0);
+ } else {
+ static const double divisor = K(0, 0) + K(0, 2);
+ double dx_left, dx_right;
+ if (status.uncapped_left_voltage > 12.0) {
+ dx_left = (status.uncapped_left_voltage - 12.0) / divisor;
+ } else if (status.uncapped_left_voltage < -12.0) {
+ dx_left = (status.uncapped_left_voltage + 12.0) / divisor;
+ } else {
+ dx_left = 0;
+ }
+ if (status.uncapped_right_voltage > 12.0) {
+ dx_right = (status.uncapped_right_voltage - 12.0) / divisor;
+ } else if (status.uncapped_right_voltage < -12.0) {
+ dx_right = (status.uncapped_right_voltage + 12.0) / divisor;
+ } else {
+ dx_right = 0;
+ }
+ double dx;
+ if (dx_left == 0 && dx_right == 0) {
+ dx = 0;
+ } else if (dx_left != 0 && dx_right != 0 &&
+ ::aos::sign(dx_left) != ::aos::sign(dx_right)) {
+ // Both saturating in opposite directions. Don't do anything.
+ LOG(DEBUG, "Saturating opposite ways, not adjusting\n");
+ dx = 0;
+ } else if (::std::abs(dx_left) > ::std::abs(dx_right)) {
+ dx = dx_left;
+ } else {
+ dx = dx_right;
+ }
+ if (dx != 0) {
+ LOG(DEBUG, "adjusting goal by %f\n", dx);
+ profile.MoveGoal(-dx);
+ }
+ }
+ } else {
+ // If we ever get here, that's bad and we should just give up
+ LOG(ERROR, "no drivetrain status!\n");
+ return false;
+ }
+
+ const auto drive_profile_goal_state =
+ profile.Update(yoffset, goal_velocity);
+ const auto turn_profile_goal_state = turn_profile.Update(turn_offset, 0.0);
+ left_goal_state = drive_profile_goal_state - turn_profile_goal_state;
+ right_goal_state = drive_profile_goal_state + turn_profile_goal_state;
+
+ if (::std::abs(drive_profile_goal_state(0, 0) - yoffset) < epsilon &&
+ ::std::abs(turn_profile_goal_state(0, 0) - turn_offset) < epsilon) {
+ break;
+ }
+
+ if (ShouldCancel()) return true;
+
+ LOG(DEBUG, "Driving left to %f, right to %f\n",
+ left_goal_state(0, 0) + params.left_initial_position,
+ right_goal_state(0, 0) + params.right_initial_position);
+ control_loops::drivetrain_queue.goal.MakeWithBuilder()
+ .control_loop_driving(true)
+ //.highgear(false)
+ .left_goal(left_goal_state(0, 0) + params.left_initial_position)
+ .right_goal(right_goal_state(0, 0) + params.right_initial_position)
+ .left_velocity_goal(left_goal_state(1, 0))
+ .right_velocity_goal(right_goal_state(1, 0))
+ .Send();
+ }
+ if (ShouldCancel()) return true;
+ control_loops::drivetrain_queue.status.FetchLatest();
+ while (!control_loops::drivetrain_queue.status.get()) {
+ LOG(WARNING,
+ "No previous drivetrain status packet, trying to fetch again\n");
+ control_loops::drivetrain_queue.status.FetchNextBlocking();
+ if (ShouldCancel()) return true;
+ }
+ while (true) {
+ if (ShouldCancel()) return true;
+ const double kPositionThreshold = 0.05;
+
+ const double left_error = ::std::abs(
+ control_loops::drivetrain_queue.status->filtered_left_position -
+ (left_goal_state(0, 0) + params.left_initial_position));
+ const double right_error = ::std::abs(
+ control_loops::drivetrain_queue.status->filtered_right_position -
+ (right_goal_state(0, 0) + params.right_initial_position));
+ const double velocity_error =
+ ::std::abs(control_loops::drivetrain_queue.status->robot_speed);
+ if (left_error < kPositionThreshold && right_error < kPositionThreshold &&
+ velocity_error < 0.2) {
+ break;
+ } else {
+ LOG(DEBUG, "Drivetrain error is %f, %f, %f\n", left_error, right_error,
+ velocity_error);
+ }
+ control_loops::drivetrain_queue.status.FetchNextBlocking();
+ }
+ LOG(INFO, "Done moving\n");
+ return true;
+}
+
+::std::unique_ptr<DrivetrainAction> MakeDrivetrainAction(
+ const ::frc971::actors::DrivetrainActionParams& params) {
+ return ::std::unique_ptr<DrivetrainAction>(
+ new DrivetrainAction(&::frc971::actors::drivetrain_action, params));
+}
+
+} // namespace actors
+} // namespace frc971
diff --git a/y2014/actors/drivetrain_actor.h b/y2014/actors/drivetrain_actor.h
new file mode 100644
index 0000000..583121b
--- /dev/null
+++ b/y2014/actors/drivetrain_actor.h
@@ -0,0 +1,32 @@
+#ifndef Y2014_ACTIONS_DRIVETRAIN_ACTION_H_
+#define Y2014_ACTIONS_DRIVETRAIN_ACTION_H_
+
+#include <memory>
+
+#include "aos/common/actions/actor.h"
+#include "aos/common/actions/actions.h"
+
+#include "y2014/actors/drivetrain_action.q.h"
+
+namespace frc971 {
+namespace actors {
+
+class DrivetrainActor
+ : public ::aos::common::actions::ActorBase<DrivetrainActionQueueGroup> {
+ public:
+ explicit DrivetrainActor(DrivetrainActionQueueGroup* s);
+
+ bool RunAction(const actors::DrivetrainActionParams ¶ms) override;
+};
+
+typedef ::aos::common::actions::TypedAction<DrivetrainActionQueueGroup>
+ DrivetrainAction;
+
+// Makes a new DrivetrainActor action.
+::std::unique_ptr<DrivetrainAction> MakeDrivetrainAction(
+ const ::frc971::actors::DrivetrainActionParams& params);
+
+} // namespace actors
+} // namespace frc971
+
+#endif
diff --git a/y2014/actors/drivetrain_actor_main.cc b/y2014/actors/drivetrain_actor_main.cc
new file mode 100644
index 0000000..375e71c
--- /dev/null
+++ b/y2014/actors/drivetrain_actor_main.cc
@@ -0,0 +1,18 @@
+#include <stdio.h>
+
+#include "aos/linux_code/init.h"
+#include "y2014/actors/drivetrain_action.q.h"
+#include "y2014/actors/drivetrain_actor.h"
+
+using ::aos::time::Time;
+
+int main(int /*argc*/, char * /*argv*/[]) {
+ ::aos::Init();
+
+ frc971::actors::DrivetrainActor drivetrain(
+ &::frc971::actors::drivetrain_action);
+ drivetrain.Run();
+
+ ::aos::Cleanup();
+ return 0;
+}
diff --git a/y2014/actors/shoot_action.q b/y2014/actors/shoot_action.q
new file mode 100644
index 0000000..1c545aa
--- /dev/null
+++ b/y2014/actors/shoot_action.q
@@ -0,0 +1,12 @@
+package frc971.actors;
+
+import "aos/common/actions/actions.q";
+
+queue_group ShootActionQueueGroup {
+ implements frc971.actions.ActionQueueGroup;
+
+ queue aos.common.actions.Goal goal;
+ queue aos.common.actions.Status status;
+};
+
+queue_group ShootActionQueueGroup shoot_action;
diff --git a/y2014/actors/shoot_actor.cc b/y2014/actors/shoot_actor.cc
new file mode 100644
index 0000000..9db34c4
--- /dev/null
+++ b/y2014/actors/shoot_actor.cc
@@ -0,0 +1,178 @@
+#include "y2014/actors/shoot_actor.h"
+
+#include <functional>
+
+#include "aos/common/logging/logging.h"
+
+#include "y2014/control_loops/shooter/shooter.q.h"
+#include "y2014/control_loops/claw/claw.q.h"
+#include "y2014/constants.h"
+#include "y2014/control_loops/drivetrain/drivetrain.q.h"
+
+namespace frc971 {
+namespace actors {
+namespace {
+
+bool IntakeOff() {
+ control_loops::claw_queue.goal.FetchLatest();
+ if (!control_loops::claw_queue.goal.get()) {
+ LOG(WARNING, "no claw goal\n");
+ // If it doesn't have a goal, then the intake isn't on so we don't have to
+ // turn it off.
+ return true;
+ } else {
+ if (!control_loops::claw_queue.goal.MakeWithBuilder()
+ .bottom_angle(control_loops::claw_queue.goal->bottom_angle)
+ .separation_angle(control_loops::claw_queue.goal->separation_angle)
+ .intake(0.0)
+ .centering(0.0)
+ .Send()) {
+ LOG(WARNING, "sending claw goal failed\n");
+ return false;
+ }
+ }
+ return true;
+}
+
+} // namespace
+
+constexpr double ShootActor::kOffsetRadians;
+constexpr double ShootActor::kClawShootingSeparation;
+constexpr double ShootActor::kClawShootingSeparationGoal;
+
+ShootActor::ShootActor(actors::ShootActionQueueGroup* s)
+ : ::aos::common::actions::ActorBase<actors::ShootActionQueueGroup>(s) {}
+
+double ShootActor::SpeedToAngleOffset(double speed) {
+ const frc971::constants::Values& values = frc971::constants::GetValues();
+ // scale speed to a [0.0-1.0] on something close to the max
+ return (speed / values.drivetrain_max_speed) * kOffsetRadians;
+}
+
+bool ShootActor::RunAction(const double&) {
+ InnerRunAction();
+
+ // Now do our 'finally' block and make sure that we aren't requesting shots
+ // continually.
+ control_loops::shooter_queue.goal.FetchLatest();
+ if (control_loops::shooter_queue.goal.get() == nullptr) {
+ return true;
+ }
+ if (!control_loops::shooter_queue.goal.MakeWithBuilder()
+ .shot_power(control_loops::shooter_queue.goal->shot_power)
+ .shot_requested(false)
+ .unload_requested(false)
+ .load_requested(false)
+ .Send()) {
+ LOG(WARNING, "sending shooter goal failed\n");
+ return false;
+ }
+
+ LOG(INFO, "finished\n");
+ return true;
+}
+
+void ShootActor::InnerRunAction() {
+ LOG(INFO, "Shooting at the current angle and power.\n");
+
+ // wait for claw to be ready
+ if (WaitUntil(::std::bind(&ShootActor::DoneSetupShot, this))) {
+ return;
+ }
+
+ if (!IntakeOff()) return;
+
+ // Make sure that we have the latest shooter status.
+ control_loops::shooter_queue.status.FetchLatest();
+ // Get the number of shots fired up to this point. This should not be updated
+ // again for another few cycles.
+ previous_shots_ = control_loops::shooter_queue.status->shots;
+ // Shoot!
+ if (!control_loops::shooter_queue.goal.MakeWithBuilder()
+ .shot_power(control_loops::shooter_queue.goal->shot_power)
+ .shot_requested(true)
+ .unload_requested(false)
+ .load_requested(false)
+ .Send()) {
+ LOG(WARNING, "sending shooter goal failed\n");
+ return;
+ }
+
+ // wait for record of shot having been fired
+ if (WaitUntil(::std::bind(&ShootActor::DoneShot, this))) return;
+
+ if (!IntakeOff()) return;
+}
+
+bool ClawIsReady() {
+ control_loops::claw_queue.goal.FetchLatest();
+
+ bool ans =
+ control_loops::claw_queue.status->zeroed &&
+ (::std::abs(control_loops::claw_queue.status->bottom_velocity) < 0.5) &&
+ (::std::abs(control_loops::claw_queue.status->bottom -
+ control_loops::claw_queue.goal->bottom_angle) < 0.10) &&
+ (::std::abs(control_loops::claw_queue.status->separation -
+ control_loops::claw_queue.goal->separation_angle) < 0.4);
+ LOG(DEBUG, "Claw is %sready zeroed %d bottom_velocity %f bottom %f sep %f\n",
+ ans ? "" : "not ", control_loops::claw_queue.status->zeroed,
+ ::std::abs(control_loops::claw_queue.status->bottom_velocity),
+ ::std::abs(control_loops::claw_queue.status->bottom -
+ control_loops::claw_queue.goal->bottom_angle),
+ ::std::abs(control_loops::claw_queue.status->separation -
+ control_loops::claw_queue.goal->separation_angle));
+ return ans;
+}
+
+bool ShooterIsReady() {
+ control_loops::shooter_queue.goal.FetchLatest();
+
+ LOG(DEBUG, "Power error is %f - %f -> %f, ready %d\n",
+ control_loops::shooter_queue.status->hard_stop_power,
+ control_loops::shooter_queue.goal->shot_power,
+ ::std::abs(control_loops::shooter_queue.status->hard_stop_power -
+ control_loops::shooter_queue.goal->shot_power),
+ control_loops::shooter_queue.status->ready);
+ return (::std::abs(control_loops::shooter_queue.status->hard_stop_power -
+ control_loops::shooter_queue.goal->shot_power) < 1.0) &&
+ control_loops::shooter_queue.status->ready;
+}
+
+bool ShootActor::DoneSetupShot() {
+ control_loops::shooter_queue.status.FetchAnother();
+ control_loops::claw_queue.status.FetchAnother();
+ // Make sure that both the shooter and claw have reached the necessary
+ // states.
+ if (ShooterIsReady() && ClawIsReady()) {
+ LOG(INFO, "Claw and Shooter ready for shooting.\n");
+ return true;
+ }
+
+ return false;
+}
+
+bool ShootActor::DonePreShotOpen() {
+ control_loops::claw_queue.status.FetchAnother();
+ if (control_loops::claw_queue.status->separation > kClawShootingSeparation) {
+ LOG(INFO, "Opened up enough to shoot.\n");
+ return true;
+ }
+ return false;
+}
+
+bool ShootActor::DoneShot() {
+ control_loops::shooter_queue.status.FetchAnother();
+ if (control_loops::shooter_queue.status->shots > previous_shots_) {
+ LOG(INFO, "Shot succeeded!\n");
+ return true;
+ }
+ return false;
+}
+
+::std::unique_ptr<ShootAction> MakeShootAction() {
+ return ::std::unique_ptr<ShootAction>(
+ new ShootAction(&::frc971::actors::shoot_action, 0.0));
+}
+
+} // namespace actors
+} // namespace frc971
diff --git a/y2014/actors/shoot_actor.h b/y2014/actors/shoot_actor.h
new file mode 100644
index 0000000..3adfe31
--- /dev/null
+++ b/y2014/actors/shoot_actor.h
@@ -0,0 +1,52 @@
+#ifndef Y2014_ACTORS_SHOOT_ACTOR_H_
+#define Y2014_ACTORS_SHOOT_ACTOR_H_
+
+#include <memory>
+
+#include "aos/common/actions/actor.h"
+#include "aos/common/actions/actions.h"
+
+#include "y2014/actors/shoot_action.q.h"
+
+namespace frc971 {
+namespace actors {
+
+class ShootActor
+ : public ::aos::common::actions::ActorBase<actors::ShootActionQueueGroup> {
+ public:
+ explicit ShootActor(actors::ShootActionQueueGroup* s);
+
+ // Actually execute the action of moving the claw and shooter into position
+ // and actually firing them.
+ bool RunAction(const double ¶ms) override;
+ void InnerRunAction();
+
+ // calc an offset to our requested shot based on robot speed
+ double SpeedToAngleOffset(double speed);
+
+ static constexpr double kOffsetRadians = 0.4;
+ static constexpr double kClawShootingSeparation = 0.10;
+ static constexpr double kClawShootingSeparationGoal = 0.10;
+
+ protected:
+ // completed shot
+ bool DoneShot();
+ // ready for shot
+ bool DonePreShotOpen();
+ // in the right place
+ bool DoneSetupShot();
+
+ // to track when shot is complete
+ int previous_shots_;
+};
+
+typedef ::aos::common::actions::TypedAction<actors::ShootActionQueueGroup>
+ ShootAction;
+
+// Makes a new ShootActor action.
+::std::unique_ptr<ShootAction> MakeShootAction();
+
+} // namespace actors
+} // namespace frc971
+
+#endif // Y2014_ACTORS_SHOOT_ACTOR_H_
diff --git a/y2014/actors/shoot_actor_main.cc b/y2014/actors/shoot_actor_main.cc
new file mode 100644
index 0000000..efa4377
--- /dev/null
+++ b/y2014/actors/shoot_actor_main.cc
@@ -0,0 +1,18 @@
+#include <stdio.h>
+
+#include "aos/linux_code/init.h"
+#include "y2014/actors/shoot_action.q.h"
+#include "y2014/actors/shoot_actor.h"
+
+using ::aos::time::Time;
+
+int main(int /*argc*/, char * /*argv*/[]) {
+ ::aos::Init();
+
+ frc971::actors::ShootActor shoot(&::frc971::actors::shoot_action);
+ shoot.Run();
+
+ ::aos::Cleanup();
+ return 0;
+}
+
diff --git a/y2014/autonomous/auto.cc b/y2014/autonomous/auto.cc
new file mode 100644
index 0000000..ab28323
--- /dev/null
+++ b/y2014/autonomous/auto.cc
@@ -0,0 +1,530 @@
+#include <stdio.h>
+
+#include <memory>
+
+#include "aos/common/util/phased_loop.h"
+#include "aos/common/time.h"
+#include "aos/common/util/trapezoid_profile.h"
+#include "aos/common/logging/logging.h"
+#include "aos/common/logging/queue_logging.h"
+#include "aos/common/actions/actions.h"
+
+#include "frc971/autonomous/auto.q.h"
+#include "y2014/constants.h"
+#include "y2014/control_loops/drivetrain/drivetrain.q.h"
+#include "y2014/control_loops/shooter/shooter.q.h"
+#include "y2014/control_loops/claw/claw.q.h"
+#include "y2014/actors/shoot_actor.h"
+#include "y2014/actors/drivetrain_actor.h"
+#include "y2014/queues/auto_mode.q.h"
+
+#include "y2014/queues/hot_goal.q.h"
+#include "y2014/queues/profile_params.q.h"
+
+using ::aos::time::Time;
+
+namespace frc971 {
+namespace autonomous {
+
+namespace time = ::aos::time;
+
+static double left_initial_position, right_initial_position;
+
+bool ShouldExitAuto() {
+ ::frc971::autonomous::autonomous.FetchLatest();
+ bool ans = !::frc971::autonomous::autonomous->run_auto;
+ if (ans) {
+ LOG(INFO, "Time to exit auto mode\n");
+ }
+ return ans;
+}
+
+void StopDrivetrain() {
+ LOG(INFO, "Stopping the drivetrain\n");
+ control_loops::drivetrain_queue.goal.MakeWithBuilder()
+ .control_loop_driving(true)
+ .left_goal(left_initial_position)
+ .left_velocity_goal(0)
+ .right_goal(right_initial_position)
+ .right_velocity_goal(0)
+ .quickturn(false)
+ .Send();
+}
+
+void ResetDrivetrain() {
+ LOG(INFO, "resetting the drivetrain\n");
+ control_loops::drivetrain_queue.goal.MakeWithBuilder()
+ .control_loop_driving(false)
+ .highgear(false)
+ .steering(0.0)
+ .throttle(0.0)
+ .left_goal(left_initial_position)
+ .left_velocity_goal(0)
+ .right_goal(right_initial_position)
+ .right_velocity_goal(0)
+ .Send();
+}
+
+void WaitUntilDoneOrCanceled(
+ ::std::unique_ptr<aos::common::actions::Action> action) {
+ if (!action) {
+ LOG(ERROR, "No action, not waiting\n");
+ return;
+ }
+ while (true) {
+ // Poll the running bit and auto done bits.
+ ::aos::time::PhasedLoopXMS(10, 5000);
+ if (!action->Running() || ShouldExitAuto()) {
+ return;
+ }
+ }
+}
+
+void StepDrive(double distance, double theta) {
+ double left_goal = (left_initial_position + distance -
+ theta * constants::GetValues().turn_width / 2.0);
+ double right_goal = (right_initial_position + distance +
+ theta * constants::GetValues().turn_width / 2.0);
+ control_loops::drivetrain_queue.goal.MakeWithBuilder()
+ .control_loop_driving(true)
+ .left_goal(left_goal)
+ .right_goal(right_goal)
+ .left_velocity_goal(0.0)
+ .right_velocity_goal(0.0)
+ .Send();
+ left_initial_position = left_goal;
+ right_initial_position = right_goal;
+}
+
+void PositionClawVertically(double intake_power = 0.0, double centering_power = 0.0) {
+ if (!control_loops::claw_queue.goal.MakeWithBuilder()
+ .bottom_angle(0.0)
+ .separation_angle(0.0)
+ .intake(intake_power)
+ .centering(centering_power)
+ .Send()) {
+ LOG(WARNING, "sending claw goal failed\n");
+ }
+}
+
+void PositionClawBackIntake() {
+ if (!control_loops::claw_queue.goal.MakeWithBuilder()
+ .bottom_angle(-2.273474)
+ .separation_angle(0.0)
+ .intake(12.0)
+ .centering(12.0)
+ .Send()) {
+ LOG(WARNING, "sending claw goal failed\n");
+ }
+}
+
+void PositionClawUpClosed() {
+ // Move the claw to where we're going to shoot from but keep it closed until
+ // it gets there.
+ if (!control_loops::claw_queue.goal.MakeWithBuilder()
+ .bottom_angle(0.86)
+ .separation_angle(0.0)
+ .intake(4.0)
+ .centering(1.0)
+ .Send()) {
+ LOG(WARNING, "sending claw goal failed\n");
+ }
+}
+
+void PositionClawForShot() {
+ if (!control_loops::claw_queue.goal.MakeWithBuilder()
+ .bottom_angle(0.86)
+ .separation_angle(0.10)
+ .intake(4.0)
+ .centering(1.0)
+ .Send()) {
+ LOG(WARNING, "sending claw goal failed\n");
+ }
+}
+
+void SetShotPower(double power) {
+ LOG(INFO, "Setting shot power to %f\n", power);
+ if (!control_loops::shooter_queue.goal.MakeWithBuilder()
+ .shot_power(power)
+ .shot_requested(false)
+ .unload_requested(false)
+ .load_requested(false)
+ .Send()) {
+ LOG(WARNING, "sending shooter goal failed\n");
+ }
+}
+
+void WaitUntilNear(double distance) {
+ while (true) {
+ if (ShouldExitAuto()) return;
+ control_loops::drivetrain_queue.status.FetchAnother();
+ double left_error = ::std::abs(
+ left_initial_position -
+ control_loops::drivetrain_queue.status->filtered_left_position);
+ double right_error = ::std::abs(
+ right_initial_position -
+ control_loops::drivetrain_queue.status->filtered_right_position);
+ const double kPositionThreshold = 0.05 + distance;
+ if (right_error < kPositionThreshold && left_error < kPositionThreshold) {
+ LOG(INFO, "At the goal\n");
+ return;
+ }
+ }
+}
+
+const ProfileParams kFastDrive = {3.0, 2.5};
+const ProfileParams kSlowDrive = {2.5, 2.5};
+const ProfileParams kFastWithBallDrive = {3.0, 2.0};
+const ProfileParams kSlowWithBallDrive = {2.5, 2.0};
+const ProfileParams kFastTurn = {3.0, 10.0};
+
+::std::unique_ptr<::frc971::actors::DrivetrainAction> SetDriveGoal(
+ double distance, const ProfileParams drive_params, double theta = 0,
+ const ProfileParams &turn_params = kFastTurn) {
+ LOG(INFO, "Driving to %f\n", distance);
+
+ ::frc971::actors::DrivetrainActionParams params;
+ params.left_initial_position = left_initial_position;
+ params.right_initial_position = right_initial_position;
+ params.y_offset = distance;
+ params.theta_offset = theta;
+ params.maximum_turn_acceleration = turn_params.acceleration;
+ params.maximum_turn_velocity = turn_params.velocity;
+ params.maximum_velocity = drive_params.velocity;
+ params.maximum_acceleration = drive_params.acceleration;
+ auto drivetrain_action = actors::MakeDrivetrainAction(params);
+ drivetrain_action->Start();
+ left_initial_position +=
+ distance - theta * constants::GetValues().turn_width / 2.0;
+ right_initial_position +=
+ distance + theta * constants::GetValues().turn_width / 2.0;
+ return ::std::move(drivetrain_action);
+}
+
+void Shoot() {
+ // Shoot.
+ auto shoot_action = actors::MakeShootAction();
+ shoot_action->Start();
+ WaitUntilDoneOrCanceled(::std::move(shoot_action));
+}
+
+void InitializeEncoders() {
+ control_loops::drivetrain_queue.status.FetchAnother();
+ left_initial_position =
+ control_loops::drivetrain_queue.status->filtered_left_position;
+ right_initial_position =
+ control_loops::drivetrain_queue.status->filtered_right_position;
+}
+
+void WaitUntilClawDone() {
+ while (true) {
+ // Poll the running bit and auto done bits.
+ ::aos::time::PhasedLoopXMS(10, 5000);
+ control_loops::claw_queue.status.FetchLatest();
+ control_loops::claw_queue.goal.FetchLatest();
+ if (ShouldExitAuto()) {
+ return;
+ }
+ if (control_loops::claw_queue.status.get() == nullptr ||
+ control_loops::claw_queue.goal.get() == nullptr) {
+ continue;
+ }
+ bool ans =
+ control_loops::claw_queue.status->zeroed &&
+ (::std::abs(control_loops::claw_queue.status->bottom_velocity) <
+ 1.0) &&
+ (::std::abs(control_loops::claw_queue.status->bottom -
+ control_loops::claw_queue.goal->bottom_angle) <
+ 0.10) &&
+ (::std::abs(control_loops::claw_queue.status->separation -
+ control_loops::claw_queue.goal->separation_angle) <
+ 0.4);
+ if (ans) {
+ return;
+ }
+ if (ShouldExitAuto()) return;
+ }
+}
+
+class HotGoalDecoder {
+ public:
+ HotGoalDecoder() {
+ ResetCounts();
+ }
+
+ void ResetCounts() {
+ hot_goal.FetchLatest();
+ if (hot_goal.get()) {
+ start_counts_ = *hot_goal;
+ LOG_STRUCT(INFO, "counts reset to", start_counts_);
+ start_counts_valid_ = true;
+ } else {
+ LOG(WARNING, "no hot goal message. ignoring\n");
+ start_counts_valid_ = false;
+ }
+ }
+
+ void Update(bool block = false) {
+ if (block) {
+ hot_goal.FetchAnother();
+ } else {
+ hot_goal.FetchLatest();
+ }
+ if (hot_goal.get()) LOG_STRUCT(INFO, "new counts", *hot_goal);
+ }
+
+ bool left_triggered() const {
+ if (!start_counts_valid_ || !hot_goal.get()) return false;
+ return (hot_goal->left_count - start_counts_.left_count) > kThreshold;
+ }
+
+ bool right_triggered() const {
+ if (!start_counts_valid_ || !hot_goal.get()) return false;
+ return (hot_goal->right_count - start_counts_.right_count) > kThreshold;
+ }
+
+ bool is_left() const {
+ if (!start_counts_valid_ || !hot_goal.get()) return false;
+ const uint64_t left_difference =
+ hot_goal->left_count - start_counts_.left_count;
+ const uint64_t right_difference =
+ hot_goal->right_count - start_counts_.right_count;
+ if (left_difference > kThreshold) {
+ if (right_difference > kThreshold) {
+ // We've seen a lot of both, so pick the one we've seen the most of.
+ return left_difference > right_difference;
+ } else {
+ // We've seen enough left but not enough right, so go with it.
+ return true;
+ }
+ } else {
+ // We haven't seen enough left, so it's not left.
+ return false;
+ }
+ }
+
+ bool is_right() const {
+ if (!start_counts_valid_ || !hot_goal.get()) return false;
+ const uint64_t left_difference =
+ hot_goal->left_count - start_counts_.left_count;
+ const uint64_t right_difference =
+ hot_goal->right_count - start_counts_.right_count;
+ if (right_difference > kThreshold) {
+ if (left_difference > kThreshold) {
+ // We've seen a lot of both, so pick the one we've seen the most of.
+ return right_difference > left_difference;
+ } else {
+ // We've seen enough right but not enough left, so go with it.
+ return true;
+ }
+ } else {
+ // We haven't seen enough right, so it's not right.
+ return false;
+ }
+ }
+
+ private:
+ static const uint64_t kThreshold = 5;
+
+ ::frc971::HotGoal start_counts_;
+ bool start_counts_valid_;
+};
+
+void HandleAuto() {
+ enum class AutoVersion : uint8_t {
+ kStraight,
+ kDoubleHot,
+ kSingleHot,
+ };
+
+ // The front of the robot is 1.854 meters from the wall
+ static const double kShootDistance = 3.15;
+ static const double kPickupDistance = 0.5;
+ static const double kTurnAngle = 0.3;
+
+ ::aos::time::Time start_time = ::aos::time::Time::Now();
+ LOG(INFO, "Handling auto mode\n");
+
+ AutoVersion auto_version;
+ ::frc971::sensors::auto_mode.FetchLatest();
+ if (!::frc971::sensors::auto_mode.get()) {
+ LOG(WARNING, "not sure which auto mode to use\n");
+ auto_version = AutoVersion::kStraight;
+ } else {
+ static const double kSelectorMin = 0.2, kSelectorMax = 4.4;
+
+ const double kSelectorStep = (kSelectorMax - kSelectorMin) / 3.0;
+ if (::frc971::sensors::auto_mode->voltage < kSelectorStep + kSelectorMin) {
+ auto_version = AutoVersion::kSingleHot;
+ } else if (::frc971::sensors::auto_mode->voltage <
+ 2 * kSelectorStep + kSelectorMin) {
+ auto_version = AutoVersion::kStraight;
+ } else {
+ auto_version = AutoVersion::kDoubleHot;
+ }
+ }
+ LOG(INFO, "running auto %" PRIu8 "\n", static_cast<uint8_t>(auto_version));
+
+ const ProfileParams &drive_params =
+ (auto_version == AutoVersion::kStraight) ? kFastDrive : kSlowDrive;
+ const ProfileParams &drive_with_ball_params =
+ (auto_version == AutoVersion::kStraight) ? kFastWithBallDrive
+ : kSlowWithBallDrive;
+
+ HotGoalDecoder hot_goal_decoder;
+ // True for left, false for right.
+ bool first_shot_left, second_shot_left_default, second_shot_left;
+
+ ResetDrivetrain();
+
+ time::SleepFor(time::Time::InSeconds(0.1));
+ if (ShouldExitAuto()) return;
+ InitializeEncoders();
+
+ // Turn the claw on, keep it straight up until the ball has been grabbed.
+ LOG(INFO, "Claw going up at %f\n",
+ (::aos::time::Time::Now() - start_time).ToSeconds());
+ PositionClawVertically(12.0, 4.0);
+ SetShotPower(115.0);
+
+ // Wait for the ball to enter the claw.
+ time::SleepFor(time::Time::InSeconds(0.25));
+ if (ShouldExitAuto()) return;
+ LOG(INFO, "Readying claw for shot at %f\n",
+ (::aos::time::Time::Now() - start_time).ToSeconds());
+
+ {
+ if (ShouldExitAuto()) return;
+ // Drive to the goal.
+ auto drivetrain_action = SetDriveGoal(-kShootDistance, drive_params);
+ time::SleepFor(time::Time::InSeconds(0.75));
+ PositionClawForShot();
+ LOG(INFO, "Waiting until drivetrain is finished\n");
+ WaitUntilDoneOrCanceled(::std::move(drivetrain_action));
+ if (ShouldExitAuto()) return;
+ }
+
+ hot_goal_decoder.Update();
+ if (hot_goal_decoder.is_left()) {
+ LOG(INFO, "first shot left\n");
+ first_shot_left = true;
+ second_shot_left_default = false;
+ } else if (hot_goal_decoder.is_right()) {
+ LOG(INFO, "first shot right\n");
+ first_shot_left = false;
+ second_shot_left_default = true;
+ } else {
+ LOG(INFO, "first shot defaulting left\n");
+ first_shot_left = true;
+ second_shot_left_default = true;
+ }
+ if (auto_version == AutoVersion::kDoubleHot) {
+ if (ShouldExitAuto()) return;
+ auto drivetrain_action = SetDriveGoal(
+ 0, drive_with_ball_params, first_shot_left ? kTurnAngle : -kTurnAngle);
+ WaitUntilDoneOrCanceled(::std::move(drivetrain_action));
+ if (ShouldExitAuto()) return;
+ } else if (auto_version == AutoVersion::kSingleHot) {
+ do {
+ // TODO(brians): Wait for next message with timeout or something.
+ ::aos::time::SleepFor(::aos::time::Time::InSeconds(0.003));
+ hot_goal_decoder.Update(false);
+ if (ShouldExitAuto()) return;
+ } while (!hot_goal_decoder.left_triggered() &&
+ (::aos::time::Time::Now() - start_time) <
+ ::aos::time::Time::InSeconds(9));
+ } else if (auto_version == AutoVersion::kStraight) {
+ time::SleepFor(time::Time::InSeconds(0.4));
+ }
+
+ // Shoot.
+ LOG(INFO, "Shooting at %f\n",
+ (::aos::time::Time::Now() - start_time).ToSeconds());
+ Shoot();
+ time::SleepFor(time::Time::InSeconds(0.05));
+
+ if (auto_version == AutoVersion::kDoubleHot) {
+ if (ShouldExitAuto()) return;
+ auto drivetrain_action = SetDriveGoal(
+ 0, drive_with_ball_params, first_shot_left ? -kTurnAngle : kTurnAngle);
+ WaitUntilDoneOrCanceled(::std::move(drivetrain_action));
+ if (ShouldExitAuto()) return;
+ } else if (auto_version == AutoVersion::kSingleHot) {
+ LOG(INFO, "auto done at %f\n",
+ (::aos::time::Time::Now() - start_time).ToSeconds());
+ PositionClawVertically(0.0, 0.0);
+ return;
+ }
+
+ {
+ if (ShouldExitAuto()) return;
+ // Intake the new ball.
+ LOG(INFO, "Claw ready for intake at %f\n",
+ (::aos::time::Time::Now() - start_time).ToSeconds());
+ PositionClawBackIntake();
+ auto drivetrain_action =
+ SetDriveGoal(kShootDistance + kPickupDistance, drive_params);
+ LOG(INFO, "Waiting until drivetrain is finished\n");
+ WaitUntilDoneOrCanceled(::std::move(drivetrain_action));
+ if (ShouldExitAuto()) return;
+ LOG(INFO, "Wait for the claw at %f\n",
+ (::aos::time::Time::Now() - start_time).ToSeconds());
+ WaitUntilClawDone();
+ if (ShouldExitAuto()) return;
+ }
+
+ // Drive back.
+ {
+ LOG(INFO, "Driving back at %f\n",
+ (::aos::time::Time::Now() - start_time).ToSeconds());
+ auto drivetrain_action =
+ SetDriveGoal(-(kShootDistance + kPickupDistance), drive_params);
+ time::SleepFor(time::Time::InSeconds(0.3));
+ hot_goal_decoder.ResetCounts();
+ if (ShouldExitAuto()) return;
+ PositionClawUpClosed();
+ WaitUntilClawDone();
+ if (ShouldExitAuto()) return;
+ PositionClawForShot();
+ LOG(INFO, "Waiting until drivetrain is finished\n");
+ WaitUntilDoneOrCanceled(::std::move(drivetrain_action));
+ if (ShouldExitAuto()) return;
+ WaitUntilClawDone();
+ if (ShouldExitAuto()) return;
+ }
+
+ hot_goal_decoder.Update();
+ if (hot_goal_decoder.is_left()) {
+ LOG(INFO, "second shot left\n");
+ second_shot_left = true;
+ } else if (hot_goal_decoder.is_right()) {
+ LOG(INFO, "second shot right\n");
+ second_shot_left = false;
+ } else {
+ LOG(INFO, "second shot defaulting %s\n",
+ second_shot_left_default ? "left" : "right");
+ second_shot_left = second_shot_left_default;
+ }
+ if (auto_version == AutoVersion::kDoubleHot) {
+ if (ShouldExitAuto()) return;
+ auto drivetrain_action = SetDriveGoal(
+ 0, drive_params, second_shot_left ? kTurnAngle : -kTurnAngle);
+ WaitUntilDoneOrCanceled(::std::move(drivetrain_action));
+ if (ShouldExitAuto()) return;
+ } else if (auto_version == AutoVersion::kStraight) {
+ time::SleepFor(time::Time::InSeconds(0.4));
+ }
+
+ LOG(INFO, "Shooting at %f\n",
+ (::aos::time::Time::Now() - start_time).ToSeconds());
+ // Shoot
+ Shoot();
+ if (ShouldExitAuto()) return;
+
+ // Get ready to zero when we come back up.
+ time::SleepFor(time::Time::InSeconds(0.05));
+ PositionClawVertically(0.0, 0.0);
+}
+
+} // namespace autonomous
+} // namespace frc971
diff --git a/y2014/autonomous/auto.h b/y2014/autonomous/auto.h
new file mode 100644
index 0000000..3e4885f
--- /dev/null
+++ b/y2014/autonomous/auto.h
@@ -0,0 +1,12 @@
+#ifndef Y2014_AUTONOMOUS_AUTO_H_
+#define Y2014_AUTONOMOUS_AUTO_H_
+
+namespace frc971 {
+namespace autonomous {
+
+void HandleAuto();
+
+} // namespace autonomous
+} // namespace frc971
+
+#endif // Y2014_AUTONOMOUS_AUTO_H_
diff --git a/y2014/autonomous/auto_main.cc b/y2014/autonomous/auto_main.cc
new file mode 100644
index 0000000..3dbad2c
--- /dev/null
+++ b/y2014/autonomous/auto_main.cc
@@ -0,0 +1,42 @@
+#include <stdio.h>
+
+#include "aos/common/time.h"
+#include "aos/linux_code/init.h"
+#include "aos/common/logging/logging.h"
+#include "frc971/autonomous/auto.q.h"
+#include "y2014/autonomous/auto.h"
+
+using ::aos::time::Time;
+
+int main(int /*argc*/, char * /*argv*/[]) {
+ ::aos::Init();
+
+ LOG(INFO, "Auto main started\n");
+ ::frc971::autonomous::autonomous.FetchLatest();
+ while (!::frc971::autonomous::autonomous.get()) {
+ ::frc971::autonomous::autonomous.FetchNextBlocking();
+ LOG(INFO, "Got another auto packet\n");
+ }
+
+ while (true) {
+ while (!::frc971::autonomous::autonomous->run_auto) {
+ ::frc971::autonomous::autonomous.FetchNextBlocking();
+ LOG(INFO, "Got another auto packet\n");
+ }
+ LOG(INFO, "Starting auto mode\n");
+ ::aos::time::Time start_time = ::aos::time::Time::Now();
+ ::frc971::autonomous::HandleAuto();
+
+ ::aos::time::Time elapsed_time = ::aos::time::Time::Now() - start_time;
+ LOG(INFO, "Auto mode exited in %f, waiting for it to finish.\n",
+ elapsed_time.ToSeconds());
+ while (::frc971::autonomous::autonomous->run_auto) {
+ ::frc971::autonomous::autonomous.FetchNextBlocking();
+ LOG(INFO, "Got another auto packet\n");
+ }
+ LOG(INFO, "Waiting for auto to start back up.\n");
+ }
+ ::aos::Cleanup();
+ return 0;
+}
+
diff --git a/y2014/autonomous/autonomous.gyp b/y2014/autonomous/autonomous.gyp
new file mode 100644
index 0000000..8efdaec
--- /dev/null
+++ b/y2014/autonomous/autonomous.gyp
@@ -0,0 +1,45 @@
+{
+ 'targets': [
+ {
+ 'target_name': 'auto_lib',
+ 'type': 'static_library',
+ 'sources': [
+ 'auto.cc',
+ ],
+ 'dependencies': [
+ '<(DEPTH)/frc971/autonomous/autonomous.gyp:auto_queue',
+ '<(AOS)/common/controls/controls.gyp:control_loop',
+ '<(DEPTH)/y2014/control_loops/drivetrain/drivetrain.gyp:drivetrain_queue',
+ '<(DEPTH)/y2014/control_loops/shooter/shooter.gyp:shooter_queue',
+ '<(DEPTH)/y2014/control_loops/claw/claw.gyp:claw_queue',
+ '<(DEPTH)/y2014/y2014.gyp:constants',
+ '<(AOS)/common/common.gyp:time',
+ '<(AOS)/common/util/util.gyp:phased_loop',
+ '<(AOS)/common/util/util.gyp:trapezoid_profile',
+ '<(AOS)/build/aos.gyp:logging',
+ '<(AOS)/common/actions/actions.gyp:action_lib',
+ '<(DEPTH)/y2014/actors/actors.gyp:shoot_action_lib',
+ '<(DEPTH)/y2014/actors/actors.gyp:drivetrain_action_lib',
+ '<(DEPTH)/y2014/queues/queues.gyp:hot_goal',
+ '<(AOS)/common/logging/logging.gyp:queue_logging',
+ '<(DEPTH)/y2014/queues/queues.gyp:profile_params',
+ '<(DEPTH)/y2014/queues/queues.gyp:auto_mode',
+ ],
+ 'export_dependent_settings': [
+ '<(AOS)/common/controls/controls.gyp:control_loop',
+ ],
+ },
+ {
+ 'target_name': 'auto',
+ 'type': 'executable',
+ 'sources': [
+ 'auto_main.cc',
+ ],
+ 'dependencies': [
+ '<(AOS)/linux_code/linux_code.gyp:init',
+ '<(DEPTH)/frc971/autonomous/autonomous.gyp:auto_queue',
+ 'auto_lib',
+ ],
+ },
+ ],
+}
diff --git a/y2014/constants.cc b/y2014/constants.cc
new file mode 100644
index 0000000..2c101c3
--- /dev/null
+++ b/y2014/constants.cc
@@ -0,0 +1,224 @@
+#include "y2014/constants.h"
+
+#include <math.h>
+#include <stdint.h>
+#include <inttypes.h>
+
+#include <map>
+
+#if __has_feature(address_sanitizer)
+#include "sanitizer/lsan_interface.h"
+#endif
+
+#include "aos/common/logging/logging.h"
+#include "aos/common/once.h"
+#include "aos/common/network/team_number.h"
+#include "aos/common/mutex.h"
+
+#include "y2014/control_loops/drivetrain/polydrivetrain_dog_motor_plant.h"
+#include "y2014/control_loops/drivetrain/drivetrain_dog_motor_plant.h"
+
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif
+
+namespace frc971 {
+namespace constants {
+namespace {
+
+const uint16_t kCompTeamNumber = 971;
+const uint16_t kPracticeTeamNumber = 9971;
+const uint16_t kRoboRioTeamNumber = 254;
+
+const double kCompDrivetrainEncoderRatio =
+ (18.0 / 50.0) /*output reduction*/ * (56.0 / 30.0) /*encoder gears*/;
+const double kCompLowGearRatio = 18.0 / 60.0 * 18.0 / 50.0;
+const double kCompHighGearRatio = 28.0 / 50.0 * 18.0 / 50.0;
+
+const double kPracticeDrivetrainEncoderRatio = kCompDrivetrainEncoderRatio;
+const double kPracticeLowGearRatio = kCompLowGearRatio;
+const double kPracticeHighGearRatio = kCompHighGearRatio;
+
+const ShifterHallEffect kCompRightDriveShifter{555, 657, 660, 560, 0.2, 0.7};
+const ShifterHallEffect kCompLeftDriveShifter{555, 660, 644, 552, 0.2, 0.7};
+
+const ShifterHallEffect kPracticeRightDriveShifter{2.95, 3.95, 3.95, 2.95, 0.2, 0.7};
+const ShifterHallEffect kPracticeLeftDriveShifter{2.95, 4.2, 3.95, 3.0, 0.2, 0.7};
+
+const double kRobotWidth = 25.0 / 100.0 * 2.54;
+
+const double shooter_zeroing_speed = 0.05;
+const double shooter_unload_speed = 0.08;
+
+// Smaller (more negative) = opening.
+const double kCompTopClawOffset = -0.120;
+
+const Values *DoGetValuesForTeam(uint16_t team) {
+ switch (team) {
+ case 1: // for tests
+ return new Values{
+ kCompDrivetrainEncoderRatio,
+ kCompLowGearRatio,
+ kCompHighGearRatio,
+ kCompLeftDriveShifter,
+ kCompRightDriveShifter,
+ false,
+ 0.5,
+ control_loops::MakeVelocityDrivetrainLoop,
+ control_loops::MakeDrivetrainLoop,
+ 0.02, // drivetrain done delta
+ 5.0, // drivetrain max speed
+
+ // ShooterLimits
+ {-0.00127, 0.298196, -0.0017, 0.305054, 0.0149098,
+ {-0.001778, 0.000762, 0, 0},
+ {-0.001778, 0.008906, 0, 0},
+ {0.006096, 0.026416, 0, 0},
+ shooter_zeroing_speed,
+ shooter_unload_speed
+ },
+ {0.5,
+ 0.1,
+ 0.1,
+ 0.0,
+ 1.57,
+ 0.05,
+ 1.5,
+ {0.0, 2.05, 0.02, 2.02, {-0.1, 0.05, -0.1, 0.05}, {1.0, 1.1, 1.0, 1.1}, {2.0, 2.1, 2.0, 2.1}},
+ {0.0, 2.05, 0.02, 2.02, {-0.1, 0.05, -0.1, 0.05}, {1.0, 1.1, 1.0, 1.1}, {2.0, 2.1, 2.0, 2.1}},
+ 0.01, // claw_unimportant_epsilon
+ 0.9, // start_fine_tune_pos
+ 4.0,
+ },
+ {0.07, 0.15}, // shooter_action
+ };
+ break;
+ case kCompTeamNumber:
+ return new Values{
+ kCompDrivetrainEncoderRatio,
+ kCompLowGearRatio,
+ kCompHighGearRatio,
+ kCompLeftDriveShifter,
+ kCompRightDriveShifter,
+ false,
+ kRobotWidth,
+ control_loops::MakeVelocityDrivetrainLoop,
+ control_loops::MakeDrivetrainLoop,
+ 0.02, // drivetrain done delta
+ 5.0, // drivetrain max speed
+
+ // ShooterLimits
+ {-0.001041, 0.296019, -0.001488, 0.302717, 0.0149098,
+ {-0.002, 0.000446, -0.002, 0.000446},
+ {-0.002, 0.009078, -0.002, 0.009078},
+ {0.003870, 0.026194, 0.003869, 0.026343},
+ shooter_zeroing_speed,
+ shooter_unload_speed
+ },
+ {0.800000,
+ 0.400000,
+ 0.000000,
+ -1.220821,
+ 1.822142,
+ -0.849484,
+ 1.42309,
+ // 0.0371
+ {-3.3284, 2.0917, -3.1661, 1.95,
+ {-3.4, -3.02 + kCompTopClawOffset, -3.4, -2.9876 + kCompTopClawOffset},
+ {-0.1433 + kCompTopClawOffset, 0.0670 + kCompTopClawOffset, -0.1460 + kCompTopClawOffset, 0.0648 + kCompTopClawOffset},
+ {1.9952 + kCompTopClawOffset, 2.2, 1.9898 + kCompTopClawOffset, 2.2}},
+ {-2.453460, 3.082960, -2.453460, 3.082960,
+ {-2.6, -2.185752, -2.6, -2.184843},
+ {-0.322249, -0.053177, -0.332248, -0.059086},
+ {2.892065, 3.2, 2.888429, 3.2}},
+ 0.040000, // claw_unimportant_epsilon
+ -0.400000, // start_fine_tune_pos
+ 4.000000,
+ },
+ //TODO(james): Get realer numbers for shooter_action.
+ {0.07, 0.15}, // shooter_action
+ };
+ break;
+ case kPracticeTeamNumber:
+ case kRoboRioTeamNumber:
+ return new Values{
+ kPracticeDrivetrainEncoderRatio,
+ kPracticeLowGearRatio,
+ kPracticeHighGearRatio,
+ kPracticeLeftDriveShifter,
+ kPracticeRightDriveShifter,
+ false,
+ kRobotWidth,
+ control_loops::MakeVelocityDrivetrainLoop,
+ control_loops::MakeDrivetrainLoop,
+ 0.02, // drivetrain done delta
+ 5.0, // drivetrain max speed
+
+ // ShooterLimits
+ {-0.001042, 0.294084, -0.001935, 0.303460, 0.0138401,
+ {-0.002, 0.000446, -0.002, 0.000446},
+ {-0.002, 0.009078, -0.002, 0.009078},
+ {0.003869, 0.026194, 0.003869, 0.026194},
+ shooter_zeroing_speed,
+ shooter_unload_speed
+ },
+ {0.400000 * 2.0,
+ 0.200000 * 2.0,
+ 0.000000 * 2.0,
+ -0.762218 * 2.0,
+ 1.767146,
+ -0.849484,
+ 1.42308,
+ {-3.364758, 2.086668, -3.166136, 1.95,
+ {-1.7 * 2.0, -1.544662 * 2.0, -1.7 * 2.0, -1.547616 * 2.0},
+ {-0.130218 * 2.0, -0.019771 * 2.0, -0.132036 * 2.0, -0.018862 * 2.0},
+ {1.871684, 2.2, 1.86532, 2.2}},
+ {-2.451642, 3.107504, -2.273474, 2.750,
+ {-2.6, -2.176662, -2.6, -2.176662},
+ {-0.316796, -0.054088, -0.319978, -0.062723},
+ {2.894792, 3.2, 2.887974, 3.2}},
+ 0.040000, // claw_unimportant_epsilon
+ -0.400000, // start_fine_tune_pos
+ 4.000000,
+ },
+ //TODO(james): Get realer numbers for shooter_action.
+ {0.07, 0.15}, // shooter_action
+ };
+ break;
+ default:
+ LOG(FATAL, "unknown team #%" PRIu16 "\n", team);
+ }
+}
+
+const Values *DoGetValues() {
+ uint16_t team = ::aos::network::GetTeamNumber();
+ LOG(INFO, "creating a Constants for team %" PRIu16 "\n", team);
+ return DoGetValuesForTeam(team);
+}
+
+} // namespace
+
+const Values &GetValues() {
+ static ::aos::Once<const Values> once(DoGetValues);
+ return *once.Get();
+}
+
+const Values &GetValuesForTeam(uint16_t team_number) {
+ static ::aos::Mutex mutex;
+ ::aos::MutexLocker locker(&mutex);
+
+ // IMPORTANT: This declaration has to stay after the mutex is locked to avoid
+ // race conditions.
+ static ::std::map<uint16_t, const Values *> values;
+
+ if (values.count(team_number) == 0) {
+ values[team_number] = DoGetValuesForTeam(team_number);
+#if __has_feature(address_sanitizer)
+ __lsan_ignore_object(values[team_number]);
+#endif
+ }
+ return *values[team_number];
+}
+
+} // namespace constants
+} // namespace frc971
diff --git a/y2014/constants.h b/y2014/constants.h
new file mode 100644
index 0000000..79ba826
--- /dev/null
+++ b/y2014/constants.h
@@ -0,0 +1,145 @@
+#ifndef Y2014_CONSTANTS_H_
+#define Y2014_CONSTANTS_H_
+
+#include <stdint.h>
+
+#include "frc971/control_loops/state_feedback_loop.h"
+#include "frc971/shifter_hall_effect.h"
+
+namespace frc971 {
+namespace constants {
+
+// Has all of the numbers that change for both robots and makes it easy to
+// retrieve the values for the current one.
+
+// Everything is in SI units (volts, radians, meters, seconds, etc).
+// Some of these values are related to the conversion between raw values
+// (encoder counts, voltage, etc) to scaled units (radians, meters, etc).
+
+// This structure contains current values for all of the things that change.
+struct Values {
+ // This is useful for representing the 2 sides of a hall effect sensor etc.
+ struct AnglePair {
+ // The angles for increasing values (posedge on lower, negedge on upper).
+ double lower_angle, upper_angle;
+ // The angles for decreasing values (negedge on lower, posedge on upper).
+ double lower_decreasing_angle, upper_decreasing_angle;
+ };
+
+ // The ratio from the encoder shaft to the drivetrain wheels.
+ double drivetrain_encoder_ratio;
+
+ // The gear ratios from motor shafts to the drivetrain wheels for high and low
+ // gear.
+ double low_gear_ratio;
+ double high_gear_ratio;
+ ShifterHallEffect left_drive, right_drive;
+ bool clutch_transmission;
+
+ double turn_width;
+
+ ::std::function<StateFeedbackLoop<2, 2, 2>()> make_v_drivetrain_loop;
+ ::std::function<StateFeedbackLoop<4, 2, 2>()> make_drivetrain_loop;
+
+ double drivetrain_done_distance;
+ double drivetrain_max_speed;
+
+ struct ZeroingConstants {
+ // The number of samples in the moving average filter.
+ int average_filter_size;
+ // The difference in scaled units between two index pulses.
+ double index_difference;
+ // The absolute position in scaled units of one of the index pulses.
+ double measured_index_position;
+ // Value between 0 and 1 which determines a fraction of the index_diff
+ // you want to use.
+ double allowable_encoder_error;
+ };
+
+ // Defines a range of motion for a subsystem.
+ // These are all absolute positions in scaled units.
+ struct Range {
+ double lower_limit;
+ double upper_limit;
+ double lower_hard_limit;
+ double upper_hard_limit;
+ };
+
+ struct Shooter {
+ double lower_limit;
+ double upper_limit;
+ double lower_hard_limit;
+ double upper_hard_limit;
+ // If the plunger is further back than this position, it is safe for the
+ // latch to be down. Anything else would be considered a collision.
+ double latch_max_safe_position;
+ AnglePair plunger_back;
+ AnglePair pusher_distal;
+ AnglePair pusher_proximal;
+ double zeroing_speed;
+ double unload_speed;
+ };
+
+ Shooter shooter;
+
+ struct Claws {
+ double claw_zeroing_off_speed;
+ double claw_zeroing_speed;
+ double claw_zeroing_separation;
+
+ // claw separation that would be considered a collision
+ double claw_min_separation;
+ double claw_max_separation;
+
+ // We should never get closer/farther than these.
+ double soft_min_separation;
+ double soft_max_separation;
+
+ // Three hall effects are known as front, calib and back
+ typedef Values::AnglePair AnglePair;
+
+ struct Claw {
+ double lower_hard_limit;
+ double upper_hard_limit;
+ double lower_limit;
+ double upper_limit;
+ AnglePair front;
+ AnglePair calibration;
+ AnglePair back;
+ };
+
+ Claw upper_claw;
+ Claw lower_claw;
+
+ double claw_unimportant_epsilon;
+ double start_fine_tune_pos;
+ double max_zeroing_voltage;
+ };
+ Claws claw;
+
+ // Has all the constants for the ShootAction class.
+ struct ShooterAction {
+ // Minimum separation required between the claws in order to be able to
+ // shoot.
+ double claw_shooting_separation;
+
+ // Goal to send to the claw when opening it up in preparation for shooting;
+ // should be larger than claw_shooting_separation so that we can shoot
+ // promptly.
+ double claw_separation_goal;
+ };
+ ShooterAction shooter_action;
+};
+
+// Creates (once) a Values instance for ::aos::network::GetTeamNumber() and
+// returns a reference to it.
+const Values &GetValues();
+
+// Creates Values instances for each team number it is called with and returns
+// them.
+const Values &GetValuesForTeam(uint16_t team_number);
+
+} // namespace constants
+} // namespace frc971
+
+#endif // Y2014_CONSTANTS_H_
diff --git a/y2014/control_loops/claw/claw.cc b/y2014/control_loops/claw/claw.cc
new file mode 100644
index 0000000..7322b1e
--- /dev/null
+++ b/y2014/control_loops/claw/claw.cc
@@ -0,0 +1,1024 @@
+#include "y2014/control_loops/claw/claw.h"
+
+#include <algorithm>
+
+#include "aos/common/controls/control_loops.q.h"
+#include "aos/common/logging/logging.h"
+#include "aos/common/logging/queue_logging.h"
+#include "aos/common/logging/matrix_logging.h"
+#include "aos/common/commonmath.h"
+
+#include "y2014/constants.h"
+#include "y2014/control_loops/claw/claw_motor_plant.h"
+
+// Zeroing plan.
+// There are 2 types of zeros. Enabled and disabled ones.
+// Disabled ones are only valid during auto mode, and can be used to speed up
+// the enabled zero process. We need to re-zero during teleop in case the auto
+// zero was poor and causes us to miss all our shots.
+//
+// We need to be able to zero manually while disabled by moving the joint over
+// the zeros.
+// Zero on the down edge when disabled (gravity in the direction of motion)
+//
+// When enabled, zero on the up edge (gravity opposing the direction of motion)
+// The enabled sequence needs to work as follows. We can crash the claw if we
+// bring them too close to each other or too far from each other. The only safe
+// thing to do is to move them in unison.
+//
+// Start by moving them both towards the front of the bot to either find either
+// the middle hall effect on either jaw, or the front hall effect on the bottom
+// jaw. Any edge that isn't the desired edge will provide an approximate edge
+// location that can be used for the fine tuning step.
+// Once an edge is found on the front claw, move back the other way with both
+// claws until an edge is found for the other claw.
+// Now that we have an approximate zero, we can robustify the limits to keep
+// both claws safe. Then, we can move both claws to a position that is the
+// correct side of the zero and go zero.
+
+// Valid region plan.
+// Difference between the arms has a range, and the values of each arm has a
+// range.
+// If a claw runs up against a static limit, don't let the goal change outside
+// the limit.
+// If a claw runs up against a movable limit, move both claws outwards to get
+// out of the condition.
+
+namespace frc971 {
+namespace control_loops {
+
+static const double kZeroingVoltage = 4.0;
+static const double kMaxVoltage = 12.0;
+const double kRezeroThreshold = 0.07;
+
+ClawLimitedLoop::ClawLimitedLoop(StateFeedbackLoop<4, 2, 2> &&loop)
+ : StateFeedbackLoop<4, 2, 2>(::std::move(loop)),
+ uncapped_average_voltage_(0.0),
+ is_zeroing_(true),
+ U_Poly_((Eigen::Matrix<double, 4, 2>() << 1, 0,
+ -1, 0,
+ 0, 1,
+ 0, -1).finished(),
+ (Eigen::Matrix<double, 4, 1>() << kMaxVoltage, kMaxVoltage,
+ kMaxVoltage, kMaxVoltage).finished()),
+ U_Poly_zeroing_((Eigen::Matrix<double, 4, 2>() << 1, 0,
+ -1, 0,
+ 0, 1,
+ 0, -1).finished(),
+ (Eigen::Matrix<double, 4, 1>() <<
+ kZeroingVoltage, kZeroingVoltage,
+ kZeroingVoltage, kZeroingVoltage).finished()) {
+ ::aos::controls::HPolytope<0>::Init();
+}
+
+// Caps the voltage prioritizing reducing velocity error over reducing
+// positional error.
+// Uses the polytope libararies which we used to just use for the drivetrain.
+// Uses a region representing the maximum voltage and then transforms it such
+// that the points represent different amounts of positional error and
+// constrains the region such that, if at all possible, it will maintain its
+// current efforts to reduce velocity error.
+void ClawLimitedLoop::CapU() {
+ const Eigen::Matrix<double, 4, 1> error = R() - X_hat();
+
+ double u_top = U(1, 0);
+ double u_bottom = U(0, 0);
+
+ uncapped_average_voltage_ = (u_top + u_bottom) / 2;
+
+ double max_voltage = is_zeroing_ ? kZeroingVoltage : kMaxVoltage;
+
+ if (::std::abs(u_bottom) > max_voltage || ::std::abs(u_top) > max_voltage) {
+ LOG_MATRIX(DEBUG, "U at start", U());
+ // H * U <= k
+ // U = UPos + UVel
+ // H * (UPos + UVel) <= k
+ // H * UPos <= k - H * UVel
+
+ // Now, we can do a coordinate transformation and say the following.
+
+ // UPos = position_K * position_error
+ // (H * position_K) * position_error <= k - H * UVel
+
+ Eigen::Matrix<double, 2, 2> position_K;
+ position_K << K(0, 0), K(0, 1),
+ K(1, 0), K(1, 1);
+ Eigen::Matrix<double, 2, 2> velocity_K;
+ velocity_K << K(0, 2), K(0, 3),
+ K(1, 2), K(1, 3);
+
+ Eigen::Matrix<double, 2, 1> position_error;
+ position_error << error(0, 0), error(1, 0);
+ Eigen::Matrix<double, 2, 1> velocity_error;
+ velocity_error << error(2, 0), error(3, 0);
+ LOG_MATRIX(DEBUG, "error", error);
+
+ const auto &poly = is_zeroing_ ? U_Poly_zeroing_ : U_Poly_;
+ const Eigen::Matrix<double, 4, 2> pos_poly_H = poly.H() * position_K;
+ const Eigen::Matrix<double, 4, 1> pos_poly_k =
+ poly.k() - poly.H() * velocity_K * velocity_error;
+ const ::aos::controls::HPolytope<2> pos_poly(pos_poly_H, pos_poly_k);
+
+ Eigen::Matrix<double, 2, 1> adjusted_pos_error;
+ {
+ const auto &P = position_error;
+
+ // This line was at 45 degrees but is now at some angle steeper than the
+ // straight one between the points.
+ Eigen::Matrix<double, 1, 2> angle_45;
+ // If the top claw is above its soft upper limit, make the line actually
+ // 45 degrees to avoid smashing it into the limit in an attempt to fix the
+ // separation error faster than the bottom position one.
+ if (X_hat(0, 0) + X_hat(1, 0) >
+ constants::GetValues().claw.upper_claw.upper_limit) {
+ angle_45 << 1, 1;
+ } else {
+ // Fixing separation error half as fast as positional error works well
+ // because it means they both close evenly.
+ angle_45 << ::std::sqrt(3), 1;
+ }
+ Eigen::Matrix<double, 1, 2> L45_quadrant;
+ L45_quadrant << ::aos::sign(P(1, 0)), -::aos::sign(P(0, 0));
+ const auto L45 = L45_quadrant.cwiseProduct(angle_45);
+ const double w45 = 0;
+
+ Eigen::Matrix<double, 1, 2> LH;
+ if (::std::abs(P(0, 0)) > ::std::abs(P(1, 0))) {
+ LH << 0, 1;
+ } else {
+ LH << 1, 0;
+ }
+ const double wh = LH.dot(P);
+
+ Eigen::Matrix<double, 2, 2> standard;
+ standard << L45, LH;
+ Eigen::Matrix<double, 2, 1> W;
+ W << w45, wh;
+ const Eigen::Matrix<double, 2, 1> intersection = standard.inverse() * W;
+
+ bool is_inside_h;
+ const auto adjusted_pos_error_h =
+ DoCoerceGoal(pos_poly, LH, wh, position_error, &is_inside_h);
+ const auto adjusted_pos_error_45 =
+ DoCoerceGoal(pos_poly, L45, w45, intersection, nullptr);
+ if (pos_poly.IsInside(intersection)) {
+ adjusted_pos_error = adjusted_pos_error_h;
+ } else {
+ if (is_inside_h) {
+ if (adjusted_pos_error_h.norm() > adjusted_pos_error_45.norm()) {
+ adjusted_pos_error = adjusted_pos_error_h;
+ } else {
+ adjusted_pos_error = adjusted_pos_error_45;
+ }
+ } else {
+ adjusted_pos_error = adjusted_pos_error_45;
+ }
+ }
+ }
+
+ LOG_MATRIX(DEBUG, "adjusted_pos_error", adjusted_pos_error);
+ mutable_U() = velocity_K * velocity_error + position_K * adjusted_pos_error;
+ LOG_MATRIX(DEBUG, "U is now", U());
+
+ {
+ const auto values = constants::GetValues().claw;
+ if (top_known_) {
+ if (X_hat(0, 0) + X_hat(1, 0) > values.upper_claw.upper_limit && U(1, 0) > 0) {
+ LOG(WARNING, "upper claw too high and moving up\n");
+ mutable_U(1, 0) = 0;
+ } else if (X_hat(0, 0) + X_hat(1, 0) < values.upper_claw.lower_limit &&
+ U(1, 0) < 0) {
+ LOG(WARNING, "upper claw too low and moving down\n");
+ mutable_U(1, 0) = 0;
+ }
+ }
+ if (bottom_known_) {
+ if (X_hat(0, 0) > values.lower_claw.upper_limit && U(0, 0) > 0) {
+ LOG(WARNING, "lower claw too high and moving up\n");
+ mutable_U(0, 0) = 0;
+ } else if (X_hat(0, 0) < values.lower_claw.lower_limit && U(0, 0) < 0) {
+ LOG(WARNING, "lower claw too low and moving down\n");
+ mutable_U(0, 0) = 0;
+ }
+ }
+ }
+ }
+}
+
+ZeroedStateFeedbackLoop::ZeroedStateFeedbackLoop(const char *name,
+ ClawMotor *motor)
+ : offset_(0.0),
+ name_(name),
+ motor_(motor),
+ zeroing_state_(UNKNOWN_POSITION),
+ encoder_(0.0),
+ last_encoder_(0.0) {}
+
+void ZeroedStateFeedbackLoop::SetPositionValues(const HalfClawPosition &claw) {
+ front_.Update(claw.front);
+ calibration_.Update(claw.calibration);
+ back_.Update(claw.back);
+
+ bool any_sensor_triggered = any_triggered();
+ if (any_sensor_triggered && any_triggered_last_) {
+ // We are still on the hall effect and nothing has changed.
+ min_hall_effect_on_angle_ =
+ ::std::min(min_hall_effect_on_angle_, claw.position);
+ max_hall_effect_on_angle_ =
+ ::std::max(max_hall_effect_on_angle_, claw.position);
+ } else if (!any_sensor_triggered && !any_triggered_last_) {
+ // We are still off the hall effect and nothing has changed.
+ min_hall_effect_off_angle_ =
+ ::std::min(min_hall_effect_off_angle_, claw.position);
+ max_hall_effect_off_angle_ =
+ ::std::max(max_hall_effect_off_angle_, claw.position);
+ }
+
+ if (front_.is_posedge()) {
+ // Saw a posedge on the hall effect. Reset the limits.
+ min_hall_effect_on_angle_ =
+ ::std::min(claw.front.posedge_value, claw.position);
+ max_hall_effect_on_angle_ =
+ ::std::max(claw.front.posedge_value, claw.position);
+ }
+ if (calibration_.is_posedge()) {
+ // Saw a posedge on the hall effect. Reset the limits.
+ min_hall_effect_on_angle_ =
+ ::std::min(claw.calibration.posedge_value, claw.position);
+ max_hall_effect_on_angle_ =
+ ::std::max(claw.calibration.posedge_value, claw.position);
+ }
+ if (back_.is_posedge()) {
+ // Saw a posedge on the hall effect. Reset the limits.
+ min_hall_effect_on_angle_ =
+ ::std::min(claw.back.posedge_value, claw.position);
+ max_hall_effect_on_angle_ =
+ ::std::max(claw.back.posedge_value, claw.position);
+ }
+
+ if (front_.is_negedge()) {
+ // Saw a negedge on the hall effect. Reset the limits.
+ min_hall_effect_off_angle_ =
+ ::std::min(claw.front.negedge_value, claw.position);
+ max_hall_effect_off_angle_ =
+ ::std::max(claw.front.negedge_value, claw.position);
+ }
+ if (calibration_.is_negedge()) {
+ // Saw a negedge on the hall effect. Reset the limits.
+ min_hall_effect_off_angle_ =
+ ::std::min(claw.calibration.negedge_value, claw.position);
+ max_hall_effect_off_angle_ =
+ ::std::max(claw.calibration.negedge_value, claw.position);
+ }
+ if (back_.is_negedge()) {
+ // Saw a negedge on the hall effect. Reset the limits.
+ min_hall_effect_off_angle_ =
+ ::std::min(claw.back.negedge_value, claw.position);
+ max_hall_effect_off_angle_ =
+ ::std::max(claw.back.negedge_value, claw.position);
+ }
+
+ last_encoder_ = encoder_;
+ if (front().value() || calibration().value() || back().value()) {
+ last_on_encoder_ = encoder_;
+ } else {
+ last_off_encoder_ = encoder_;
+ }
+ encoder_ = claw.position;
+ any_triggered_last_ = any_sensor_triggered;
+}
+
+void ZeroedStateFeedbackLoop::Reset(const HalfClawPosition &claw) {
+ set_zeroing_state(ZeroedStateFeedbackLoop::UNKNOWN_POSITION);
+
+ front_.Reset(claw.front);
+ calibration_.Reset(claw.calibration);
+ back_.Reset(claw.back);
+ // close up the min and max edge positions as they are no longer valid and
+ // will be expanded in future iterations
+ min_hall_effect_on_angle_ = claw.position;
+ max_hall_effect_on_angle_ = claw.position;
+ min_hall_effect_off_angle_ = claw.position;
+ max_hall_effect_off_angle_ = claw.position;
+ any_triggered_last_ = any_triggered();
+}
+
+bool TopZeroedStateFeedbackLoop::SetCalibrationOnEdge(
+ const constants::Values::Claws::Claw &claw_values,
+ JointZeroingState zeroing_state) {
+ double edge_encoder;
+ double edge_angle;
+ if (GetPositionOfEdge(claw_values, &edge_encoder, &edge_angle)) {
+ LOG(INFO, "Calibration edge edge should be %f.\n", edge_angle);
+ SetCalibration(edge_encoder, edge_angle);
+ set_zeroing_state(zeroing_state);
+ return true;
+ }
+ return false;
+}
+
+void TopZeroedStateFeedbackLoop::HandleCalibrationError(
+ const constants::Values::Claws::Claw &claw_values) {
+ double edge_encoder;
+ double edge_angle;
+ if (GetPositionOfEdge(claw_values, &edge_encoder, &edge_angle)) {
+ const double calibration_error =
+ ComputeCalibrationChange(edge_encoder, edge_angle);
+ LOG(INFO, "Top calibration error is %f\n", calibration_error);
+ if (::std::abs(calibration_error) > kRezeroThreshold) {
+ LOG(WARNING, "rezeroing top\n");
+ SetCalibration(edge_encoder, edge_angle);
+ set_zeroing_state(ZeroedStateFeedbackLoop::UNKNOWN_POSITION);
+ }
+ }
+}
+
+
+void BottomZeroedStateFeedbackLoop::HandleCalibrationError(
+ const constants::Values::Claws::Claw &claw_values) {
+ double edge_encoder;
+ double edge_angle;
+ if (GetPositionOfEdge(claw_values, &edge_encoder, &edge_angle)) {
+ const double calibration_error =
+ ComputeCalibrationChange(edge_encoder, edge_angle);
+ LOG(INFO, "Bottom calibration error is %f\n", calibration_error);
+ if (::std::abs(calibration_error) > kRezeroThreshold) {
+ LOG(WARNING, "rezeroing bottom\n");
+ SetCalibration(edge_encoder, edge_angle);
+ set_zeroing_state(ZeroedStateFeedbackLoop::UNKNOWN_POSITION);
+ }
+ }
+}
+
+bool BottomZeroedStateFeedbackLoop::SetCalibrationOnEdge(
+ const constants::Values::Claws::Claw &claw_values,
+ JointZeroingState zeroing_state) {
+ double edge_encoder;
+ double edge_angle;
+ if (GetPositionOfEdge(claw_values, &edge_encoder, &edge_angle)) {
+ LOG(INFO, "Calibration edge.\n");
+ SetCalibration(edge_encoder, edge_angle);
+ set_zeroing_state(zeroing_state);
+ return true;
+ }
+ return false;
+}
+
+ClawMotor::ClawMotor(control_loops::ClawQueue *my_claw)
+ : aos::controls::ControlLoop<control_loops::ClawQueue>(my_claw),
+ has_top_claw_goal_(false),
+ top_claw_goal_(0.0),
+ top_claw_(this),
+ has_bottom_claw_goal_(false),
+ bottom_claw_goal_(0.0),
+ bottom_claw_(this),
+ claw_(MakeClawLoop()),
+ was_enabled_(false),
+ doing_calibration_fine_tune_(false),
+ capped_goal_(false),
+ mode_(UNKNOWN_LOCATION) {}
+
+const int ZeroedStateFeedbackLoop::kZeroingMaxVoltage;
+
+bool ZeroedStateFeedbackLoop::SawFilteredPosedge(
+ const HallEffectTracker &this_sensor, const HallEffectTracker &sensorA,
+ const HallEffectTracker &sensorB) {
+ if (posedge_filter_ == nullptr && this_sensor.posedge_count_changed() &&
+ !sensorA.posedge_count_changed() && !sensorB.posedge_count_changed() &&
+ this_sensor.value() && !this_sensor.last_value()) {
+ posedge_filter_ = &this_sensor;
+ } else if (posedge_filter_ == &this_sensor &&
+ !this_sensor.posedge_count_changed() &&
+ !sensorA.posedge_count_changed() &&
+ !sensorB.posedge_count_changed() && this_sensor.value()) {
+ posedge_filter_ = nullptr;
+ return true;
+ } else if (posedge_filter_ == &this_sensor) {
+ posedge_filter_ = nullptr;
+ }
+ return false;
+}
+
+bool ZeroedStateFeedbackLoop::SawFilteredNegedge(
+ const HallEffectTracker &this_sensor, const HallEffectTracker &sensorA,
+ const HallEffectTracker &sensorB) {
+ if (negedge_filter_ == nullptr && this_sensor.negedge_count_changed() &&
+ !sensorA.negedge_count_changed() && !sensorB.negedge_count_changed() &&
+ !this_sensor.value() && this_sensor.last_value()) {
+ negedge_filter_ = &this_sensor;
+ } else if (negedge_filter_ == &this_sensor &&
+ !this_sensor.negedge_count_changed() &&
+ !sensorA.negedge_count_changed() &&
+ !sensorB.negedge_count_changed() && !this_sensor.value()) {
+ negedge_filter_ = nullptr;
+ return true;
+ } else if (negedge_filter_ == &this_sensor) {
+ negedge_filter_ = nullptr;
+ }
+ return false;
+}
+
+bool ZeroedStateFeedbackLoop::DoGetPositionOfEdge(
+ const constants::Values::Claws::AnglePair &angles, double *edge_encoder,
+ double *edge_angle, const HallEffectTracker &this_sensor,
+ const HallEffectTracker &sensorA, const HallEffectTracker &sensorB,
+ const char *hall_effect_name) {
+ bool found_edge = false;
+
+ if (SawFilteredPosedge(this_sensor, sensorA, sensorB)) {
+ if (min_hall_effect_off_angle_ == max_hall_effect_off_angle_) {
+ LOG(WARNING, "%s: Uncertain which side, rejecting posedge\n", name_);
+ } else {
+ const double average_last_encoder =
+ (min_hall_effect_off_angle_ + max_hall_effect_off_angle_) / 2.0;
+ if (this_sensor.posedge_value() < average_last_encoder) {
+ *edge_angle = angles.upper_decreasing_angle;
+ LOG(INFO, "%s Posedge upper of %s -> %f posedge: %f avg_encoder: %f\n",
+ name_, hall_effect_name, *edge_angle, this_sensor.posedge_value(),
+ average_last_encoder);
+ } else {
+ *edge_angle = angles.lower_angle;
+ LOG(INFO, "%s Posedge lower of %s -> %f posedge: %f avg_encoder: %f\n",
+ name_, hall_effect_name, *edge_angle, this_sensor.posedge_value(),
+ average_last_encoder);
+ }
+ *edge_encoder = this_sensor.posedge_value();
+ found_edge = true;
+ }
+ }
+
+ if (SawFilteredNegedge(this_sensor, sensorA, sensorB)) {
+ if (min_hall_effect_on_angle_ == max_hall_effect_on_angle_) {
+ LOG(WARNING, "%s: Uncertain which side, rejecting negedge\n", name_);
+ } else {
+ const double average_last_encoder =
+ (min_hall_effect_on_angle_ + max_hall_effect_on_angle_) / 2.0;
+ if (this_sensor.negedge_value() > average_last_encoder) {
+ *edge_angle = angles.upper_angle;
+ LOG(INFO, "%s Negedge upper of %s -> %f negedge: %f avg_encoder: %f\n",
+ name_, hall_effect_name, *edge_angle, this_sensor.negedge_value(),
+ average_last_encoder);
+ } else {
+ *edge_angle = angles.lower_decreasing_angle;
+ LOG(INFO, "%s Negedge lower of %s -> %f negedge: %f avg_encoder: %f\n",
+ name_, hall_effect_name, *edge_angle, this_sensor.negedge_value(),
+ average_last_encoder);
+ }
+ *edge_encoder = this_sensor.negedge_value();
+ found_edge = true;
+ }
+ }
+
+ return found_edge;
+}
+
+bool ZeroedStateFeedbackLoop::GetPositionOfEdge(
+ const constants::Values::Claws::Claw &claw_values, double *edge_encoder,
+ double *edge_angle) {
+ if (DoGetPositionOfEdge(claw_values.front, edge_encoder, edge_angle, front_,
+ calibration_, back_, "front")) {
+ return true;
+ }
+ if (DoGetPositionOfEdge(claw_values.calibration, edge_encoder, edge_angle,
+ calibration_, front_, back_, "calibration")) {
+ return true;
+ }
+ if (DoGetPositionOfEdge(claw_values.back, edge_encoder, edge_angle, back_,
+ calibration_, front_, "back")) {
+ return true;
+ }
+ return false;
+}
+
+void TopZeroedStateFeedbackLoop::SetCalibration(double edge_encoder,
+ double edge_angle) {
+ double old_offset = offset_;
+ offset_ = edge_angle - edge_encoder;
+ const double doffset = offset_ - old_offset;
+ motor_->ChangeTopOffset(doffset);
+}
+
+double TopZeroedStateFeedbackLoop::ComputeCalibrationChange(double edge_encoder,
+ double edge_angle) {
+ const double offset = edge_angle - edge_encoder;
+ const double doffset = offset - offset_;
+ return doffset;
+}
+
+void BottomZeroedStateFeedbackLoop::SetCalibration(double edge_encoder,
+ double edge_angle) {
+ double old_offset = offset_;
+ offset_ = edge_angle - edge_encoder;
+ const double doffset = offset_ - old_offset;
+ motor_->ChangeBottomOffset(doffset);
+}
+
+double BottomZeroedStateFeedbackLoop::ComputeCalibrationChange(
+ double edge_encoder, double edge_angle) {
+ const double offset = edge_angle - edge_encoder;
+ const double doffset = offset - offset_;
+ return doffset;
+}
+
+void ClawMotor::ChangeTopOffset(double doffset) {
+ claw_.ChangeTopOffset(doffset);
+ if (has_top_claw_goal_) {
+ top_claw_goal_ += doffset;
+ }
+}
+
+void ClawMotor::ChangeBottomOffset(double doffset) {
+ claw_.ChangeBottomOffset(doffset);
+ if (has_bottom_claw_goal_) {
+ bottom_claw_goal_ += doffset;
+ }
+}
+
+void ClawLimitedLoop::ChangeTopOffset(double doffset) {
+ mutable_X_hat()(1, 0) += doffset;
+ LOG(INFO, "Changing top offset by %f\n", doffset);
+}
+void ClawLimitedLoop::ChangeBottomOffset(double doffset) {
+ mutable_X_hat()(0, 0) += doffset;
+ mutable_X_hat()(1, 0) -= doffset;
+ LOG(INFO, "Changing bottom offset by %f\n", doffset);
+}
+
+void LimitClawGoal(double *bottom_goal, double *top_goal,
+ const frc971::constants::Values &values) {
+ // first update position based on angle limit
+ const double separation = *top_goal - *bottom_goal;
+ if (separation > values.claw.soft_max_separation) {
+ LOG_STRUCT(DEBUG, "before", ClawPositionToLog(*top_goal, *bottom_goal));
+ const double dsep = (separation - values.claw.soft_max_separation) / 2.0;
+ *bottom_goal += dsep;
+ *top_goal -= dsep;
+ LOG_STRUCT(DEBUG, "after", ClawPositionToLog(*top_goal, *bottom_goal));
+ }
+ if (separation < values.claw.soft_min_separation) {
+ LOG_STRUCT(DEBUG, "before", ClawPositionToLog(*top_goal, *bottom_goal));
+ const double dsep = (separation - values.claw.soft_min_separation) / 2.0;
+ *bottom_goal += dsep;
+ *top_goal -= dsep;
+ LOG_STRUCT(DEBUG, "after", ClawPositionToLog(*top_goal, *bottom_goal));
+ }
+
+ // now move both goals in unison
+ if (*bottom_goal < values.claw.lower_claw.lower_limit) {
+ LOG_STRUCT(DEBUG, "before", ClawPositionToLog(*top_goal, *bottom_goal));
+ *top_goal += values.claw.lower_claw.lower_limit - *bottom_goal;
+ *bottom_goal = values.claw.lower_claw.lower_limit;
+ LOG_STRUCT(DEBUG, "after", ClawPositionToLog(*top_goal, *bottom_goal));
+ }
+ if (*bottom_goal > values.claw.lower_claw.upper_limit) {
+ LOG_STRUCT(DEBUG, "before", ClawPositionToLog(*top_goal, *bottom_goal));
+ *top_goal -= *bottom_goal - values.claw.lower_claw.upper_limit;
+ *bottom_goal = values.claw.lower_claw.upper_limit;
+ LOG_STRUCT(DEBUG, "after", ClawPositionToLog(*top_goal, *bottom_goal));
+ }
+
+ if (*top_goal < values.claw.upper_claw.lower_limit) {
+ LOG_STRUCT(DEBUG, "before", ClawPositionToLog(*top_goal, *bottom_goal));
+ *bottom_goal += values.claw.upper_claw.lower_limit - *top_goal;
+ *top_goal = values.claw.upper_claw.lower_limit;
+ LOG_STRUCT(DEBUG, "after", ClawPositionToLog(*top_goal, *bottom_goal));
+ }
+ if (*top_goal > values.claw.upper_claw.upper_limit) {
+ LOG_STRUCT(DEBUG, "before", ClawPositionToLog(*top_goal, *bottom_goal));
+ *bottom_goal -= *top_goal - values.claw.upper_claw.upper_limit;
+ *top_goal = values.claw.upper_claw.upper_limit;
+ LOG_STRUCT(DEBUG, "after", ClawPositionToLog(*top_goal, *bottom_goal));
+ }
+}
+
+bool ClawMotor::is_ready() const {
+ return (
+ (top_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED &&
+ bottom_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED) ||
+ (((::aos::joystick_state.get() == NULL)
+ ? true
+ : ::aos::joystick_state->autonomous) &&
+ ((top_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED ||
+ top_claw_.zeroing_state() ==
+ ZeroedStateFeedbackLoop::DISABLED_CALIBRATION) &&
+ (bottom_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED ||
+ bottom_claw_.zeroing_state() ==
+ ZeroedStateFeedbackLoop::DISABLED_CALIBRATION))));
+}
+
+bool ClawMotor::is_zeroing() const { return !is_ready(); }
+
+// Positive angle is up, and positive power is up.
+void ClawMotor::RunIteration(const control_loops::ClawQueue::Goal *goal,
+ const control_loops::ClawQueue::Position *position,
+ control_loops::ClawQueue::Output *output,
+ control_loops::ClawQueue::Status *status) {
+ constexpr double dt = 0.01;
+
+ // Disable the motors now so that all early returns will return with the
+ // motors disabled.
+ if (output) {
+ output->top_claw_voltage = 0;
+ output->bottom_claw_voltage = 0;
+ output->intake_voltage = 0;
+ output->tusk_voltage = 0;
+ }
+
+ if (goal) {
+ if (::std::isnan(goal->bottom_angle) ||
+ ::std::isnan(goal->separation_angle) || ::std::isnan(goal->intake) ||
+ ::std::isnan(goal->centering)) {
+ return;
+ }
+ }
+
+ if (WasReset()) {
+ top_claw_.Reset(position->top);
+ bottom_claw_.Reset(position->bottom);
+ }
+
+ const frc971::constants::Values &values = constants::GetValues();
+
+ if (position) {
+ Eigen::Matrix<double, 2, 1> Y;
+ Y << position->bottom.position + bottom_claw_.offset(),
+ position->top.position + top_claw_.offset();
+ claw_.Correct(Y);
+
+ top_claw_.SetPositionValues(position->top);
+ bottom_claw_.SetPositionValues(position->bottom);
+
+ if (!has_top_claw_goal_) {
+ has_top_claw_goal_ = true;
+ top_claw_goal_ = top_claw_.absolute_position();
+ initial_separation_ =
+ top_claw_.absolute_position() - bottom_claw_.absolute_position();
+ }
+ if (!has_bottom_claw_goal_) {
+ has_bottom_claw_goal_ = true;
+ bottom_claw_goal_ = bottom_claw_.absolute_position();
+ initial_separation_ =
+ top_claw_.absolute_position() - bottom_claw_.absolute_position();
+ }
+ LOG_STRUCT(DEBUG, "absolute position",
+ ClawPositionToLog(top_claw_.absolute_position(),
+ bottom_claw_.absolute_position()));
+ }
+
+ bool autonomous, enabled;
+ if (::aos::joystick_state.get() == nullptr) {
+ autonomous = true;
+ enabled = false;
+ } else {
+ autonomous = ::aos::joystick_state->autonomous;
+ enabled = ::aos::joystick_state->enabled;
+ }
+
+ double bottom_claw_velocity_ = 0.0;
+ double top_claw_velocity_ = 0.0;
+
+ if (goal != NULL &&
+ ((top_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED &&
+ bottom_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED) ||
+ (autonomous &&
+ ((top_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED ||
+ top_claw_.zeroing_state() ==
+ ZeroedStateFeedbackLoop::DISABLED_CALIBRATION) &&
+ (bottom_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED ||
+ bottom_claw_.zeroing_state() ==
+ ZeroedStateFeedbackLoop::DISABLED_CALIBRATION))))) {
+ // Ready to use the claw.
+ // Limit the goals here.
+ bottom_claw_goal_ = goal->bottom_angle;
+ top_claw_goal_ = goal->bottom_angle + goal->separation_angle;
+ has_bottom_claw_goal_ = true;
+ has_top_claw_goal_ = true;
+ doing_calibration_fine_tune_ = false;
+ mode_ = READY;
+
+ bottom_claw_.HandleCalibrationError(values.claw.lower_claw);
+ top_claw_.HandleCalibrationError(values.claw.upper_claw);
+ } else if (top_claw_.zeroing_state() !=
+ ZeroedStateFeedbackLoop::UNKNOWN_POSITION &&
+ bottom_claw_.zeroing_state() !=
+ ZeroedStateFeedbackLoop::UNKNOWN_POSITION) {
+ // Time to fine tune the zero.
+ // Limit the goals here.
+ if (!enabled) {
+ // If we are disabled, start the fine tune process over again.
+ doing_calibration_fine_tune_ = false;
+ }
+ if (bottom_claw_.zeroing_state() != ZeroedStateFeedbackLoop::CALIBRATED) {
+ // always get the bottom claw to calibrated first
+ LOG(DEBUG, "Calibrating the bottom of the claw\n");
+ if (!doing_calibration_fine_tune_) {
+ if (::std::abs(bottom_absolute_position() -
+ values.claw.start_fine_tune_pos) <
+ values.claw.claw_unimportant_epsilon) {
+ doing_calibration_fine_tune_ = true;
+ bottom_claw_goal_ += values.claw.claw_zeroing_speed * dt;
+ top_claw_velocity_ = bottom_claw_velocity_ =
+ values.claw.claw_zeroing_speed;
+ LOG(DEBUG, "Ready to fine tune the bottom\n");
+ mode_ = FINE_TUNE_BOTTOM;
+ } else {
+ // send bottom to zeroing start
+ bottom_claw_goal_ = values.claw.start_fine_tune_pos;
+ LOG(DEBUG, "Going to the start position for the bottom\n");
+ mode_ = PREP_FINE_TUNE_BOTTOM;
+ }
+ } else {
+ mode_ = FINE_TUNE_BOTTOM;
+ bottom_claw_goal_ += values.claw.claw_zeroing_speed * dt;
+ top_claw_velocity_ = bottom_claw_velocity_ =
+ values.claw.claw_zeroing_speed;
+ if (top_claw_.front_or_back_triggered() ||
+ bottom_claw_.front_or_back_triggered()) {
+ // We shouldn't hit a limit, but if we do, go back to the zeroing
+ // point and try again.
+ doing_calibration_fine_tune_ = false;
+ bottom_claw_goal_ = values.claw.start_fine_tune_pos;
+ top_claw_velocity_ = bottom_claw_velocity_ = 0.0;
+ LOG(DEBUG, "Found a limit, starting over.\n");
+ mode_ = PREP_FINE_TUNE_BOTTOM;
+ }
+
+ if (position && bottom_claw_.SawFilteredPosedge(
+ bottom_claw_.calibration(), bottom_claw_.front(),
+ bottom_claw_.back())) {
+ // do calibration
+ bottom_claw_.SetCalibration(
+ position->bottom.calibration.posedge_value,
+ values.claw.lower_claw.calibration.lower_angle);
+ bottom_claw_.set_zeroing_state(ZeroedStateFeedbackLoop::CALIBRATED);
+ // calibrated so we are done fine tuning bottom
+ doing_calibration_fine_tune_ = false;
+ LOG(DEBUG, "Calibrated the bottom correctly!\n");
+ } else if (bottom_claw_.calibration().last_value()) {
+ LOG(DEBUG, "Aborting bottom fine tune because sensor triggered\n");
+ doing_calibration_fine_tune_ = false;
+ bottom_claw_.set_zeroing_state(
+ ZeroedStateFeedbackLoop::UNKNOWN_POSITION);
+ } else {
+ LOG(DEBUG, "Fine tuning\n");
+ }
+ }
+ // now set the top claw to track
+
+ top_claw_goal_ = bottom_claw_goal_ + values.claw.claw_zeroing_separation;
+ } else {
+ // bottom claw must be calibrated, start on the top
+ if (!doing_calibration_fine_tune_) {
+ if (::std::abs(top_absolute_position() -
+ values.claw.start_fine_tune_pos) <
+ values.claw.claw_unimportant_epsilon) {
+ doing_calibration_fine_tune_ = true;
+ top_claw_goal_ += values.claw.claw_zeroing_speed * dt;
+ top_claw_velocity_ = bottom_claw_velocity_ =
+ values.claw.claw_zeroing_speed;
+ LOG(DEBUG, "Ready to fine tune the top\n");
+ mode_ = FINE_TUNE_TOP;
+ } else {
+ // send top to zeroing start
+ top_claw_goal_ = values.claw.start_fine_tune_pos;
+ LOG(DEBUG, "Going to the start position for the top\n");
+ mode_ = PREP_FINE_TUNE_TOP;
+ }
+ } else {
+ mode_ = FINE_TUNE_TOP;
+ top_claw_goal_ += values.claw.claw_zeroing_speed * dt;
+ top_claw_velocity_ = bottom_claw_velocity_ =
+ values.claw.claw_zeroing_speed;
+ if (top_claw_.front_or_back_triggered() ||
+ bottom_claw_.front_or_back_triggered()) {
+ // this should not happen, but now we know it won't
+ doing_calibration_fine_tune_ = false;
+ top_claw_goal_ = values.claw.start_fine_tune_pos;
+ top_claw_velocity_ = bottom_claw_velocity_ = 0.0;
+ LOG(DEBUG, "Found a limit, starting over.\n");
+ mode_ = PREP_FINE_TUNE_TOP;
+ }
+
+ if (position &&
+ top_claw_.SawFilteredPosedge(top_claw_.calibration(),
+ top_claw_.front(), top_claw_.back())) {
+ // do calibration
+ top_claw_.SetCalibration(
+ position->top.calibration.posedge_value,
+ values.claw.upper_claw.calibration.lower_angle);
+ top_claw_.set_zeroing_state(ZeroedStateFeedbackLoop::CALIBRATED);
+ // calibrated so we are done fine tuning top
+ doing_calibration_fine_tune_ = false;
+ LOG(DEBUG, "Calibrated the top correctly!\n");
+ } else if (top_claw_.calibration().last_value()) {
+ LOG(DEBUG, "Aborting top fine tune because sensor triggered\n");
+ doing_calibration_fine_tune_ = false;
+ top_claw_.set_zeroing_state(
+ ZeroedStateFeedbackLoop::UNKNOWN_POSITION);
+ }
+ }
+ // now set the bottom claw to track
+ bottom_claw_goal_ = top_claw_goal_ - values.claw.claw_zeroing_separation;
+ }
+ } else {
+ doing_calibration_fine_tune_ = false;
+ if (!was_enabled_ && enabled) {
+ if (position) {
+ top_claw_goal_ = position->top.position + top_claw_.offset();
+ bottom_claw_goal_ = position->bottom.position + bottom_claw_.offset();
+ initial_separation_ =
+ position->top.position - position->bottom.position;
+ } else {
+ has_top_claw_goal_ = false;
+ has_bottom_claw_goal_ = false;
+ }
+ }
+
+ if ((bottom_claw_.zeroing_state() !=
+ ZeroedStateFeedbackLoop::UNKNOWN_POSITION ||
+ bottom_claw_.front().value() || top_claw_.front().value()) &&
+ !top_claw_.back().value() && !bottom_claw_.back().value()) {
+ if (enabled) {
+ // Time to slowly move back up to find any position to narrow down the
+ // zero.
+ top_claw_goal_ += values.claw.claw_zeroing_off_speed * dt;
+ bottom_claw_goal_ += values.claw.claw_zeroing_off_speed * dt;
+ top_claw_velocity_ = bottom_claw_velocity_ =
+ values.claw.claw_zeroing_off_speed;
+ LOG(DEBUG, "Bottom is known.\n");
+ }
+ } else {
+ // We don't know where either claw is. Slowly start moving down to find
+ // any hall effect.
+ if (enabled) {
+ top_claw_goal_ -= values.claw.claw_zeroing_off_speed * dt;
+ bottom_claw_goal_ -= values.claw.claw_zeroing_off_speed * dt;
+ top_claw_velocity_ = bottom_claw_velocity_ =
+ -values.claw.claw_zeroing_off_speed;
+ LOG(DEBUG, "Both are unknown.\n");
+ }
+ }
+
+ if (position) {
+ if (enabled) {
+ top_claw_.SetCalibrationOnEdge(
+ values.claw.upper_claw,
+ ZeroedStateFeedbackLoop::APPROXIMATE_CALIBRATION);
+ bottom_claw_.SetCalibrationOnEdge(
+ values.claw.lower_claw,
+ ZeroedStateFeedbackLoop::APPROXIMATE_CALIBRATION);
+ } else {
+ // TODO(austin): Only calibrate on the predetermined edge.
+ // We might be able to just ignore this since the backlash is soooo
+ // low.
+ // :)
+ top_claw_.SetCalibrationOnEdge(
+ values.claw.upper_claw,
+ ZeroedStateFeedbackLoop::DISABLED_CALIBRATION);
+ bottom_claw_.SetCalibrationOnEdge(
+ values.claw.lower_claw,
+ ZeroedStateFeedbackLoop::DISABLED_CALIBRATION);
+ }
+ }
+ mode_ = UNKNOWN_LOCATION;
+ }
+
+ // Limit the goals if both claws have been (mostly) found.
+ if (mode_ != UNKNOWN_LOCATION) {
+ LimitClawGoal(&bottom_claw_goal_, &top_claw_goal_, values);
+ }
+
+ claw_.set_positions_known(
+ top_claw_.zeroing_state() != ZeroedStateFeedbackLoop::UNKNOWN_POSITION,
+ bottom_claw_.zeroing_state() !=
+ ZeroedStateFeedbackLoop::UNKNOWN_POSITION);
+ if (has_top_claw_goal_ && has_bottom_claw_goal_) {
+ claw_.mutable_R() << bottom_claw_goal_, top_claw_goal_ - bottom_claw_goal_,
+ bottom_claw_velocity_, top_claw_velocity_ - bottom_claw_velocity_;
+ LOG_MATRIX(DEBUG, "actual goal", claw_.R());
+
+ // Only cap power when one of the halves of the claw is moving slowly and
+ // could wind up.
+ claw_.set_is_zeroing(mode_ == UNKNOWN_LOCATION || mode_ == FINE_TUNE_TOP ||
+ mode_ == FINE_TUNE_BOTTOM);
+ claw_.Update(output == nullptr);
+ } else {
+ claw_.Update(true);
+ }
+
+ capped_goal_ = false;
+ switch (mode_) {
+ case READY:
+ case PREP_FINE_TUNE_TOP:
+ case PREP_FINE_TUNE_BOTTOM:
+ break;
+ case FINE_TUNE_BOTTOM:
+ case FINE_TUNE_TOP:
+ case UNKNOWN_LOCATION: {
+ if (claw_.uncapped_average_voltage() > values.claw.max_zeroing_voltage) {
+ double dx_bot = (claw_.U_uncapped(0, 0) -
+ values.claw.max_zeroing_voltage) /
+ claw_.K(0, 0);
+ double dx_top = (claw_.U_uncapped(1, 0) -
+ values.claw.max_zeroing_voltage) /
+ claw_.K(0, 0);
+ double dx = ::std::max(dx_top, dx_bot);
+ bottom_claw_goal_ -= dx;
+ top_claw_goal_ -= dx;
+ Eigen::Matrix<double, 4, 1> R;
+ R << bottom_claw_goal_, top_claw_goal_ - bottom_claw_goal_, claw_.R(2, 0),
+ claw_.R(3, 0);
+ claw_.mutable_U() = claw_.K() * (R - claw_.X_hat());
+ capped_goal_ = true;
+ LOG(DEBUG, "Moving the goal by %f to prevent windup."
+ " Uncapped is %f, max is %f, difference is %f\n",
+ dx,
+ claw_.uncapped_average_voltage(), values.claw.max_zeroing_voltage,
+ (claw_.uncapped_average_voltage() -
+ values.claw.max_zeroing_voltage));
+ } else if (claw_.uncapped_average_voltage() <
+ -values.claw.max_zeroing_voltage) {
+ double dx_bot = (claw_.U_uncapped(0, 0) +
+ values.claw.max_zeroing_voltage) /
+ claw_.K(0, 0);
+ double dx_top = (claw_.U_uncapped(1, 0) +
+ values.claw.max_zeroing_voltage) /
+ claw_.K(0, 0);
+ double dx = ::std::min(dx_top, dx_bot);
+ bottom_claw_goal_ -= dx;
+ top_claw_goal_ -= dx;
+ Eigen::Matrix<double, 4, 1> R;
+ R << bottom_claw_goal_, top_claw_goal_ - bottom_claw_goal_, claw_.R(2, 0),
+ claw_.R(3, 0);
+ claw_.mutable_U() = claw_.K() * (R - claw_.X_hat());
+ capped_goal_ = true;
+ LOG(DEBUG, "Moving the goal by %f to prevent windup\n", dx);
+ }
+ } break;
+ }
+
+ if (output) {
+ if (goal) {
+ //setup the intake
+ output->intake_voltage =
+ (goal->intake > 12.0) ? 12 : (goal->intake < -12.0) ? -12.0
+ : goal->intake;
+ output->tusk_voltage = goal->centering;
+ output->tusk_voltage =
+ (goal->centering > 12.0) ? 12 : (goal->centering < -12.0)
+ ? -12.0
+ : goal->centering;
+ }
+ output->top_claw_voltage = claw_.U(1, 0);
+ output->bottom_claw_voltage = claw_.U(0, 0);
+
+ if (output->top_claw_voltage > kMaxVoltage) {
+ output->top_claw_voltage = kMaxVoltage;
+ } else if (output->top_claw_voltage < -kMaxVoltage) {
+ output->top_claw_voltage = -kMaxVoltage;
+ }
+
+ if (output->bottom_claw_voltage > kMaxVoltage) {
+ output->bottom_claw_voltage = kMaxVoltage;
+ } else if (output->bottom_claw_voltage < -kMaxVoltage) {
+ output->bottom_claw_voltage = -kMaxVoltage;
+ }
+ }
+
+ status->bottom = bottom_absolute_position();
+ status->separation = top_absolute_position() - bottom_absolute_position();
+ status->bottom_velocity = claw_.X_hat(2, 0);
+ status->separation_velocity = claw_.X_hat(3, 0);
+
+ if (goal) {
+ bool bottom_done =
+ ::std::abs(bottom_absolute_position() - goal->bottom_angle) < 0.020;
+ bool bottom_velocity_done = ::std::abs(status->bottom_velocity) < 0.2;
+ bool separation_done =
+ ::std::abs((top_absolute_position() - bottom_absolute_position()) -
+ goal->separation_angle) < 0.020;
+ bool separation_done_with_ball =
+ ::std::abs((top_absolute_position() - bottom_absolute_position()) -
+ goal->separation_angle) < 0.06;
+ status->done = is_ready() && separation_done && bottom_done && bottom_velocity_done;
+ status->done_with_ball =
+ is_ready() && separation_done_with_ball && bottom_done && bottom_velocity_done;
+ } else {
+ status->done = status->done_with_ball = false;
+ }
+
+ status->zeroed = is_ready();
+ status->zeroed_for_auto =
+ (top_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED ||
+ top_claw_.zeroing_state() ==
+ ZeroedStateFeedbackLoop::DISABLED_CALIBRATION) &&
+ (bottom_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED ||
+ bottom_claw_.zeroing_state() ==
+ ZeroedStateFeedbackLoop::DISABLED_CALIBRATION);
+
+ was_enabled_ = enabled;
+}
+
+} // namespace control_loops
+} // namespace frc971
+
diff --git a/y2014/control_loops/claw/claw.gyp b/y2014/control_loops/claw/claw.gyp
new file mode 100644
index 0000000..8a6a7c5
--- /dev/null
+++ b/y2014/control_loops/claw/claw.gyp
@@ -0,0 +1,99 @@
+{
+ 'targets': [
+ {
+ 'target_name': 'replay_claw',
+ 'type': 'executable',
+ 'variables': {
+ 'no_rsync': 1,
+ },
+ 'sources': [
+ 'replay_claw.cc',
+ ],
+ 'dependencies': [
+ 'claw_queue',
+ '<(AOS)/common/controls/controls.gyp:replay_control_loop',
+ '<(AOS)/linux_code/linux_code.gyp:init',
+ ],
+ },
+ {
+ 'target_name': 'claw_queue',
+ 'type': 'static_library',
+ 'sources': ['claw.q'],
+ 'variables': {
+ 'header_path': 'y2014/control_loops/claw',
+ },
+ 'dependencies': [
+ '<(AOS)/common/controls/controls.gyp:control_loop_queues',
+ '<(DEPTH)/frc971/control_loops/control_loops.gyp:queues',
+ ],
+ 'export_dependent_settings': [
+ '<(AOS)/common/controls/controls.gyp:control_loop_queues',
+ '<(DEPTH)/frc971/control_loops/control_loops.gyp:queues',
+ ],
+ 'includes': ['../../../aos/build/queues.gypi'],
+ },
+ {
+ 'target_name': 'claw_lib',
+ 'type': 'static_library',
+ 'sources': [
+ 'claw.cc',
+ 'claw_motor_plant.cc',
+ ],
+ 'dependencies': [
+ 'claw_queue',
+ '<(AOS)/common/controls/controls.gyp:control_loop',
+ '<(DEPTH)/y2014/y2014.gyp:constants',
+ '<(DEPTH)/frc971/control_loops/control_loops.gyp:state_feedback_loop',
+ '<(AOS)/common/controls/controls.gyp:polytope',
+ '<(DEPTH)/frc971/control_loops/control_loops.gyp:coerce_goal',
+ '<(AOS)/common/logging/logging.gyp:queue_logging',
+ '<(AOS)/common/logging/logging.gyp:matrix_logging',
+ ],
+ 'export_dependent_settings': [
+ 'claw_queue',
+ '<(AOS)/common/controls/controls.gyp:control_loop',
+ '<(DEPTH)/frc971/control_loops/control_loops.gyp:state_feedback_loop',
+ '<(AOS)/common/controls/controls.gyp:polytope',
+ '<(DEPTH)/frc971/control_loops/control_loops.gyp:coerce_goal',
+ ],
+ },
+ {
+ 'target_name': 'claw_lib_test',
+ 'type': 'executable',
+ 'sources': [
+ 'claw_lib_test.cc',
+ ],
+ 'dependencies': [
+ '<(EXTERNALS):gtest',
+ 'claw_queue',
+ 'claw_lib',
+ '<(DEPTH)/frc971/control_loops/control_loops.gyp:state_feedback_loop',
+ '<(AOS)/common/controls/controls.gyp:control_loop_test',
+ ],
+ },
+ {
+ 'target_name': 'claw_calibration',
+ 'type': 'executable',
+ 'sources': [
+ 'claw_calibration.cc',
+ ],
+ 'dependencies': [
+ '<(AOS)/linux_code/linux_code.gyp:init',
+ 'claw_queue',
+ '<(AOS)/common/controls/controls.gyp:control_loop',
+ '<(DEPTH)/y2014/y2014.gyp:constants',
+ ],
+ },
+ {
+ 'target_name': 'claw',
+ 'type': 'executable',
+ 'sources': [
+ 'claw_main.cc',
+ ],
+ 'dependencies': [
+ '<(AOS)/linux_code/linux_code.gyp:init',
+ 'claw_lib',
+ ],
+ },
+ ],
+}
diff --git a/y2014/control_loops/claw/claw.h b/y2014/control_loops/claw/claw.h
new file mode 100644
index 0000000..cdd3948
--- /dev/null
+++ b/y2014/control_loops/claw/claw.h
@@ -0,0 +1,265 @@
+#ifndef Y2014_CONTROL_LOOPS_CLAW_CLAW_H_
+#define Y2014_CONTROL_LOOPS_CLAW_CLAW_H_
+
+#include <memory>
+
+#include "aos/common/controls/control_loop.h"
+#include "aos/common/controls/polytope.h"
+#include "y2014/constants.h"
+#include "frc971/control_loops/state_feedback_loop.h"
+#include "frc971/control_loops/coerce_goal.h"
+#include "y2014/control_loops/claw/claw.q.h"
+#include "y2014/control_loops/claw/claw_motor_plant.h"
+#include "frc971/control_loops/hall_effect_tracker.h"
+
+namespace frc971 {
+namespace control_loops {
+namespace testing {
+class WindupClawTest;
+};
+
+// Note: Everything in this file assumes that there is a 1 cycle delay between
+// power being requested and it showing up at the motor. It assumes that
+// X_hat(2, 1) is the voltage being applied as well. It will go unstable if
+// that isn't true.
+
+class ClawLimitedLoop : public StateFeedbackLoop<4, 2, 2> {
+ public:
+ ClawLimitedLoop(StateFeedbackLoop<4, 2, 2> &&loop);
+ virtual void CapU();
+
+ void set_is_zeroing(bool is_zeroing) { is_zeroing_ = is_zeroing; }
+ void set_positions_known(bool top_known, bool bottom_known) {
+ top_known_ = top_known;
+ bottom_known_ = bottom_known;
+ }
+
+ void ChangeTopOffset(double doffset);
+ void ChangeBottomOffset(double doffset);
+
+ double uncapped_average_voltage() const { return uncapped_average_voltage_; }
+
+ private:
+ double uncapped_average_voltage_;
+ bool is_zeroing_;
+
+ bool top_known_ = false, bottom_known_ = false;
+
+ const ::aos::controls::HPolytope<2> U_Poly_, U_Poly_zeroing_;
+};
+
+class ClawMotor;
+
+// This class implements the CapU function correctly given all the extra
+// information that we know about from the wrist motor.
+// It does not have any zeroing logic in it, only logic to deal with a delta U
+// controller.
+class ZeroedStateFeedbackLoop {
+ public:
+ ZeroedStateFeedbackLoop(const char *name, ClawMotor *motor);
+
+ const static int kZeroingMaxVoltage = 5;
+
+ enum JointZeroingState {
+ // We don't know where the joint is at all.
+ UNKNOWN_POSITION,
+ // We have an approximate position for where the claw is using.
+ APPROXIMATE_CALIBRATION,
+ // We observed the calibration edge while disabled. This is good enough for
+ // autonomous mode.
+ DISABLED_CALIBRATION,
+ // Ready for use during teleop.
+ CALIBRATED
+ };
+
+ void set_zeroing_state(JointZeroingState zeroing_state) {
+ zeroing_state_ = zeroing_state;
+ }
+ JointZeroingState zeroing_state() const { return zeroing_state_; }
+
+ void SetPositionValues(const HalfClawPosition &claw);
+
+ void Reset(const HalfClawPosition &claw);
+
+ double absolute_position() const { return encoder() + offset(); }
+
+ const HallEffectTracker &front() const { return front_; }
+ const HallEffectTracker &calibration() const { return calibration_; }
+ const HallEffectTracker &back() const { return back_; }
+
+ bool any_hall_effect_changed() const {
+ return front().either_count_changed() ||
+ calibration().either_count_changed() ||
+ back().either_count_changed();
+ }
+ bool front_or_back_triggered() const {
+ return front().value() || back().value();
+ }
+ bool any_triggered() const {
+ return calibration().value() || front().value() || back().value();
+ }
+
+ double encoder() const { return encoder_; }
+ double last_encoder() const { return last_encoder_; }
+
+ double offset() const { return offset_; }
+
+ // Returns true if an edge was detected in the last cycle and then sets the
+ // edge_position to the absolute position of the edge.
+ bool GetPositionOfEdge(const constants::Values::Claws::Claw &claw,
+ double *edge_encoder, double *edge_angle);
+
+ bool SawFilteredPosedge(const HallEffectTracker &this_sensor,
+ const HallEffectTracker &sensorA,
+ const HallEffectTracker &sensorB);
+
+ bool SawFilteredNegedge(const HallEffectTracker &this_sensor,
+ const HallEffectTracker &sensorA,
+ const HallEffectTracker &sensorB);
+
+#undef COUNT_SETTER_GETTER
+
+ protected:
+ // The accumulated voltage to apply to the motor.
+ double offset_;
+ const char *name_;
+
+ ClawMotor *motor_;
+
+ HallEffectTracker front_, calibration_, back_;
+
+ JointZeroingState zeroing_state_;
+ double min_hall_effect_on_angle_;
+ double max_hall_effect_on_angle_;
+ double min_hall_effect_off_angle_;
+ double max_hall_effect_off_angle_;
+ double encoder_;
+ double last_encoder_;
+ double last_on_encoder_;
+ double last_off_encoder_;
+ bool any_triggered_last_;
+
+ const HallEffectTracker* posedge_filter_ = nullptr;
+ const HallEffectTracker* negedge_filter_ = nullptr;
+
+ private:
+ // Does the edges of 1 sensor for GetPositionOfEdge.
+ bool DoGetPositionOfEdge(const constants::Values::Claws::AnglePair &angles,
+ double *edge_encoder, double *edge_angle,
+ const HallEffectTracker &sensor,
+ const HallEffectTracker &sensorA,
+ const HallEffectTracker &sensorB,
+ const char *hall_effect_name);
+};
+
+class TopZeroedStateFeedbackLoop : public ZeroedStateFeedbackLoop {
+ public:
+ TopZeroedStateFeedbackLoop(ClawMotor *motor)
+ : ZeroedStateFeedbackLoop("top", motor) {}
+ // Sets the calibration offset given the absolute angle and the corresponding
+ // encoder value.
+ void SetCalibration(double edge_encoder, double edge_angle);
+
+ bool SetCalibrationOnEdge(const constants::Values::Claws::Claw &claw_values,
+ JointZeroingState zeroing_state);
+ double ComputeCalibrationChange(double edge_encoder, double edge_angle);
+ void HandleCalibrationError(
+ const constants::Values::Claws::Claw &claw_values);
+};
+
+class BottomZeroedStateFeedbackLoop : public ZeroedStateFeedbackLoop {
+ public:
+ BottomZeroedStateFeedbackLoop(ClawMotor *motor)
+ : ZeroedStateFeedbackLoop("bottom", motor) {}
+ // Sets the calibration offset given the absolute angle and the corrisponding
+ // encoder value.
+ void SetCalibration(double edge_encoder, double edge_angle);
+
+ bool SetCalibrationOnEdge(const constants::Values::Claws::Claw &claw_values,
+ JointZeroingState zeroing_state);
+ double ComputeCalibrationChange(double edge_encoder, double edge_angle);
+ void HandleCalibrationError(
+ const constants::Values::Claws::Claw &claw_values);
+};
+
+class ClawMotor : public aos::controls::ControlLoop<control_loops::ClawQueue> {
+ public:
+ explicit ClawMotor(control_loops::ClawQueue *my_claw =
+ &control_loops::claw_queue);
+
+ // True if the state machine is ready.
+ bool capped_goal() const { return capped_goal_; }
+
+ double uncapped_average_voltage() const {
+ return claw_.uncapped_average_voltage();
+ }
+
+ // True if the claw is zeroing.
+ bool is_zeroing() const;
+
+ // True if the state machine is ready.
+ bool is_ready() const;
+
+ void ChangeTopOffset(double doffset);
+ void ChangeBottomOffset(double doffset);
+
+ enum CalibrationMode {
+ READY,
+ PREP_FINE_TUNE_TOP,
+ FINE_TUNE_TOP,
+ PREP_FINE_TUNE_BOTTOM,
+ FINE_TUNE_BOTTOM,
+ UNKNOWN_LOCATION
+ };
+
+ CalibrationMode mode() const { return mode_; }
+
+ protected:
+ virtual void RunIteration(const control_loops::ClawQueue::Goal *goal,
+ const control_loops::ClawQueue::Position *position,
+ control_loops::ClawQueue::Output *output,
+ control_loops::ClawQueue::Status *status);
+
+ double top_absolute_position() const {
+ return claw_.X_hat(1, 0) + claw_.X_hat(0, 0);
+ }
+ double bottom_absolute_position() const { return claw_.X_hat(0, 0); }
+
+ private:
+ // Friend the test classes for acces to the internal state.
+ friend class testing::WindupClawTest;
+
+ // The zeroed joint to use.
+ bool has_top_claw_goal_;
+ double top_claw_goal_;
+ TopZeroedStateFeedbackLoop top_claw_;
+
+ bool has_bottom_claw_goal_;
+ double bottom_claw_goal_;
+ BottomZeroedStateFeedbackLoop bottom_claw_;
+
+ // The claw loop.
+ ClawLimitedLoop claw_;
+
+ bool was_enabled_;
+ bool doing_calibration_fine_tune_;
+
+ // The initial separation when disabled. Used as the safe separation
+ // distance.
+ double initial_separation_;
+
+ bool capped_goal_;
+ CalibrationMode mode_;
+
+ DISALLOW_COPY_AND_ASSIGN(ClawMotor);
+};
+
+// Modifies the bottom and top goal such that they are within the limits and
+// their separation isn't too much or little.
+void LimitClawGoal(double *bottom_goal, double *top_goal,
+ const frc971::constants::Values &values);
+
+} // namespace control_loops
+} // namespace frc971
+
+#endif // Y2014_CONTROL_LOOPS_CLAW_CLAW_H_
diff --git a/y2014/control_loops/claw/claw.q b/y2014/control_loops/claw/claw.q
new file mode 100644
index 0000000..a116970
--- /dev/null
+++ b/y2014/control_loops/claw/claw.q
@@ -0,0 +1,75 @@
+package frc971.control_loops;
+
+import "aos/common/controls/control_loops.q";
+import "frc971/control_loops/control_loops.q";
+
+struct HalfClawPosition {
+ // The current position of this half of the claw.
+ double position;
+
+ // The hall effect sensor at the front limit.
+ HallEffectStruct front;
+ // The hall effect sensor in the middle to use for real calibration.
+ HallEffectStruct calibration;
+ // The hall effect at the back limit.
+ HallEffectStruct back;
+};
+
+// All angles here are 0 vertical, positive "up" (aka backwards).
+queue_group ClawQueue {
+ implements aos.control_loops.ControlLoop;
+
+ message Goal {
+ // The angle of the bottom claw.
+ double bottom_angle;
+ // How much higher the top claw is.
+ double separation_angle;
+ // top claw intake roller
+ double intake;
+ // bottom claw tusk centering
+ double centering;
+ };
+
+ message Position {
+ // All the top claw information.
+ HalfClawPosition top;
+ // All the bottom claw information.
+ HalfClawPosition bottom;
+ };
+
+ message Output {
+ double intake_voltage;
+ double top_claw_voltage;
+ double bottom_claw_voltage;
+ double tusk_voltage;
+ };
+
+ message Status {
+ // True if zeroed enough for the current period (autonomous or teleop).
+ bool zeroed;
+ // True if zeroed as much as we will force during autonomous.
+ bool zeroed_for_auto;
+ // True if zeroed and within tolerance for separation and bottom angle.
+ bool done;
+ // True if zeroed and within tolerance for separation and bottom angle.
+ // seperation allowance much wider as a ball may be included
+ bool done_with_ball;
+ // Dump the values of the state matrix.
+ double bottom;
+ double bottom_velocity;
+ double separation;
+ double separation_velocity;
+ };
+
+ queue Goal goal;
+ queue Position position;
+ queue Output output;
+ queue Status status;
+};
+
+queue_group ClawQueue claw_queue;
+
+struct ClawPositionToLog {
+ double top;
+ double bottom;
+};
diff --git a/y2014/control_loops/claw/claw_calibration.cc b/y2014/control_loops/claw/claw_calibration.cc
new file mode 100644
index 0000000..772a940
--- /dev/null
+++ b/y2014/control_loops/claw/claw_calibration.cc
@@ -0,0 +1,315 @@
+#include "y2014/control_loops/claw/claw.q.h"
+#include "frc971/control_loops/control_loops.q.h"
+
+#include "aos/linux_code/init.h"
+#include "y2014/constants.h"
+
+namespace frc971 {
+
+typedef constants::Values::Claws Claws;
+
+class Sensor {
+ public:
+ Sensor(const double start_position,
+ const HallEffectStruct &initial_hall_effect)
+ : start_position_(start_position),
+ last_hall_effect_(initial_hall_effect),
+ last_posedge_count_(initial_hall_effect.posedge_count),
+ last_negedge_count_(initial_hall_effect.negedge_count) {
+ last_on_min_position_ = start_position;
+ last_on_max_position_ = start_position;
+ last_off_min_position_ = start_position;
+ last_off_max_position_ = start_position;
+ }
+
+ bool DoGetPositionOfEdge(
+ const control_loops::HalfClawPosition &claw_position,
+ const HallEffectStruct &hall_effect, Claws::AnglePair *limits) {
+ bool print = false;
+
+ if (hall_effect.posedge_count != last_posedge_count_) {
+ const double avg_off_position =
+ (last_off_min_position_ + last_off_max_position_) / 2.0;
+ if (hall_effect.posedge_value < avg_off_position) {
+ printf("Posedge upper current %f posedge %f avg_off %f [%f, %f]\n",
+ claw_position.position, hall_effect.posedge_value,
+ avg_off_position, last_off_min_position_,
+ last_off_max_position_);
+ limits->upper_decreasing_angle =
+ hall_effect.posedge_value - start_position_;
+ } else {
+ printf("Posedge lower current %f posedge %f avg_off %f [%f, %f]\n",
+ claw_position.position, hall_effect.posedge_value,
+ avg_off_position, last_off_min_position_,
+ last_off_max_position_);
+ limits->lower_angle =
+ hall_effect.posedge_value - start_position_;
+ }
+ print = true;
+ }
+ if (hall_effect.negedge_count != last_negedge_count_) {
+ const double avg_on_position =
+ (last_on_min_position_ + last_on_max_position_) / 2.0;
+ if (hall_effect.negedge_value > avg_on_position) {
+ printf("Negedge upper current %f negedge %f last_on %f [%f, %f]\n",
+ claw_position.position, hall_effect.negedge_value,
+ avg_on_position, last_on_min_position_,
+ last_on_max_position_);
+ limits->upper_angle =
+ hall_effect.negedge_value - start_position_;
+ } else {
+ printf("Negedge lower current %f negedge %f last_on %f [%f, %f]\n",
+ claw_position.position, hall_effect.negedge_value,
+ avg_on_position, last_on_min_position_,
+ last_on_max_position_);
+ limits->lower_decreasing_angle =
+ hall_effect.negedge_value - start_position_;
+ }
+ print = true;
+ }
+
+ if (hall_effect.current) {
+ if (!last_hall_effect_.current) {
+ last_on_min_position_ = last_on_max_position_ = claw_position.position;
+ } else {
+ last_on_min_position_ =
+ ::std::min(claw_position.position, last_on_min_position_);
+ last_on_max_position_ =
+ ::std::max(claw_position.position, last_on_max_position_);
+ }
+ } else {
+ if (last_hall_effect_.current) {
+ last_off_min_position_ = last_off_max_position_ =
+ claw_position.position;
+ } else {
+ last_off_min_position_ =
+ ::std::min(claw_position.position, last_off_min_position_);
+ last_off_max_position_ =
+ ::std::max(claw_position.position, last_off_max_position_);
+ }
+ }
+
+ last_hall_effect_ = hall_effect;
+ last_posedge_count_ = hall_effect.posedge_count;
+ last_negedge_count_ = hall_effect.negedge_count;
+
+ return print;
+ }
+
+ private:
+ const double start_position_;
+ HallEffectStruct last_hall_effect_;
+ int32_t last_posedge_count_;
+ int32_t last_negedge_count_;
+ double last_on_min_position_;
+ double last_off_min_position_;
+ double last_on_max_position_;
+ double last_off_max_position_;
+};
+
+class ClawSensors {
+ public:
+ ClawSensors(const double start_position,
+ const control_loops::HalfClawPosition &initial_claw_position)
+ : start_position_(start_position),
+ front_(start_position, initial_claw_position.front),
+ calibration_(start_position, initial_claw_position.calibration),
+ back_(start_position, initial_claw_position.back) {}
+
+ bool GetPositionOfEdge(const control_loops::HalfClawPosition &claw_position,
+ Claws::Claw *claw) {
+
+ bool print = false;
+ if (front_.DoGetPositionOfEdge(claw_position,
+ claw_position.front, &claw->front)) {
+ print = true;
+ } else if (calibration_.DoGetPositionOfEdge(claw_position,
+ claw_position.calibration,
+ &claw->calibration)) {
+ print = true;
+ } else if (back_.DoGetPositionOfEdge(claw_position,
+ claw_position.back, &claw->back)) {
+ print = true;
+ }
+
+ double position = claw_position.position - start_position_;
+
+ if (position > claw->upper_limit) {
+ claw->upper_hard_limit = claw->upper_limit = position;
+ print = true;
+ }
+ if (position < claw->lower_limit) {
+ claw->lower_hard_limit = claw->lower_limit = position;
+ print = true;
+ }
+ return print;
+ }
+
+ private:
+ const double start_position_;
+ Sensor front_;
+ Sensor calibration_;
+ Sensor back_;
+};
+
+int Main() {
+ control_loops::claw_queue.position.FetchNextBlocking();
+
+ const double top_start_position =
+ control_loops::claw_queue.position->top.position;
+ const double bottom_start_position =
+ control_loops::claw_queue.position->bottom.position;
+
+ ClawSensors top(top_start_position,
+ control_loops::claw_queue.position->top);
+ ClawSensors bottom(bottom_start_position,
+ control_loops::claw_queue.position->bottom);
+
+ Claws limits;
+
+ limits.claw_zeroing_off_speed = 0.5;
+ limits.claw_zeroing_speed = 0.1;
+ limits.claw_zeroing_separation = 0.1;
+
+ // claw separation that would be considered a collision
+ limits.claw_min_separation = 0.0;
+ limits.claw_max_separation = 0.0;
+
+ // We should never get closer/farther than these.
+ limits.soft_min_separation = 0.0;
+ limits.soft_max_separation = 0.0;
+
+ limits.upper_claw.lower_hard_limit = 0.0;
+ limits.upper_claw.upper_hard_limit = 0.0;
+ limits.upper_claw.lower_limit = 0.0;
+ limits.upper_claw.upper_limit = 0.0;
+ limits.upper_claw.front.lower_angle = 0.0;
+ limits.upper_claw.front.upper_angle = 0.0;
+ limits.upper_claw.front.lower_decreasing_angle = 0.0;
+ limits.upper_claw.front.upper_decreasing_angle = 0.0;
+ limits.upper_claw.calibration.lower_angle = 0.0;
+ limits.upper_claw.calibration.upper_angle = 0.0;
+ limits.upper_claw.calibration.lower_decreasing_angle = 0.0;
+ limits.upper_claw.calibration.upper_decreasing_angle = 0.0;
+ limits.upper_claw.back.lower_angle = 0.0;
+ limits.upper_claw.back.upper_angle = 0.0;
+ limits.upper_claw.back.lower_decreasing_angle = 0.0;
+ limits.upper_claw.back.upper_decreasing_angle = 0.0;
+
+ limits.lower_claw.lower_hard_limit = 0.0;
+ limits.lower_claw.upper_hard_limit = 0.0;
+ limits.lower_claw.lower_limit = 0.0;
+ limits.lower_claw.upper_limit = 0.0;
+ limits.lower_claw.front.lower_angle = 0.0;
+ limits.lower_claw.front.upper_angle = 0.0;
+ limits.lower_claw.front.lower_decreasing_angle = 0.0;
+ limits.lower_claw.front.upper_decreasing_angle = 0.0;
+ limits.lower_claw.calibration.lower_angle = 0.0;
+ limits.lower_claw.calibration.upper_angle = 0.0;
+ limits.lower_claw.calibration.lower_decreasing_angle = 0.0;
+ limits.lower_claw.calibration.upper_decreasing_angle = 0.0;
+ limits.lower_claw.back.lower_angle = 0.0;
+ limits.lower_claw.back.upper_angle = 0.0;
+ limits.lower_claw.back.lower_decreasing_angle = 0.0;
+ limits.lower_claw.back.upper_decreasing_angle = 0.0;
+
+ limits.claw_unimportant_epsilon = 0.01;
+ limits.start_fine_tune_pos = -0.2;
+ limits.max_zeroing_voltage = 4.0;
+
+ control_loops::ClawQueue::Position last_position =
+ *control_loops::claw_queue.position;
+
+ while (true) {
+ control_loops::claw_queue.position.FetchNextBlocking();
+ bool print = false;
+ if (top.GetPositionOfEdge(control_loops::claw_queue.position->top,
+ &limits.upper_claw)) {
+ print = true;
+ printf("Got an edge on the upper claw\n");
+ }
+ if (bottom.GetPositionOfEdge(
+ control_loops::claw_queue.position->bottom,
+ &limits.lower_claw)) {
+ print = true;
+ printf("Got an edge on the lower claw\n");
+ }
+ const double top_position =
+ control_loops::claw_queue.position->top.position -
+ top_start_position;
+ const double bottom_position =
+ control_loops::claw_queue.position->bottom.position -
+ bottom_start_position;
+ const double separation = top_position - bottom_position;
+ if (separation > limits.claw_max_separation) {
+ limits.soft_max_separation = limits.claw_max_separation = separation;
+ print = true;
+ }
+ if (separation < limits.claw_min_separation) {
+ limits.soft_min_separation = limits.claw_min_separation = separation;
+ print = true;
+ }
+
+ if (print) {
+ printf("{%f,\n", limits.claw_zeroing_off_speed);
+ printf("%f,\n", limits.claw_zeroing_speed);
+ printf("%f,\n", limits.claw_zeroing_separation);
+ printf("%f,\n", limits.claw_min_separation);
+ printf("%f,\n", limits.claw_max_separation);
+ printf("%f,\n", limits.soft_min_separation);
+ printf("%f,\n", limits.soft_max_separation);
+ printf(
+ "{%f, %f, %f, %f, {%f, %f, %f, %f}, {%f, %f, %f, %f}, {%f, %f, %f, "
+ "%f}},\n",
+ limits.upper_claw.lower_hard_limit,
+ limits.upper_claw.upper_hard_limit, limits.upper_claw.lower_limit,
+ limits.upper_claw.upper_limit, limits.upper_claw.front.lower_angle,
+ limits.upper_claw.front.upper_angle,
+ limits.upper_claw.front.lower_decreasing_angle,
+ limits.upper_claw.front.upper_decreasing_angle,
+ limits.upper_claw.calibration.lower_angle,
+ limits.upper_claw.calibration.upper_angle,
+ limits.upper_claw.calibration.lower_decreasing_angle,
+ limits.upper_claw.calibration.upper_decreasing_angle,
+ limits.upper_claw.back.lower_angle,
+ limits.upper_claw.back.upper_angle,
+ limits.upper_claw.back.lower_decreasing_angle,
+ limits.upper_claw.back.upper_decreasing_angle);
+
+ printf(
+ "{%f, %f, %f, %f, {%f, %f, %f, %f}, {%f, %f, %f, %f}, {%f, %f, %f, "
+ "%f}},\n",
+ limits.lower_claw.lower_hard_limit,
+ limits.lower_claw.upper_hard_limit, limits.lower_claw.lower_limit,
+ limits.lower_claw.upper_limit, limits.lower_claw.front.lower_angle,
+ limits.lower_claw.front.upper_angle,
+ limits.lower_claw.front.lower_decreasing_angle,
+ limits.lower_claw.front.upper_decreasing_angle,
+ limits.lower_claw.calibration.lower_angle,
+ limits.lower_claw.calibration.upper_angle,
+ limits.lower_claw.calibration.lower_decreasing_angle,
+ limits.lower_claw.calibration.upper_decreasing_angle,
+ limits.lower_claw.back.lower_angle,
+ limits.lower_claw.back.upper_angle,
+ limits.lower_claw.back.lower_decreasing_angle,
+ limits.lower_claw.back.upper_decreasing_angle);
+ printf("%f, // claw_unimportant_epsilon\n",
+ limits.claw_unimportant_epsilon);
+ printf("%f, // start_fine_tune_pos\n", limits.start_fine_tune_pos);
+ printf("%f,\n", limits.max_zeroing_voltage);
+ printf("}\n");
+ }
+
+ last_position = *control_loops::claw_queue.position;
+ }
+ return 0;
+}
+
+} // namespace frc971
+
+int main() {
+ ::aos::Init();
+ int returnvalue = ::frc971::Main();
+ ::aos::Cleanup();
+ return returnvalue;
+}
diff --git a/y2014/control_loops/claw/claw_lib_test.cc b/y2014/control_loops/claw/claw_lib_test.cc
new file mode 100644
index 0000000..9eb173e
--- /dev/null
+++ b/y2014/control_loops/claw/claw_lib_test.cc
@@ -0,0 +1,608 @@
+#include <unistd.h>
+
+#include <memory>
+
+#include "aos/common/network/team_number.h"
+#include "aos/common/controls/control_loop_test.h"
+
+#include "y2014/control_loops/claw/claw.q.h"
+#include "y2014/control_loops/claw/claw.h"
+#include "y2014/constants.h"
+#include "y2014/control_loops/claw/claw_motor_plant.h"
+
+#include "gtest/gtest.h"
+
+
+namespace frc971 {
+namespace control_loops {
+namespace testing {
+
+typedef enum {
+ TOP_CLAW = 0,
+ BOTTOM_CLAW,
+
+ CLAW_COUNT
+} ClawType;
+
+class TeamNumberEnvironment : public ::testing::Environment {
+ public:
+ // Override this to define how to set up the environment.
+ virtual void SetUp() { aos::network::OverrideTeamNumber(1); }
+};
+
+::testing::Environment* const team_number_env =
+ ::testing::AddGlobalTestEnvironment(new TeamNumberEnvironment);
+
+// Class which simulates the wrist and sends out queue messages containing the
+// position.
+class ClawMotorSimulation {
+ public:
+ // Constructs a motor simulation. initial_position is the inital angle of the
+ // wrist, which will be treated as 0 by the encoder.
+ ClawMotorSimulation(double initial_top_position,
+ double initial_bottom_position)
+ : claw_plant_(new StateFeedbackPlant<4, 2, 2>(MakeClawPlant())),
+ claw_queue(".frc971.control_loops.claw_queue", 0x9f1a99dd,
+ ".frc971.control_loops.claw_queue.goal",
+ ".frc971.control_loops.claw_queue.position",
+ ".frc971.control_loops.claw_queue.output",
+ ".frc971.control_loops.claw_queue.status") {
+ Reinitialize(initial_top_position, initial_bottom_position);
+ }
+
+ void Reinitialize(double initial_top_position,
+ double initial_bottom_position) {
+ LOG(INFO, "Reinitializing to {top: %f, bottom: %f}\n", initial_top_position,
+ initial_bottom_position);
+ claw_plant_->mutable_X(0, 0) = initial_bottom_position;
+ claw_plant_->mutable_X(1, 0) = initial_top_position - initial_bottom_position;
+ claw_plant_->mutable_X(2, 0) = 0.0;
+ claw_plant_->mutable_X(3, 0) = 0.0;
+ claw_plant_->mutable_Y() = claw_plant_->C() * claw_plant_->X();
+
+ ReinitializePartial(TOP_CLAW, initial_top_position);
+ ReinitializePartial(BOTTOM_CLAW, initial_bottom_position);
+ last_position_.Zero();
+ SetPhysicalSensors(&last_position_);
+ }
+
+ // Returns the absolute angle of the wrist.
+ double GetAbsolutePosition(ClawType type) const {
+ if (type == TOP_CLAW) {
+ return claw_plant_->Y(1, 0);
+ } else {
+ return claw_plant_->Y(0, 0);
+ }
+ }
+
+ // Returns the adjusted angle of the wrist.
+ double GetPosition(ClawType type) const {
+ return GetAbsolutePosition(type) - initial_position_[type];
+ }
+
+ // Makes sure pos is inside range (exclusive)
+ bool CheckRange(double pos, const constants::Values::Claws::AnglePair &pair) {
+ // Note: If the >= and <= signs are used, then the there exists a case
+ // where the wrist starts precisely on the edge and because initial
+ // position and the *edge_value_ are the same, then the comparison
+ // in ZeroedStateFeedbackLoop::DoGetPositionOfEdge will return that
+ // the lower, rather than upper, edge of the hall effect was passed.
+ return (pos > pair.lower_angle && pos < pair.upper_angle);
+ }
+
+ void SetHallEffect(double pos,
+ const constants::Values::Claws::AnglePair &pair,
+ HallEffectStruct *hall_effect) {
+ hall_effect->current = CheckRange(pos, pair);
+ }
+
+ void SetClawHallEffects(double pos,
+ const constants::Values::Claws::Claw &claw,
+ control_loops::HalfClawPosition *half_claw) {
+ SetHallEffect(pos, claw.front, &half_claw->front);
+ SetHallEffect(pos, claw.calibration, &half_claw->calibration);
+ SetHallEffect(pos, claw.back, &half_claw->back);
+ }
+
+ // Sets the values of the physical sensors that can be directly observed
+ // (encoder, hall effect).
+ void SetPhysicalSensors(control_loops::ClawQueue::Position *position) {
+ position->top.position = GetPosition(TOP_CLAW);
+ position->bottom.position = GetPosition(BOTTOM_CLAW);
+
+ double pos[2] = {GetAbsolutePosition(TOP_CLAW),
+ GetAbsolutePosition(BOTTOM_CLAW)};
+ LOG(DEBUG, "Physical claws are at {top: %f, bottom: %f}\n", pos[TOP_CLAW],
+ pos[BOTTOM_CLAW]);
+
+ const frc971::constants::Values& values = constants::GetValues();
+
+ // Signal that each hall effect sensor has been triggered if it is within
+ // the correct range.
+ SetClawHallEffects(pos[TOP_CLAW], values.claw.upper_claw, &position->top);
+ SetClawHallEffects(pos[BOTTOM_CLAW], values.claw.lower_claw,
+ &position->bottom);
+ }
+
+ void UpdateHallEffect(double angle,
+ double last_angle,
+ double initial_position,
+ HallEffectStruct *position,
+ const HallEffectStruct &last_position,
+ const constants::Values::Claws::AnglePair &pair,
+ const char *claw_name, const char *hall_effect_name) {
+ if (position->current && !last_position.current) {
+ ++position->posedge_count;
+
+ if (last_angle < pair.lower_angle) {
+ LOG(DEBUG, "%s: Positive lower edge on %s hall effect\n", claw_name,
+ hall_effect_name);
+ position->posedge_value = pair.lower_angle - initial_position;
+ } else {
+ LOG(DEBUG, "%s: Positive upper edge on %s hall effect\n", claw_name,
+ hall_effect_name);
+ position->posedge_value = pair.upper_angle - initial_position;
+ }
+ }
+ if (!position->current && last_position.current) {
+ ++position->negedge_count;
+
+ if (angle < pair.lower_angle) {
+ LOG(DEBUG, "%s: Negative lower edge on %s hall effect\n", claw_name,
+ hall_effect_name);
+ position->negedge_value = pair.lower_angle - initial_position;
+ } else {
+ LOG(DEBUG, "%s: Negative upper edge on %s hall effect\n", claw_name,
+ hall_effect_name);
+ position->negedge_value = pair.upper_angle - initial_position;
+ }
+ }
+ }
+
+ void UpdateClawHallEffects(
+ control_loops::HalfClawPosition *position,
+ const control_loops::HalfClawPosition &last_position,
+ const constants::Values::Claws::Claw &claw, double initial_position,
+ const char *claw_name) {
+ UpdateHallEffect(position->position + initial_position,
+ last_position.position + initial_position,
+ initial_position, &position->front, last_position.front,
+ claw.front, claw_name, "front");
+ UpdateHallEffect(position->position + initial_position,
+ last_position.position + initial_position,
+ initial_position, &position->calibration,
+ last_position.calibration, claw.calibration, claw_name,
+ "calibration");
+ UpdateHallEffect(position->position + initial_position,
+ last_position.position + initial_position,
+ initial_position, &position->back, last_position.back,
+ claw.back, claw_name, "back");
+ }
+
+ // Sends out the position queue messages.
+ void SendPositionMessage() {
+ ::aos::ScopedMessagePtr<control_loops::ClawQueue::Position> position =
+ claw_queue.position.MakeMessage();
+
+ // Initialize all the counters to their previous values.
+ *position = last_position_;
+
+ SetPhysicalSensors(position.get());
+
+ const frc971::constants::Values& values = constants::GetValues();
+
+ UpdateClawHallEffects(&position->top, last_position_.top,
+ values.claw.upper_claw, initial_position_[TOP_CLAW],
+ "Top");
+ UpdateClawHallEffects(&position->bottom, last_position_.bottom,
+ values.claw.lower_claw,
+ initial_position_[BOTTOM_CLAW], "Bottom");
+
+ // Only set calibration if it changed last cycle. Calibration starts out
+ // with a value of 0.
+ last_position_ = *position;
+ position.Send();
+ }
+
+ // Simulates the claw moving for one timestep.
+ void Simulate() {
+ const frc971::constants::Values& v = constants::GetValues();
+ EXPECT_TRUE(claw_queue.output.FetchLatest());
+
+ claw_plant_->mutable_U() << claw_queue.output->bottom_claw_voltage,
+ claw_queue.output->top_claw_voltage;
+ claw_plant_->Update();
+
+ // Check that the claw is within the limits.
+ EXPECT_GE(v.claw.upper_claw.upper_limit, claw_plant_->Y(0, 0));
+ EXPECT_LE(v.claw.upper_claw.lower_hard_limit, claw_plant_->Y(0, 0));
+
+ EXPECT_GE(v.claw.lower_claw.upper_hard_limit, claw_plant_->Y(1, 0));
+ EXPECT_LE(v.claw.lower_claw.lower_hard_limit, claw_plant_->Y(1, 0));
+
+ EXPECT_LE(claw_plant_->Y(1, 0) - claw_plant_->Y(0, 0),
+ v.claw.claw_max_separation);
+ EXPECT_GE(claw_plant_->Y(1, 0) - claw_plant_->Y(0, 0),
+ v.claw.claw_min_separation);
+ }
+ // The whole claw.
+ ::std::unique_ptr<StateFeedbackPlant<4, 2, 2>> claw_plant_;
+
+ private:
+ // Resets the plant so that it starts at initial_position.
+ void ReinitializePartial(ClawType type, double initial_position) {
+ initial_position_[type] = initial_position;
+ }
+
+ ClawQueue claw_queue;
+ double initial_position_[CLAW_COUNT];
+
+ control_loops::ClawQueue::Position last_position_;
+};
+
+
+class ClawTest : public ::aos::testing::ControlLoopTest {
+ protected:
+ // Create a new instance of the test queue so that it invalidates the queue
+ // that it points to. Otherwise, we will have a pointer to shared memory that
+ // is no longer valid.
+ ClawQueue claw_queue;
+
+ // Create a loop and simulation plant.
+ ClawMotor claw_motor_;
+ ClawMotorSimulation claw_motor_plant_;
+
+ // Minimum amount of acceptable separation between the top and bottom of the
+ // claw.
+ double min_separation_;
+
+ ClawTest()
+ : claw_queue(".frc971.control_loops.claw_queue", 0x9f1a99dd,
+ ".frc971.control_loops.claw_queue.goal",
+ ".frc971.control_loops.claw_queue.position",
+ ".frc971.control_loops.claw_queue.output",
+ ".frc971.control_loops.claw_queue.status"),
+ claw_motor_(&claw_queue),
+ claw_motor_plant_(0.4, 0.2),
+ min_separation_(constants::GetValues().claw.claw_min_separation) {}
+
+ void VerifyNearGoal() {
+ claw_queue.goal.FetchLatest();
+ claw_queue.position.FetchLatest();
+ double bottom = claw_motor_plant_.GetAbsolutePosition(BOTTOM_CLAW);
+ double separation =
+ claw_motor_plant_.GetAbsolutePosition(TOP_CLAW) - bottom;
+ EXPECT_NEAR(claw_queue.goal->bottom_angle, bottom, 1e-4);
+ EXPECT_NEAR(claw_queue.goal->separation_angle, separation, 1e-4);
+ EXPECT_LE(min_separation_, separation);
+ }
+};
+
+TEST_F(ClawTest, HandlesNAN) {
+ claw_queue.goal.MakeWithBuilder()
+ .bottom_angle(::std::nan(""))
+ .separation_angle(::std::nan(""))
+ .Send();
+ for (int i = 0; i < 500; ++i) {
+ claw_motor_plant_.SendPositionMessage();
+ claw_motor_.Iterate();
+ claw_motor_plant_.Simulate();
+ SimulateTimestep(true);
+ }
+}
+
+// Tests that the wrist zeros correctly and goes to a position.
+TEST_F(ClawTest, ZerosCorrectly) {
+ claw_queue.goal.MakeWithBuilder()
+ .bottom_angle(0.1)
+ .separation_angle(0.2)
+ .Send();
+ for (int i = 0; i < 500; ++i) {
+ claw_motor_plant_.SendPositionMessage();
+ claw_motor_.Iterate();
+ claw_motor_plant_.Simulate();
+ SimulateTimestep(true);
+ }
+ VerifyNearGoal();
+}
+
+// Tests that the wrist zeros correctly and goes to a position.
+TEST_F(ClawTest, LimitClawGoal) {
+ frc971::constants::Values values;
+ values.claw.claw_min_separation = -2.0;
+ values.claw.claw_max_separation = 1.0;
+ values.claw.soft_min_separation = -2.0;
+ values.claw.soft_max_separation = 1.0;
+ values.claw.upper_claw.lower_limit = -5.0;
+ values.claw.upper_claw.upper_limit = 7.5;
+ values.claw.lower_claw.lower_limit = -5.5;
+ values.claw.lower_claw.upper_limit = 8.0;
+
+ double bottom_goal = 0.0;
+ double top_goal = 0.0;
+
+ LimitClawGoal(&bottom_goal, &top_goal, values);
+ EXPECT_NEAR(0.0, bottom_goal, 1e-4);
+ EXPECT_NEAR(0.0, top_goal, 1e-4);
+
+ bottom_goal = 0.0;
+ top_goal = -4.0;
+
+ LimitClawGoal(&bottom_goal, &top_goal, values);
+ EXPECT_NEAR(-1.0, bottom_goal, 1e-4);
+ EXPECT_NEAR(-3.0, top_goal, 1e-4);
+
+ bottom_goal = 0.0;
+ top_goal = 4.0;
+
+ LimitClawGoal(&bottom_goal, &top_goal, values);
+ EXPECT_NEAR(1.5, bottom_goal, 1e-4);
+ EXPECT_NEAR(2.5, top_goal, 1e-4);
+
+ bottom_goal = -10.0;
+ top_goal = -9.5;
+
+ LimitClawGoal(&bottom_goal, &top_goal, values);
+ EXPECT_NEAR(-5.5, bottom_goal, 1e-4);
+ EXPECT_NEAR(-5.0, top_goal, 1e-4);
+
+ bottom_goal = -20.0;
+ top_goal = -4.0;
+
+ LimitClawGoal(&bottom_goal, &top_goal, values);
+ EXPECT_NEAR(-5.5, bottom_goal, 1e-4);
+ EXPECT_NEAR(-4.5, top_goal, 1e-4);
+
+ bottom_goal = -20.0;
+ top_goal = -4.0;
+
+ LimitClawGoal(&bottom_goal, &top_goal, values);
+ EXPECT_NEAR(-5.5, bottom_goal, 1e-4);
+ EXPECT_NEAR(-4.5, top_goal, 1e-4);
+
+ bottom_goal = -5.0;
+ top_goal = -10.0;
+
+ LimitClawGoal(&bottom_goal, &top_goal, values);
+ EXPECT_NEAR(-3.0, bottom_goal, 1e-4);
+ EXPECT_NEAR(-5.0, top_goal, 1e-4);
+
+ bottom_goal = 10.0;
+ top_goal = 9.0;
+
+ LimitClawGoal(&bottom_goal, &top_goal, values);
+ EXPECT_NEAR(8.0, bottom_goal, 1e-4);
+ EXPECT_NEAR(7.0, top_goal, 1e-4);
+
+ bottom_goal = 8.0;
+ top_goal = 9.0;
+
+ LimitClawGoal(&bottom_goal, &top_goal, values);
+ EXPECT_NEAR(6.5, bottom_goal, 1e-4);
+ EXPECT_NEAR(7.5, top_goal, 1e-4);
+}
+
+
+class ZeroingClawTest
+ : public ClawTest,
+ public ::testing::WithParamInterface< ::std::pair<double, double>> {};
+
+// Tests that the wrist zeros correctly starting on the hall effect sensor.
+TEST_P(ZeroingClawTest, ParameterizedZero) {
+ claw_motor_plant_.Reinitialize(GetParam().first, GetParam().second);
+
+ claw_queue.goal.MakeWithBuilder()
+ .bottom_angle(0.1)
+ .separation_angle(0.2)
+ .Send();
+ for (int i = 0; i < 700; ++i) {
+ claw_motor_plant_.SendPositionMessage();
+ claw_motor_.Iterate();
+ claw_motor_plant_.Simulate();
+
+ SimulateTimestep(true);
+ }
+ VerifyNearGoal();
+}
+
+INSTANTIATE_TEST_CASE_P(ZeroingClawTest, ZeroingClawTest,
+ ::testing::Values(::std::make_pair(0.04, 0.02),
+ ::std::make_pair(0.2, 0.1),
+ ::std::make_pair(0.3, 0.2),
+ ::std::make_pair(0.4, 0.3),
+ ::std::make_pair(0.5, 0.4),
+ ::std::make_pair(0.6, 0.5),
+ ::std::make_pair(0.7, 0.6),
+ ::std::make_pair(0.8, 0.7),
+ ::std::make_pair(0.9, 0.8),
+ ::std::make_pair(1.0, 0.9),
+ ::std::make_pair(1.1, 1.0),
+ ::std::make_pair(1.15, 1.05),
+ ::std::make_pair(1.05, 0.95),
+ ::std::make_pair(1.2, 1.1),
+ ::std::make_pair(1.3, 1.2),
+ ::std::make_pair(1.4, 1.3),
+ ::std::make_pair(1.5, 1.4),
+ ::std::make_pair(1.6, 1.5),
+ ::std::make_pair(1.7, 1.6),
+ ::std::make_pair(1.8, 1.7),
+ ::std::make_pair(2.015, 2.01)
+));
+
+/*
+// Tests that loosing the encoder for a second triggers a re-zero.
+TEST_F(ClawTest, RezeroWithMissingPos) {
+ claw_queue.goal.MakeWithBuilder()
+ .bottom_angle(0.1)
+ .separation_angle(0.2)
+ .Send();
+ for (int i = 0; i < 800; ++i) {
+ // After 3 seconds, simulate the encoder going missing.
+ // This should trigger a re-zero. To make sure it works, change the goal as
+ // well.
+ if (i < 300 || i > 400) {
+ claw_motor_plant_.SendPositionMessage();
+ } else {
+ if (i > 310) {
+ // Should be re-zeroing now.
+ EXPECT_TRUE(claw_motor_.is_uninitialized());
+ }
+ claw_queue.goal.MakeWithBuilder()
+ .bottom_angle(0.2)
+ .separation_angle(0.2)
+ .Send();
+ }
+ if (i == 410) {
+ EXPECT_TRUE(claw_motor_.is_zeroing());
+ // TODO(austin): Expose if the top and bototm is zeroing through
+ // functions.
+ // EXPECT_TRUE(bottom_claw_motor_.is_zeroing());
+ }
+
+ claw_motor_.Iterate();
+ claw_motor_plant_.Simulate();
+ SimulateTimestep(true);
+ }
+ VerifyNearGoal();
+}
+
+// Tests that disabling while zeroing sends the state machine into the
+// uninitialized state.
+TEST_F(ClawTest, DisableGoesUninitialized) {
+ claw_queue.goal.MakeWithBuilder()
+ .bottom_angle(0.1)
+ .separation_angle(0.2)
+ .Send();
+ for (int i = 0; i < 800; ++i) {
+ claw_motor_plant_.SendPositionMessage();
+ // After 0.5 seconds, disable the robot.
+ if (i > 50 && i < 200) {
+ SimulateTimestep(false);
+ if (i > 100) {
+ // Give the loop a couple cycled to get the message and then verify that
+ // it is in the correct state.
+ EXPECT_TRUE(claw_motor_.is_uninitialized());
+ // When disabled, we should be applying 0 power.
+ EXPECT_TRUE(claw_queue.output.FetchLatest());
+ EXPECT_EQ(0, claw_queue.output->top_claw_voltage);
+ EXPECT_EQ(0, claw_queue.output->bottom_claw_voltage);
+ }
+ } else {
+ SimulateTimestep(true);
+ }
+ if (i == 202) {
+ // Verify that we are zeroing after the bot gets enabled again.
+ EXPECT_TRUE(wrist_motor_.is_zeroing());
+ // TODO(austin): Expose if the top and bottom is zeroing through
+ // functions.
+ }
+
+ claw_motor_.Iterate();
+ claw_motor_plant_.Simulate();
+ }
+ VerifyNearGoal();
+}
+*/
+
+class WindupClawTest : public ClawTest {
+ protected:
+ void TestWindup(ClawMotor::CalibrationMode mode, int start_time, double offset) {
+ int capped_count = 0;
+ const frc971::constants::Values& values = constants::GetValues();
+ bool kicked = false;
+ bool measured = false;
+ for (int i = 0; i < 700; ++i) {
+ claw_motor_plant_.SendPositionMessage();
+ if (i >= start_time && mode == claw_motor_.mode() && !kicked) {
+ EXPECT_EQ(mode, claw_motor_.mode());
+ // Move the zeroing position far away and verify that it gets moved
+ // back.
+ claw_motor_.top_claw_goal_ += offset;
+ claw_motor_.bottom_claw_goal_ += offset;
+ kicked = true;
+ } else {
+ if (kicked && !measured) {
+ measured = true;
+ EXPECT_EQ(mode, claw_motor_.mode());
+
+ Eigen::Matrix<double, 4, 1> R;
+ R << claw_motor_.bottom_claw_goal_,
+ claw_motor_.top_claw_goal_ - claw_motor_.bottom_claw_goal_, 0.0,
+ 0.0;
+ Eigen::Matrix<double, 2, 1> uncapped_voltage =
+ claw_motor_.claw_.K() * (R - claw_motor_.claw_.X_hat());
+ // Use a factor of 1.8 because so long as it isn't actually running
+ // away, the CapU function will deal with getting the actual output
+ // down.
+ EXPECT_LT(::std::abs(uncapped_voltage(0, 0)),
+ values.claw.max_zeroing_voltage * 1.8);
+ EXPECT_LT(::std::abs(uncapped_voltage(1, 0)),
+ values.claw.max_zeroing_voltage * 1.8);
+ }
+ }
+ if (claw_motor_.mode() == mode) {
+ if (claw_motor_.capped_goal()) {
+ ++capped_count;
+ // The cycle after we kick the zero position is the only cycle during
+ // which we should expect to see a high uncapped power during zeroing.
+ ASSERT_LT(values.claw.max_zeroing_voltage,
+ ::std::abs(claw_motor_.uncapped_average_voltage()));
+ } else {
+ ASSERT_GT(values.claw.max_zeroing_voltage,
+ ::std::abs(claw_motor_.uncapped_average_voltage()));
+ }
+ }
+
+ claw_motor_.Iterate();
+ claw_motor_plant_.Simulate();
+ SimulateTimestep(true);
+ }
+ EXPECT_TRUE(kicked);
+ EXPECT_TRUE(measured);
+ EXPECT_LE(1, capped_count);
+ EXPECT_GE(3, capped_count);
+ }
+};
+
+// Tests that the wrist can't get too far away from the zeroing position if the
+// zeroing position is saturating the goal.
+TEST_F(WindupClawTest, NoWindupPositive) {
+ claw_queue.goal.MakeWithBuilder()
+ .bottom_angle(0.1)
+ .separation_angle(0.2)
+ .Send();
+
+ TestWindup(ClawMotor::UNKNOWN_LOCATION, 100, 971.0);
+
+ VerifyNearGoal();
+}
+
+// Tests that the wrist can't get too far away from the zeroing position if the
+// zeroing position is saturating the goal.
+TEST_F(WindupClawTest, NoWindupNegative) {
+ claw_queue.goal.MakeWithBuilder()
+ .bottom_angle(0.1)
+ .separation_angle(0.2)
+ .Send();
+
+ TestWindup(ClawMotor::UNKNOWN_LOCATION, 100, -971.0);
+
+ VerifyNearGoal();
+}
+
+// Tests that the wrist can't get too far away from the zeroing position if the
+// zeroing position is saturating the goal.
+TEST_F(WindupClawTest, NoWindupNegativeFineTune) {
+ claw_queue.goal.MakeWithBuilder()
+ .bottom_angle(0.1)
+ .separation_angle(0.2)
+ .Send();
+
+ TestWindup(ClawMotor::FINE_TUNE_BOTTOM, 200, -971.0);
+
+ VerifyNearGoal();
+}
+
+} // namespace testing
+} // namespace control_loops
+} // namespace frc971
diff --git a/y2014/control_loops/claw/claw_main.cc b/y2014/control_loops/claw/claw_main.cc
new file mode 100644
index 0000000..e573d6f
--- /dev/null
+++ b/y2014/control_loops/claw/claw_main.cc
@@ -0,0 +1,11 @@
+#include "y2014/control_loops/claw/claw.h"
+
+#include "aos/linux_code/init.h"
+
+int main() {
+ ::aos::Init();
+ frc971::control_loops::ClawMotor claw;
+ claw.Run();
+ ::aos::Cleanup();
+ return 0;
+}
diff --git a/y2014/control_loops/claw/claw_motor_plant.cc b/y2014/control_loops/claw/claw_motor_plant.cc
new file mode 100644
index 0000000..acba8d0
--- /dev/null
+++ b/y2014/control_loops/claw/claw_motor_plant.cc
@@ -0,0 +1,49 @@
+#include "y2014/control_loops/claw/claw_motor_plant.h"
+
+#include <vector>
+
+#include "frc971/control_loops/state_feedback_loop.h"
+
+namespace frc971 {
+namespace control_loops {
+
+StateFeedbackPlantCoefficients<4, 2, 2> MakeClawPlantCoefficients() {
+ Eigen::Matrix<double, 4, 4> A;
+ A << 1.0, 0.0, 0.00909331035298, 0.0, 0.0, 1.0, -6.04514323962e-05, 0.00903285892058, 0.0, 0.0, 0.824315642255, 0.0, 0.0, 0.0, -0.0112975266368, 0.813018115618;
+ Eigen::Matrix<double, 4, 2> B;
+ B << 0.000352527133889, 0.0, -0.000352527133889, 0.000376031064118, 0.0683072794628, 0.0, -0.0683072794628, 0.0726998350615;
+ Eigen::Matrix<double, 2, 4> C;
+ C << 1, 0, 0, 0, 1, 1, 0, 0;
+ Eigen::Matrix<double, 2, 2> D;
+ D << 0, 0, 0, 0;
+ Eigen::Matrix<double, 2, 1> U_max;
+ U_max << 12.0, 12.0;
+ Eigen::Matrix<double, 2, 1> U_min;
+ U_min << -12.0, -12.0;
+ return StateFeedbackPlantCoefficients<4, 2, 2>(A, B, C, D, U_max, U_min);
+}
+
+StateFeedbackController<4, 2, 2> MakeClawController() {
+ Eigen::Matrix<double, 4, 2> L;
+ L << 1.72431564225, 2.70472958703e-16, -1.72431564225, 1.71301811562, 65.9456997026, 1.03478906105e-14, -65.9456997026, 64.4642687194;
+ Eigen::Matrix<double, 2, 4> K;
+ K << 106.138028875, 0.0, 4.20012492658, 0.0, 99.5038407367, 99.7251230882, 3.77683310096, 3.78980738032;
+ Eigen::Matrix<double, 4, 4> A_inv;
+ A_inv << 1.0, 0.0, -0.0110313451387, 0.0, 0.0, 1.0, -7.89348747778e-05, -0.0111102800135, 0.0, 0.0, 1.21312753118, 0.0, 0.0, 0.0, 0.016857361889, 1.22998489307;
+ return StateFeedbackController<4, 2, 2>(L, K, A_inv, MakeClawPlantCoefficients());
+}
+
+StateFeedbackPlant<4, 2, 2> MakeClawPlant() {
+ ::std::vector< ::std::unique_ptr<StateFeedbackPlantCoefficients<4, 2, 2>>> plants(1);
+ plants[0] = ::std::unique_ptr<StateFeedbackPlantCoefficients<4, 2, 2>>(new StateFeedbackPlantCoefficients<4, 2, 2>(MakeClawPlantCoefficients()));
+ return StateFeedbackPlant<4, 2, 2>(&plants);
+}
+
+StateFeedbackLoop<4, 2, 2> MakeClawLoop() {
+ ::std::vector< ::std::unique_ptr<StateFeedbackController<4, 2, 2>>> controllers(1);
+ controllers[0] = ::std::unique_ptr<StateFeedbackController<4, 2, 2>>(new StateFeedbackController<4, 2, 2>(MakeClawController()));
+ return StateFeedbackLoop<4, 2, 2>(&controllers);
+}
+
+} // namespace control_loops
+} // namespace frc971
diff --git a/y2014/control_loops/claw/claw_motor_plant.h b/y2014/control_loops/claw/claw_motor_plant.h
new file mode 100644
index 0000000..c7de33a
--- /dev/null
+++ b/y2014/control_loops/claw/claw_motor_plant.h
@@ -0,0 +1,22 @@
+#ifndef Y2014_CONTROL_LOOPS_CLAW_CLAW_MOTOR_PLANT_H_
+#define Y2014_CONTROL_LOOPS_CLAW_CLAW_MOTOR_PLANT_H_
+
+#include "frc971/control_loops/state_feedback_loop.h"
+
+namespace frc971 {
+namespace control_loops {
+static constexpr double kClawMomentOfInertiaRatio = 0.933333;
+
+
+StateFeedbackPlantCoefficients<4, 2, 2> MakeClawPlantCoefficients();
+
+StateFeedbackController<4, 2, 2> MakeClawController();
+
+StateFeedbackPlant<4, 2, 2> MakeClawPlant();
+
+StateFeedbackLoop<4, 2, 2> MakeClawLoop();
+
+} // namespace control_loops
+} // namespace frc971
+
+#endif // Y2014_CONTROL_LOOPS_CLAW_CLAW_MOTOR_PLANT_H_
diff --git a/y2014/control_loops/claw/replay_claw.cc b/y2014/control_loops/claw/replay_claw.cc
new file mode 100644
index 0000000..70d881c
--- /dev/null
+++ b/y2014/control_loops/claw/replay_claw.cc
@@ -0,0 +1,24 @@
+#include "aos/common/controls/replay_control_loop.h"
+#include "aos/linux_code/init.h"
+
+#include "y2014/control_loops/claw/claw.q.h"
+
+// Reads one or more log files and sends out all the queue messages (in the
+// correct order and at the correct time) to feed a "live" claw process.
+
+int main(int argc, char **argv) {
+ if (argc <= 1) {
+ fprintf(stderr, "Need at least one file to replay!\n");
+ return EXIT_FAILURE;
+ }
+
+ ::aos::InitNRT();
+
+ ::aos::controls::ControlLoopReplayer<::frc971::control_loops::ClawQueue>
+ replayer(&::frc971::control_loops::claw_queue, "claw");
+ for (int i = 1; i < argc; ++i) {
+ replayer.ProcessFile(argv[i]);
+ }
+
+ ::aos::Cleanup();
+}
diff --git a/y2014/control_loops/drivetrain/drivetrain.cc b/y2014/control_loops/drivetrain/drivetrain.cc
new file mode 100644
index 0000000..ecb8ed2
--- /dev/null
+++ b/y2014/control_loops/drivetrain/drivetrain.cc
@@ -0,0 +1,775 @@
+#include "y2014/control_loops/drivetrain/drivetrain.h"
+
+#include <stdio.h>
+#include <sched.h>
+#include <cmath>
+#include <memory>
+#include "Eigen/Dense"
+
+#include "aos/common/logging/logging.h"
+#include "aos/common/controls/polytope.h"
+#include "aos/common/commonmath.h"
+#include "aos/common/logging/queue_logging.h"
+#include "aos/common/logging/matrix_logging.h"
+
+#include "y2014/constants.h"
+#include "frc971/control_loops/state_feedback_loop.h"
+#include "frc971/control_loops/coerce_goal.h"
+#include "y2014/control_loops/drivetrain/polydrivetrain_cim_plant.h"
+#include "y2014/control_loops/drivetrain/drivetrain.q.h"
+#include "frc971/queues/gyro.q.h"
+#include "frc971/shifter_hall_effect.h"
+
+// A consistent way to mark code that goes away without shifters. It's still
+// here because we will have shifters again in the future.
+#define HAVE_SHIFTERS 1
+
+using frc971::sensors::gyro_reading;
+
+namespace frc971 {
+namespace control_loops {
+
+class DrivetrainMotorsSS {
+ public:
+ class LimitedDrivetrainLoop : public StateFeedbackLoop<4, 2, 2> {
+ public:
+ LimitedDrivetrainLoop(StateFeedbackLoop<4, 2, 2> &&loop)
+ : StateFeedbackLoop<4, 2, 2>(::std::move(loop)),
+ U_Poly_((Eigen::Matrix<double, 4, 2>() << 1, 0,
+ -1, 0,
+ 0, 1,
+ 0, -1).finished(),
+ (Eigen::Matrix<double, 4, 1>() << 12.0, 12.0,
+ 12.0, 12.0).finished()) {
+ ::aos::controls::HPolytope<0>::Init();
+ T << 1, -1, 1, 1;
+ T_inverse = T.inverse();
+ }
+
+ bool output_was_capped() const {
+ return output_was_capped_;
+ }
+
+ private:
+ virtual void CapU() {
+ const Eigen::Matrix<double, 4, 1> error = R() - X_hat();
+
+ if (::std::abs(U(0, 0)) > 12.0 || ::std::abs(U(1, 0)) > 12.0) {
+ mutable_U() =
+ U() * 12.0 / ::std::max(::std::abs(U(0, 0)), ::std::abs(U(1, 0)));
+ LOG_MATRIX(DEBUG, "U is now", U());
+ // TODO(Austin): Figure out why the polytope stuff wasn't working and
+ // remove this hack.
+ output_was_capped_ = true;
+ return;
+
+ LOG_MATRIX(DEBUG, "U at start", U());
+ LOG_MATRIX(DEBUG, "R at start", R());
+ LOG_MATRIX(DEBUG, "Xhat at start", X_hat());
+
+ Eigen::Matrix<double, 2, 2> position_K;
+ position_K << K(0, 0), K(0, 2),
+ K(1, 0), K(1, 2);
+ Eigen::Matrix<double, 2, 2> velocity_K;
+ velocity_K << K(0, 1), K(0, 3),
+ K(1, 1), K(1, 3);
+
+ Eigen::Matrix<double, 2, 1> position_error;
+ position_error << error(0, 0), error(2, 0);
+ const auto drive_error = T_inverse * position_error;
+ Eigen::Matrix<double, 2, 1> velocity_error;
+ velocity_error << error(1, 0), error(3, 0);
+ LOG_MATRIX(DEBUG, "error", error);
+
+ const auto &poly = U_Poly_;
+ const Eigen::Matrix<double, 4, 2> pos_poly_H =
+ poly.H() * position_K * T;
+ const Eigen::Matrix<double, 4, 1> pos_poly_k =
+ poly.k() - poly.H() * velocity_K * velocity_error;
+ const ::aos::controls::HPolytope<2> pos_poly(pos_poly_H, pos_poly_k);
+
+ Eigen::Matrix<double, 2, 1> adjusted_pos_error;
+ {
+ const auto &P = drive_error;
+
+ Eigen::Matrix<double, 1, 2> L45;
+ L45 << ::aos::sign(P(1, 0)), -::aos::sign(P(0, 0));
+ const double w45 = 0;
+
+ Eigen::Matrix<double, 1, 2> LH;
+ if (::std::abs(P(0, 0)) > ::std::abs(P(1, 0))) {
+ LH << 0, 1;
+ } else {
+ LH << 1, 0;
+ }
+ const double wh = LH.dot(P);
+
+ Eigen::Matrix<double, 2, 2> standard;
+ standard << L45, LH;
+ Eigen::Matrix<double, 2, 1> W;
+ W << w45, wh;
+ const Eigen::Matrix<double, 2, 1> intersection =
+ standard.inverse() * W;
+
+ bool is_inside_h;
+ const auto adjusted_pos_error_h =
+ DoCoerceGoal(pos_poly, LH, wh, drive_error, &is_inside_h);
+ const auto adjusted_pos_error_45 =
+ DoCoerceGoal(pos_poly, L45, w45, intersection, nullptr);
+ if (pos_poly.IsInside(intersection)) {
+ adjusted_pos_error = adjusted_pos_error_h;
+ } else {
+ if (is_inside_h) {
+ if (adjusted_pos_error_h.norm() > adjusted_pos_error_45.norm()) {
+ adjusted_pos_error = adjusted_pos_error_h;
+ } else {
+ adjusted_pos_error = adjusted_pos_error_45;
+ }
+ } else {
+ adjusted_pos_error = adjusted_pos_error_45;
+ }
+ }
+ }
+
+ LOG_MATRIX(DEBUG, "adjusted_pos_error", adjusted_pos_error);
+ mutable_U() =
+ velocity_K * velocity_error + position_K * T * adjusted_pos_error;
+ LOG_MATRIX(DEBUG, "U is now", U());
+ } else {
+ output_was_capped_ = false;
+ }
+ }
+
+ const ::aos::controls::HPolytope<2> U_Poly_;
+ Eigen::Matrix<double, 2, 2> T, T_inverse;
+ bool output_was_capped_ = false;;
+ };
+
+ DrivetrainMotorsSS()
+ : loop_(new LimitedDrivetrainLoop(
+ constants::GetValues().make_drivetrain_loop())),
+ filtered_offset_(0.0),
+ gyro_(0.0),
+ left_goal_(0.0),
+ right_goal_(0.0),
+ raw_left_(0.0),
+ raw_right_(0.0) {
+ // Low gear on both.
+ loop_->set_controller_index(0);
+ }
+
+ void SetGoal(double left, double left_velocity, double right,
+ double right_velocity) {
+ left_goal_ = left;
+ right_goal_ = right;
+ loop_->mutable_R() << left, left_velocity, right, right_velocity;
+ }
+ void SetRawPosition(double left, double right) {
+ raw_right_ = right;
+ raw_left_ = left;
+ Eigen::Matrix<double, 2, 1> Y;
+ Y << left + filtered_offset_, right - filtered_offset_;
+ loop_->Correct(Y);
+ }
+ void SetPosition(double left, double right, double gyro) {
+ // Decay the offset quickly because this gyro is great.
+ const double offset =
+ (right - left - gyro * constants::GetValues().turn_width) / 2.0;
+ filtered_offset_ = 0.25 * offset + 0.75 * filtered_offset_;
+ gyro_ = gyro;
+ SetRawPosition(left, right);
+ }
+
+ void SetExternalMotors(double left_voltage, double right_voltage) {
+ loop_->mutable_U() << left_voltage, right_voltage;
+ }
+
+ void Update(bool stop_motors, bool enable_control_loop) {
+ if (enable_control_loop) {
+ loop_->Update(stop_motors);
+ } else {
+ if (stop_motors) {
+ loop_->mutable_U().setZero();
+ loop_->mutable_U_uncapped().setZero();
+ }
+ loop_->UpdateObserver();
+ }
+ ::Eigen::Matrix<double, 4, 1> E = loop_->R() - loop_->X_hat();
+ LOG_MATRIX(DEBUG, "E", E);
+ }
+
+ double GetEstimatedRobotSpeed() const {
+ // lets just call the average of left and right velocities close enough
+ return (loop_->X_hat(1, 0) + loop_->X_hat(3, 0)) / 2;
+ }
+
+ double GetEstimatedLeftEncoder() const {
+ return loop_->X_hat(0, 0);
+ }
+
+ double GetEstimatedRightEncoder() const {
+ return loop_->X_hat(2, 0);
+ }
+
+ bool OutputWasCapped() const {
+ return loop_->output_was_capped();
+ }
+
+ void SendMotors(DrivetrainQueue::Output *output) const {
+ if (output) {
+ output->left_voltage = loop_->U(0, 0);
+ output->right_voltage = loop_->U(1, 0);
+ output->left_high = false;
+ output->right_high = false;
+ }
+ }
+
+ const LimitedDrivetrainLoop &loop() const { return *loop_; }
+
+ private:
+ ::std::unique_ptr<LimitedDrivetrainLoop> loop_;
+
+ double filtered_offset_;
+ double gyro_;
+ double left_goal_;
+ double right_goal_;
+ double raw_left_;
+ double raw_right_;
+};
+
+class PolyDrivetrain {
+ public:
+
+ enum Gear {
+ HIGH,
+ LOW,
+ SHIFTING_UP,
+ SHIFTING_DOWN
+ };
+ // Stall Torque in N m
+ static constexpr double kStallTorque = 2.42;
+ // Stall Current in Amps
+ static constexpr double kStallCurrent = 133.0;
+ // Free Speed in RPM. Used number from last year.
+ static constexpr double kFreeSpeed = 4650.0;
+ // Free Current in Amps
+ static constexpr double kFreeCurrent = 2.7;
+ // Moment of inertia of the drivetrain in kg m^2
+ // Just borrowed from last year.
+ static constexpr double J = 6.4;
+ // Mass of the robot, in kg.
+ static constexpr double m = 68.0;
+ // Radius of the robot, in meters (from last year).
+ static constexpr double rb = 0.617998644 / 2.0;
+ static constexpr double kWheelRadius = 0.04445;
+ // Resistance of the motor, divided by the number of motors.
+ static constexpr double kR = (12.0 / kStallCurrent / 4 + 0.03) / (0.93 * 0.93);
+ // Motor velocity constant
+ static constexpr double Kv =
+ ((kFreeSpeed / 60.0 * 2.0 * M_PI) / (12.0 - kR * kFreeCurrent));
+ // Torque constant
+ static constexpr double Kt = kStallTorque / kStallCurrent;
+
+ PolyDrivetrain()
+ : U_Poly_((Eigen::Matrix<double, 4, 2>() << /*[[*/ 1, 0 /*]*/,
+ /*[*/ -1, 0 /*]*/,
+ /*[*/ 0, 1 /*]*/,
+ /*[*/ 0, -1 /*]]*/).finished(),
+ (Eigen::Matrix<double, 4, 1>() << /*[[*/ 12 /*]*/,
+ /*[*/ 12 /*]*/,
+ /*[*/ 12 /*]*/,
+ /*[*/ 12 /*]]*/).finished()),
+ loop_(new StateFeedbackLoop<2, 2, 2>(
+ constants::GetValues().make_v_drivetrain_loop())),
+ ttrust_(1.1),
+ wheel_(0.0),
+ throttle_(0.0),
+ quickturn_(false),
+ stale_count_(0),
+ position_time_delta_(0.01),
+ left_gear_(LOW),
+ right_gear_(LOW),
+ counter_(0) {
+
+ last_position_.Zero();
+ position_.Zero();
+ }
+ static bool IsInGear(Gear gear) { return gear == LOW || gear == HIGH; }
+
+ static double MotorSpeed(const constants::ShifterHallEffect &hall_effect,
+ double shifter_position, double velocity) {
+ // TODO(austin): G_high, G_low and kWheelRadius
+ const double avg_hall_effect =
+ (hall_effect.clear_high + hall_effect.clear_low) / 2.0;
+
+ if (shifter_position > avg_hall_effect) {
+ return velocity / constants::GetValues().high_gear_ratio / kWheelRadius;
+ } else {
+ return velocity / constants::GetValues().low_gear_ratio / kWheelRadius;
+ }
+ }
+
+ Gear ComputeGear(const constants::ShifterHallEffect &hall_effect,
+ double velocity, Gear current) {
+ const double low_omega = MotorSpeed(hall_effect, 0.0, ::std::abs(velocity));
+ const double high_omega =
+ MotorSpeed(hall_effect, 1.0, ::std::abs(velocity));
+
+ double high_torque = ((12.0 - high_omega / Kv) * Kt / kR);
+ double low_torque = ((12.0 - low_omega / Kv) * Kt / kR);
+ double high_power = high_torque * high_omega;
+ double low_power = low_torque * low_omega;
+
+ // TODO(aschuh): Do this right!
+ if ((current == HIGH || high_power > low_power + 160) &&
+ ::std::abs(velocity) > 0.14) {
+ return HIGH;
+ } else {
+ return LOW;
+ }
+ }
+
+ void SetGoal(double wheel, double throttle, bool quickturn, bool highgear) {
+ const double kWheelNonLinearity = 0.3;
+ // Apply a sin function that's scaled to make it feel better.
+ const double angular_range = M_PI_2 * kWheelNonLinearity;
+
+ wheel_ = sin(angular_range * wheel) / sin(angular_range);
+ wheel_ = sin(angular_range * wheel_) / sin(angular_range);
+ quickturn_ = quickturn;
+
+ static const double kThrottleDeadband = 0.05;
+ if (::std::abs(throttle) < kThrottleDeadband) {
+ throttle_ = 0;
+ } else {
+ throttle_ = copysign((::std::abs(throttle) - kThrottleDeadband) /
+ (1.0 - kThrottleDeadband), throttle);
+ }
+
+ // TODO(austin): Fix the upshift logic to include states.
+ Gear requested_gear;
+ if (false) {
+ const auto &values = constants::GetValues();
+ const double current_left_velocity =
+ (position_.left_encoder - last_position_.left_encoder) /
+ position_time_delta_;
+ const double current_right_velocity =
+ (position_.right_encoder - last_position_.right_encoder) /
+ position_time_delta_;
+
+ Gear left_requested =
+ ComputeGear(values.left_drive, current_left_velocity, left_gear_);
+ Gear right_requested =
+ ComputeGear(values.right_drive, current_right_velocity, right_gear_);
+ requested_gear =
+ (left_requested == HIGH || right_requested == HIGH) ? HIGH : LOW;
+ } else {
+ requested_gear = highgear ? HIGH : LOW;
+ }
+
+ const Gear shift_up =
+ constants::GetValues().clutch_transmission ? HIGH : SHIFTING_UP;
+ const Gear shift_down =
+ constants::GetValues().clutch_transmission ? LOW : SHIFTING_DOWN;
+
+ if (left_gear_ != requested_gear) {
+ if (IsInGear(left_gear_)) {
+ if (requested_gear == HIGH) {
+ left_gear_ = shift_up;
+ } else {
+ left_gear_ = shift_down;
+ }
+ } else {
+ if (requested_gear == HIGH && left_gear_ == SHIFTING_DOWN) {
+ left_gear_ = SHIFTING_UP;
+ } else if (requested_gear == LOW && left_gear_ == SHIFTING_UP) {
+ left_gear_ = SHIFTING_DOWN;
+ }
+ }
+ }
+ if (right_gear_ != requested_gear) {
+ if (IsInGear(right_gear_)) {
+ if (requested_gear == HIGH) {
+ right_gear_ = shift_up;
+ } else {
+ right_gear_ = shift_down;
+ }
+ } else {
+ if (requested_gear == HIGH && right_gear_ == SHIFTING_DOWN) {
+ right_gear_ = SHIFTING_UP;
+ } else if (requested_gear == LOW && right_gear_ == SHIFTING_UP) {
+ right_gear_ = SHIFTING_DOWN;
+ }
+ }
+ }
+ }
+ void SetPosition(const DrivetrainQueue::Position *position) {
+ const auto &values = constants::GetValues();
+ if (position == NULL) {
+ ++stale_count_;
+ } else {
+ last_position_ = position_;
+ position_ = *position;
+ position_time_delta_ = (stale_count_ + 1) * 0.01;
+ stale_count_ = 0;
+ }
+
+#if HAVE_SHIFTERS
+ if (position) {
+ GearLogging gear_logging;
+ // Switch to the correct controller.
+ const double left_middle_shifter_position =
+ (values.left_drive.clear_high + values.left_drive.clear_low) / 2.0;
+ const double right_middle_shifter_position =
+ (values.right_drive.clear_high + values.right_drive.clear_low) / 2.0;
+
+ if (position->left_shifter_position < left_middle_shifter_position ||
+ left_gear_ == LOW) {
+ if (position->right_shifter_position < right_middle_shifter_position ||
+ right_gear_ == LOW) {
+ gear_logging.left_loop_high = false;
+ gear_logging.right_loop_high = false;
+ loop_->set_controller_index(gear_logging.controller_index = 0);
+ } else {
+ gear_logging.left_loop_high = false;
+ gear_logging.right_loop_high = true;
+ loop_->set_controller_index(gear_logging.controller_index = 1);
+ }
+ } else {
+ if (position->right_shifter_position < right_middle_shifter_position ||
+ right_gear_ == LOW) {
+ gear_logging.left_loop_high = true;
+ gear_logging.right_loop_high = false;
+ loop_->set_controller_index(gear_logging.controller_index = 2);
+ } else {
+ gear_logging.left_loop_high = true;
+ gear_logging.right_loop_high = true;
+ loop_->set_controller_index(gear_logging.controller_index = 3);
+ }
+ }
+
+ // TODO(austin): Constants.
+ if (position->left_shifter_position > values.left_drive.clear_high && left_gear_ == SHIFTING_UP) {
+ left_gear_ = HIGH;
+ }
+ if (position->left_shifter_position < values.left_drive.clear_low && left_gear_ == SHIFTING_DOWN) {
+ left_gear_ = LOW;
+ }
+ if (position->right_shifter_position > values.right_drive.clear_high && right_gear_ == SHIFTING_UP) {
+ right_gear_ = HIGH;
+ }
+ if (position->right_shifter_position < values.right_drive.clear_low && right_gear_ == SHIFTING_DOWN) {
+ right_gear_ = LOW;
+ }
+
+ gear_logging.left_state = left_gear_;
+ gear_logging.right_state = right_gear_;
+ LOG_STRUCT(DEBUG, "state", gear_logging);
+ }
+#else
+ (void) values;
+#endif
+ }
+
+ double FilterVelocity(double throttle) {
+ const Eigen::Matrix<double, 2, 2> FF =
+ loop_->B().inverse() *
+ (Eigen::Matrix<double, 2, 2>::Identity() - loop_->A());
+
+ constexpr int kHighGearController = 3;
+ const Eigen::Matrix<double, 2, 2> FF_high =
+ loop_->controller(kHighGearController).plant.B().inverse() *
+ (Eigen::Matrix<double, 2, 2>::Identity() -
+ loop_->controller(kHighGearController).plant.A());
+
+ ::Eigen::Matrix<double, 1, 2> FF_sum = FF.colwise().sum();
+ int min_FF_sum_index;
+ const double min_FF_sum = FF_sum.minCoeff(&min_FF_sum_index);
+ const double min_K_sum = loop_->K().col(min_FF_sum_index).sum();
+ const double high_min_FF_sum = FF_high.col(0).sum();
+
+ const double adjusted_ff_voltage = ::aos::Clip(
+ throttle * 12.0 * min_FF_sum / high_min_FF_sum, -12.0, 12.0);
+ return (adjusted_ff_voltage +
+ ttrust_ * min_K_sum * (loop_->X_hat(0, 0) + loop_->X_hat(1, 0)) /
+ 2.0) /
+ (ttrust_ * min_K_sum + min_FF_sum);
+ }
+
+ double MaxVelocity() {
+ const Eigen::Matrix<double, 2, 2> FF =
+ loop_->B().inverse() *
+ (Eigen::Matrix<double, 2, 2>::Identity() - loop_->A());
+
+ constexpr int kHighGearController = 3;
+ const Eigen::Matrix<double, 2, 2> FF_high =
+ loop_->controller(kHighGearController).plant.B().inverse() *
+ (Eigen::Matrix<double, 2, 2>::Identity() -
+ loop_->controller(kHighGearController).plant.A());
+
+ ::Eigen::Matrix<double, 1, 2> FF_sum = FF.colwise().sum();
+ int min_FF_sum_index;
+ const double min_FF_sum = FF_sum.minCoeff(&min_FF_sum_index);
+ //const double min_K_sum = loop_->K().col(min_FF_sum_index).sum();
+ const double high_min_FF_sum = FF_high.col(0).sum();
+
+ const double adjusted_ff_voltage = ::aos::Clip(
+ 12.0 * min_FF_sum / high_min_FF_sum, -12.0, 12.0);
+ return adjusted_ff_voltage / min_FF_sum;
+ }
+
+ void Update() {
+ const auto &values = constants::GetValues();
+ // TODO(austin): Observer for the current velocity instead of difference
+ // calculations.
+ ++counter_;
+#if HAVE_SHIFTERS
+ const double current_left_velocity =
+ (position_.left_encoder - last_position_.left_encoder) /
+ position_time_delta_;
+ const double current_right_velocity =
+ (position_.right_encoder - last_position_.right_encoder) /
+ position_time_delta_;
+ const double left_motor_speed =
+ MotorSpeed(values.left_drive, position_.left_shifter_position,
+ current_left_velocity);
+ const double right_motor_speed =
+ MotorSpeed(values.right_drive, position_.right_shifter_position,
+ current_right_velocity);
+
+ {
+ CIMLogging logging;
+
+ // Reset the CIM model to the current conditions to be ready for when we
+ // shift.
+ if (IsInGear(left_gear_)) {
+ logging.left_in_gear = true;
+ } else {
+ logging.left_in_gear = false;
+ }
+ logging.left_motor_speed = left_motor_speed;
+ logging.left_velocity = current_left_velocity;
+ if (IsInGear(right_gear_)) {
+ logging.right_in_gear = true;
+ } else {
+ logging.right_in_gear = false;
+ }
+ logging.right_motor_speed = right_motor_speed;
+ logging.right_velocity = current_right_velocity;
+
+ LOG_STRUCT(DEBUG, "currently", logging);
+ }
+#else
+ (void) values;
+#endif
+
+#if HAVE_SHIFTERS
+ if (IsInGear(left_gear_) && IsInGear(right_gear_)) {
+#else
+ {
+#endif
+ // FF * X = U (steady state)
+ const Eigen::Matrix<double, 2, 2> FF =
+ loop_->B().inverse() *
+ (Eigen::Matrix<double, 2, 2>::Identity() - loop_->A());
+
+ // Invert the plant to figure out how the velocity filter would have to
+ // work
+ // out in order to filter out the forwards negative inertia.
+ // This math assumes that the left and right power and velocity are
+ // equals,
+ // and that the plant is the same on the left and right.
+ const double fvel = FilterVelocity(throttle_);
+
+ const double sign_svel = wheel_ * ((fvel > 0.0) ? 1.0 : -1.0);
+ double steering_velocity;
+ if (quickturn_) {
+ steering_velocity = wheel_ * MaxVelocity();
+ } else {
+ steering_velocity = ::std::abs(fvel) * wheel_;
+ }
+ const double left_velocity = fvel - steering_velocity;
+ const double right_velocity = fvel + steering_velocity;
+
+ // Integrate velocity to get the position.
+ // This position is used to get integral control.
+ loop_->mutable_R() << left_velocity, right_velocity;
+
+ if (!quickturn_) {
+ // K * R = w
+ Eigen::Matrix<double, 1, 2> equality_k;
+ equality_k << 1 + sign_svel, -(1 - sign_svel);
+ const double equality_w = 0.0;
+
+ // Construct a constraint on R by manipulating the constraint on U
+ ::aos::controls::HPolytope<2> R_poly = ::aos::controls::HPolytope<2>(
+ U_Poly_.H() * (loop_->K() + FF),
+ U_Poly_.k() + U_Poly_.H() * loop_->K() * loop_->X_hat());
+
+ // Limit R back inside the box.
+ loop_->mutable_R() =
+ CoerceGoal(R_poly, equality_k, equality_w, loop_->R());
+ }
+
+ const Eigen::Matrix<double, 2, 1> FF_volts = FF * loop_->R();
+ const Eigen::Matrix<double, 2, 1> U_ideal =
+ loop_->K() * (loop_->R() - loop_->X_hat()) + FF_volts;
+
+ for (int i = 0; i < 2; i++) {
+ loop_->mutable_U()[i] = ::aos::Clip(U_ideal[i], -12, 12);
+ }
+
+ // TODO(austin): Model this better.
+ // TODO(austin): Feed back?
+ loop_->mutable_X_hat() =
+ loop_->A() * loop_->X_hat() + loop_->B() * loop_->U();
+#if HAVE_SHIFTERS
+ } else {
+ // Any motor is not in gear. Speed match.
+ ::Eigen::Matrix<double, 1, 1> R_left;
+ ::Eigen::Matrix<double, 1, 1> R_right;
+ R_left(0, 0) = left_motor_speed;
+ R_right(0, 0) = right_motor_speed;
+
+ const double wiggle =
+ (static_cast<double>((counter_ % 20) / 10) - 0.5) * 5.0;
+
+ loop_->mutable_U(0, 0) = ::aos::Clip(
+ (R_left / Kv)(0, 0) + (IsInGear(left_gear_) ? 0 : wiggle),
+ -12.0, 12.0);
+ loop_->mutable_U(1, 0) = ::aos::Clip(
+ (R_right / Kv)(0, 0) + (IsInGear(right_gear_) ? 0 : wiggle),
+ -12.0, 12.0);
+ loop_->mutable_U() *= 12.0 / ::aos::robot_state->voltage_battery;
+#endif
+ }
+ }
+
+ void SendMotors(DrivetrainQueue::Output *output) {
+ if (output != NULL) {
+ output->left_voltage = loop_->U(0, 0);
+ output->right_voltage = loop_->U(1, 0);
+ output->left_high = left_gear_ == HIGH || left_gear_ == SHIFTING_UP;
+ output->right_high = right_gear_ == HIGH || right_gear_ == SHIFTING_UP;
+ }
+ }
+
+ private:
+ const ::aos::controls::HPolytope<2> U_Poly_;
+
+ ::std::unique_ptr<StateFeedbackLoop<2, 2, 2>> loop_;
+
+ const double ttrust_;
+ double wheel_;
+ double throttle_;
+ bool quickturn_;
+ int stale_count_;
+ double position_time_delta_;
+ Gear left_gear_;
+ Gear right_gear_;
+ DrivetrainQueue::Position last_position_;
+ DrivetrainQueue::Position position_;
+ int counter_;
+};
+constexpr double PolyDrivetrain::kStallTorque;
+constexpr double PolyDrivetrain::kStallCurrent;
+constexpr double PolyDrivetrain::kFreeSpeed;
+constexpr double PolyDrivetrain::kFreeCurrent;
+constexpr double PolyDrivetrain::J;
+constexpr double PolyDrivetrain::m;
+constexpr double PolyDrivetrain::rb;
+constexpr double PolyDrivetrain::kWheelRadius;
+constexpr double PolyDrivetrain::kR;
+constexpr double PolyDrivetrain::Kv;
+constexpr double PolyDrivetrain::Kt;
+
+
+void DrivetrainLoop::RunIteration(const DrivetrainQueue::Goal *goal,
+ const DrivetrainQueue::Position *position,
+ DrivetrainQueue::Output *output,
+ DrivetrainQueue::Status * status) {
+ // TODO(aschuh): These should be members of the class.
+ static DrivetrainMotorsSS dt_closedloop;
+ static PolyDrivetrain dt_openloop;
+
+ bool bad_pos = false;
+ if (position == nullptr) {
+ LOG_INTERVAL(no_position_);
+ bad_pos = true;
+ }
+ no_position_.Print();
+
+ bool control_loop_driving = false;
+ if (goal) {
+ double wheel = goal->steering;
+ double throttle = goal->throttle;
+ bool quickturn = goal->quickturn;
+#if HAVE_SHIFTERS
+ bool highgear = goal->highgear;
+#endif
+
+ control_loop_driving = goal->control_loop_driving;
+ double left_goal = goal->left_goal;
+ double right_goal = goal->right_goal;
+
+ dt_closedloop.SetGoal(left_goal, goal->left_velocity_goal, right_goal,
+ goal->right_velocity_goal);
+#if HAVE_SHIFTERS
+ dt_openloop.SetGoal(wheel, throttle, quickturn, highgear);
+#else
+ dt_openloop.SetGoal(wheel, throttle, quickturn, false);
+#endif
+ }
+
+ if (!bad_pos) {
+ const double left_encoder = position->left_encoder;
+ const double right_encoder = position->right_encoder;
+ if (gyro_reading.FetchLatest()) {
+ LOG_STRUCT(DEBUG, "using", *gyro_reading.get());
+ dt_closedloop.SetPosition(left_encoder, right_encoder,
+ gyro_reading->angle);
+ } else {
+ dt_closedloop.SetRawPosition(left_encoder, right_encoder);
+ }
+ }
+ dt_openloop.SetPosition(position);
+ dt_openloop.Update();
+
+ if (control_loop_driving) {
+ dt_closedloop.Update(output == NULL, true);
+ dt_closedloop.SendMotors(output);
+ } else {
+ dt_openloop.SendMotors(output);
+ if (output) {
+ dt_closedloop.SetExternalMotors(output->left_voltage,
+ output->right_voltage);
+ }
+ dt_closedloop.Update(output == NULL, false);
+ }
+
+ // set the output status of the control loop state
+ if (status) {
+ bool done = false;
+ if (goal) {
+ done = ((::std::abs(goal->left_goal -
+ dt_closedloop.GetEstimatedLeftEncoder()) <
+ constants::GetValues().drivetrain_done_distance) &&
+ (::std::abs(goal->right_goal -
+ dt_closedloop.GetEstimatedRightEncoder()) <
+ constants::GetValues().drivetrain_done_distance));
+ }
+ status->is_done = done;
+ status->robot_speed = dt_closedloop.GetEstimatedRobotSpeed();
+ status->filtered_left_position = dt_closedloop.GetEstimatedLeftEncoder();
+ status->filtered_right_position = dt_closedloop.GetEstimatedRightEncoder();
+
+ status->filtered_left_velocity = dt_closedloop.loop().X_hat(1, 0);
+ status->filtered_right_velocity = dt_closedloop.loop().X_hat(3, 0);
+ status->output_was_capped = dt_closedloop.OutputWasCapped();
+ status->uncapped_left_voltage = dt_closedloop.loop().U_uncapped(0, 0);
+ status->uncapped_right_voltage = dt_closedloop.loop().U_uncapped(1, 0);
+ }
+}
+
+} // namespace control_loops
+} // namespace frc971
diff --git a/y2014/control_loops/drivetrain/drivetrain.gyp b/y2014/control_loops/drivetrain/drivetrain.gyp
new file mode 100644
index 0000000..ffe4337
--- /dev/null
+++ b/y2014/control_loops/drivetrain/drivetrain.gyp
@@ -0,0 +1,103 @@
+{
+ 'targets': [
+ {
+ 'target_name': 'replay_drivetrain',
+ 'type': 'executable',
+ 'variables': {
+ 'no_rsync': 1,
+ },
+ 'sources': [
+ 'replay_drivetrain.cc',
+ ],
+ 'dependencies': [
+ 'drivetrain_queue',
+ '<(AOS)/common/controls/controls.gyp:replay_control_loop',
+ '<(AOS)/linux_code/linux_code.gyp:init',
+ ],
+ },
+ {
+ 'target_name': 'drivetrain_queue',
+ 'type': 'static_library',
+ 'sources': ['drivetrain.q'],
+ 'variables': {
+ 'header_path': 'y2014/control_loops/drivetrain',
+ },
+ 'dependencies': [
+ '<(AOS)/common/controls/controls.gyp:control_loop_queues',
+ ],
+ 'export_dependent_settings': [
+ '<(AOS)/common/controls/controls.gyp:control_loop_queues',
+ ],
+ 'includes': ['../../../aos/build/queues.gypi'],
+ },
+ {
+ 'target_name': 'polydrivetrain_plants',
+ 'type': 'static_library',
+ 'sources': [
+ 'polydrivetrain_dog_motor_plant.cc',
+ 'drivetrain_dog_motor_plant.cc',
+ ],
+ 'dependencies': [
+ '<(DEPTH)/frc971/control_loops/control_loops.gyp:state_feedback_loop',
+ ],
+ 'export_dependent_settings': [
+ '<(DEPTH)/frc971/control_loops/control_loops.gyp:state_feedback_loop',
+ ],
+ },
+ {
+ 'target_name': 'drivetrain_lib',
+ 'type': 'static_library',
+ 'sources': [
+ 'drivetrain.cc',
+ 'polydrivetrain_cim_plant.cc',
+ ],
+ 'dependencies': [
+ 'drivetrain_queue',
+ '<(AOS)/common/controls/controls.gyp:control_loop',
+ '<(DEPTH)/y2014/y2014.gyp:constants',
+ '<(AOS)/common/controls/controls.gyp:polytope',
+ '<(DEPTH)/frc971/control_loops/control_loops.gyp:state_feedback_loop',
+ '<(DEPTH)/frc971/control_loops/control_loops.gyp:coerce_goal',
+ '<(DEPTH)/frc971/queues/queues.gyp:gyro',
+ '<(AOS)/common/util/util.gyp:log_interval',
+ '<(AOS)/common/logging/logging.gyp:queue_logging',
+ '<(AOS)/common/logging/logging.gyp:matrix_logging',
+ ],
+ 'export_dependent_settings': [
+ '<(AOS)/common/controls/controls.gyp:polytope',
+ '<(DEPTH)/frc971/control_loops/control_loops.gyp:state_feedback_loop',
+ '<(DEPTH)/frc971/control_loops/control_loops.gyp:coerce_goal',
+ '<(AOS)/common/controls/controls.gyp:control_loop',
+ 'drivetrain_queue',
+ ],
+ },
+ {
+ 'target_name': 'drivetrain_lib_test',
+ 'type': 'executable',
+ 'sources': [
+ 'drivetrain_lib_test.cc',
+ ],
+ 'dependencies': [
+ '<(EXTERNALS):gtest',
+ 'drivetrain_queue',
+ 'drivetrain_lib',
+ '<(AOS)/common/controls/controls.gyp:control_loop_test',
+ '<(DEPTH)/frc971/control_loops/control_loops.gyp:state_feedback_loop',
+ '<(DEPTH)/frc971/queues/queues.gyp:gyro',
+ '<(AOS)/common/common.gyp:queues',
+ ],
+ },
+ {
+ 'target_name': 'drivetrain',
+ 'type': 'executable',
+ 'sources': [
+ 'drivetrain_main.cc',
+ ],
+ 'dependencies': [
+ '<(AOS)/linux_code/linux_code.gyp:init',
+ 'drivetrain_lib',
+ 'drivetrain_queue',
+ ],
+ },
+ ],
+}
diff --git a/y2014/control_loops/drivetrain/drivetrain.h b/y2014/control_loops/drivetrain/drivetrain.h
new file mode 100644
index 0000000..87e6362
--- /dev/null
+++ b/y2014/control_loops/drivetrain/drivetrain.h
@@ -0,0 +1,43 @@
+#ifndef Y2014_CONTROL_LOOPS_DRIVETRAIN_H_
+#define Y2014_CONTROL_LOOPS_DRIVETRAIN_H_
+
+#include "Eigen/Dense"
+
+#include "aos/common/controls/polytope.h"
+#include "aos/common/controls/control_loop.h"
+#include "aos/common/controls/polytope.h"
+#include "y2014/control_loops/drivetrain/drivetrain.q.h"
+#include "aos/common/util/log_interval.h"
+
+namespace frc971 {
+namespace control_loops {
+
+class DrivetrainLoop
+ : public aos::controls::ControlLoop<control_loops::DrivetrainQueue> {
+ public:
+ // Constructs a control loop which can take a Drivetrain or defaults to the
+ // drivetrain at frc971::control_loops::drivetrain
+ explicit DrivetrainLoop(control_loops::DrivetrainQueue *my_drivetrain =
+ &control_loops::drivetrain_queue)
+ : aos::controls::ControlLoop<control_loops::DrivetrainQueue>(
+ my_drivetrain) {
+ ::aos::controls::HPolytope<0>::Init();
+ }
+
+ protected:
+ // Executes one cycle of the control loop.
+ virtual void RunIteration(
+ const control_loops::DrivetrainQueue::Goal *goal,
+ const control_loops::DrivetrainQueue::Position *position,
+ control_loops::DrivetrainQueue::Output *output,
+ control_loops::DrivetrainQueue::Status *status);
+
+ typedef ::aos::util::SimpleLogInterval SimpleLogInterval;
+ SimpleLogInterval no_position_ = SimpleLogInterval(
+ ::aos::time::Time::InSeconds(0.25), WARNING, "no position");
+};
+
+} // namespace control_loops
+} // namespace frc971
+
+#endif // Y2014_CONTROL_LOOPS_DRIVETRAIN_H_
diff --git a/y2014/control_loops/drivetrain/drivetrain.q b/y2014/control_loops/drivetrain/drivetrain.q
new file mode 100644
index 0000000..872efec
--- /dev/null
+++ b/y2014/control_loops/drivetrain/drivetrain.q
@@ -0,0 +1,71 @@
+package frc971.control_loops;
+
+import "aos/common/controls/control_loops.q";
+
+struct GearLogging {
+ int8_t controller_index;
+ bool left_loop_high;
+ bool right_loop_high;
+ int8_t left_state;
+ int8_t right_state;
+};
+
+struct CIMLogging {
+ bool left_in_gear;
+ bool right_in_gear;
+ double left_motor_speed;
+ double right_motor_speed;
+ double left_velocity;
+ double right_velocity;
+};
+
+queue_group DrivetrainQueue {
+ implements aos.control_loops.ControlLoop;
+
+ message Goal {
+ double steering;
+ double throttle;
+ bool highgear;
+ bool quickturn;
+ bool control_loop_driving;
+ double left_goal;
+ double left_velocity_goal;
+ double right_goal;
+ double right_velocity_goal;
+ };
+
+ message Position {
+ double left_encoder;
+ double right_encoder;
+ double left_shifter_position;
+ double right_shifter_position;
+ };
+
+ message Output {
+ double left_voltage;
+ double right_voltage;
+ bool left_high;
+ bool right_high;
+ };
+
+ message Status {
+ double robot_speed;
+ double filtered_left_position;
+ double filtered_right_position;
+ double filtered_left_velocity;
+ double filtered_right_velocity;
+
+ double uncapped_left_voltage;
+ double uncapped_right_voltage;
+ bool output_was_capped;
+
+ bool is_done;
+ };
+
+ queue Goal goal;
+ queue Position position;
+ queue Output output;
+ queue Status status;
+};
+
+queue_group DrivetrainQueue drivetrain_queue;
diff --git a/y2014/control_loops/drivetrain/drivetrain_dog_motor_plant.cc b/y2014/control_loops/drivetrain/drivetrain_dog_motor_plant.cc
new file mode 100644
index 0000000..2bb5c94
--- /dev/null
+++ b/y2014/control_loops/drivetrain/drivetrain_dog_motor_plant.cc
@@ -0,0 +1,133 @@
+#include "y2014/control_loops/drivetrain/drivetrain_dog_motor_plant.h"
+
+#include <vector>
+
+#include "frc971/control_loops/state_feedback_loop.h"
+
+namespace frc971 {
+namespace control_loops {
+
+StateFeedbackPlantCoefficients<4, 2, 2> MakeDrivetrainLowLowPlantCoefficients() {
+ Eigen::Matrix<double, 4, 4> A;
+ A << 1.0, 0.00860955515291, 0.0, 0.000228184998733, 0.0, 0.735841675858, 0.0, 0.0410810558113, 0.0, 0.000228184998733, 1.0, 0.00860955515291, 0.0, 0.0410810558113, 0.0, 0.735841675858;
+ Eigen::Matrix<double, 4, 2> B;
+ B << 0.000272244648044, -4.46778919705e-05, 0.0517213538779, -0.00804353916233, -4.46778919705e-05, 0.000272244648044, -0.00804353916233, 0.0517213538779;
+ Eigen::Matrix<double, 2, 4> C;
+ C << 1, 0, 0, 0, 0, 0, 1, 0;
+ Eigen::Matrix<double, 2, 2> D;
+ D << 0, 0, 0, 0;
+ Eigen::Matrix<double, 2, 1> U_max;
+ U_max << 12.0, 12.0;
+ Eigen::Matrix<double, 2, 1> U_min;
+ U_min << -12.0, -12.0;
+ return StateFeedbackPlantCoefficients<4, 2, 2>(A, B, C, D, U_max, U_min);
+}
+
+StateFeedbackPlantCoefficients<4, 2, 2> MakeDrivetrainLowHighPlantCoefficients() {
+ Eigen::Matrix<double, 4, 4> A;
+ A << 1.0, 0.00860667098456, 0.0, 7.04111872002e-05, 0.0, 0.735048848179, 0.0, 0.0131811893199, 0.0, 0.000245343870066, 1.0, 0.00957169266049, 0.0, 0.045929121897, 0.0, 0.915703853642;
+ Eigen::Matrix<double, 4, 2> B;
+ B << 0.000272809358971, -2.57343985847e-05, 0.0518765869984, -0.00481755802263, -4.80375440247e-05, 0.00015654091672, -0.00899277497558, 0.0308091755839;
+ Eigen::Matrix<double, 2, 4> C;
+ C << 1, 0, 0, 0, 0, 0, 1, 0;
+ Eigen::Matrix<double, 2, 2> D;
+ D << 0, 0, 0, 0;
+ Eigen::Matrix<double, 2, 1> U_max;
+ U_max << 12.0, 12.0;
+ Eigen::Matrix<double, 2, 1> U_min;
+ U_min << -12.0, -12.0;
+ return StateFeedbackPlantCoefficients<4, 2, 2>(A, B, C, D, U_max, U_min);
+}
+
+StateFeedbackPlantCoefficients<4, 2, 2> MakeDrivetrainHighLowPlantCoefficients() {
+ Eigen::Matrix<double, 4, 4> A;
+ A << 1.0, 0.00957169266049, 0.0, 0.000245343870066, 0.0, 0.915703853642, 0.0, 0.045929121897, 0.0, 7.04111872002e-05, 1.0, 0.00860667098456, 0.0, 0.0131811893199, 0.0, 0.735048848179;
+ Eigen::Matrix<double, 4, 2> B;
+ B << 0.00015654091672, -4.80375440247e-05, 0.0308091755839, -0.00899277497558, -2.57343985847e-05, 0.000272809358971, -0.00481755802263, 0.0518765869984;
+ Eigen::Matrix<double, 2, 4> C;
+ C << 1, 0, 0, 0, 0, 0, 1, 0;
+ Eigen::Matrix<double, 2, 2> D;
+ D << 0, 0, 0, 0;
+ Eigen::Matrix<double, 2, 1> U_max;
+ U_max << 12.0, 12.0;
+ Eigen::Matrix<double, 2, 1> U_min;
+ U_min << -12.0, -12.0;
+ return StateFeedbackPlantCoefficients<4, 2, 2>(A, B, C, D, U_max, U_min);
+}
+
+StateFeedbackPlantCoefficients<4, 2, 2> MakeDrivetrainHighHighPlantCoefficients() {
+ Eigen::Matrix<double, 4, 4> A;
+ A << 1.0, 0.00957076892085, 0.0, 7.56192087769e-05, 0.0, 0.915439806567, 0.0, 0.0146814193986, 0.0, 7.56192087769e-05, 1.0, 0.00957076892085, 0.0, 0.0146814193986, 0.0, 0.915439806567;
+ Eigen::Matrix<double, 4, 2> B;
+ B << 0.000156878531877, -2.76378646165e-05, 0.0309056814511, -0.00536587314624, -2.76378646165e-05, 0.000156878531877, -0.00536587314624, 0.0309056814511;
+ Eigen::Matrix<double, 2, 4> C;
+ C << 1, 0, 0, 0, 0, 0, 1, 0;
+ Eigen::Matrix<double, 2, 2> D;
+ D << 0, 0, 0, 0;
+ Eigen::Matrix<double, 2, 1> U_max;
+ U_max << 12.0, 12.0;
+ Eigen::Matrix<double, 2, 1> U_min;
+ U_min << -12.0, -12.0;
+ return StateFeedbackPlantCoefficients<4, 2, 2>(A, B, C, D, U_max, U_min);
+}
+
+StateFeedbackController<4, 2, 2> MakeDrivetrainLowLowController() {
+ Eigen::Matrix<double, 4, 2> L;
+ L << 1.03584167586, 0.0410810558113, 17.1117704011, 3.22861251708, 0.0410810558113, 1.03584167586, 3.22861251708, 17.1117704011;
+ Eigen::Matrix<double, 2, 4> K;
+ K << 128.210620632, 6.93828382074, 5.11036686771, 0.729493080206, 5.1103668677, 0.729493080206, 128.210620632, 6.93828382074;
+ Eigen::Matrix<double, 4, 4> A_inv;
+ A_inv << 1.0, -0.0117194973377, 0.0, 0.000344183176608, 0.0, 1.36323698074, 0.0, -0.0761076958907, 0.0, 0.000344183176608, 1.0, -0.0117194973377, 0.0, -0.0761076958907, 0.0, 1.36323698074;
+ return StateFeedbackController<4, 2, 2>(L, K, A_inv, MakeDrivetrainLowLowPlantCoefficients());
+}
+
+StateFeedbackController<4, 2, 2> MakeDrivetrainLowHighController() {
+ Eigen::Matrix<double, 4, 2> L;
+ L << 1.02891982345, 0.0143715516939, 16.6997472571, 1.23741823594, 0.0143715516939, 1.22183287838, 2.40440177527, 33.5403677132;
+ Eigen::Matrix<double, 2, 4> K;
+ K << 127.841025245, 6.90618982868, -2.11442482189, 0.171361719101, 11.257083857, 1.47190974842, 138.457761234, 11.0770574926;
+ Eigen::Matrix<double, 4, 4> A_inv;
+ A_inv << 1.0, -0.011714710309, 0.0, 9.17355833725e-05, 0.0, 1.36167854796, 0.0, -0.0196008159867, 0.0, 0.00031964754384, 1.0, -0.0104574267731, 0.0, -0.0682979543713, 0.0, 1.09303924439;
+ return StateFeedbackController<4, 2, 2>(L, K, A_inv, MakeDrivetrainLowHighPlantCoefficients());
+}
+
+StateFeedbackController<4, 2, 2> MakeDrivetrainHighLowController() {
+ Eigen::Matrix<double, 4, 2> L;
+ L << 1.21584032636, 0.045928553155, 33.3376290177, 4.12652814156, 0.045928553155, 1.03491237546, 2.45838080322, 16.967272239;
+ Eigen::Matrix<double, 2, 4> K;
+ K << 138.457761234, 11.0770574926, 11.257083857, 1.47190974842, -2.1144248219, 0.171361719101, 127.841025245, 6.90618982868;
+ Eigen::Matrix<double, 4, 4> A_inv;
+ A_inv << 1.0, -0.0104574267731, 0.0, 0.00031964754384, 0.0, 1.09303924439, 0.0, -0.0682979543713, 0.0, 9.17355833725e-05, 1.0, -0.011714710309, 0.0, -0.0196008159867, 0.0, 1.36167854796;
+ return StateFeedbackController<4, 2, 2>(L, K, A_inv, MakeDrivetrainHighLowPlantCoefficients());
+}
+
+StateFeedbackController<4, 2, 2> MakeDrivetrainHighHighController() {
+ Eigen::Matrix<double, 4, 2> L;
+ L << 1.21543980657, 0.0146814193986, 33.1557840927, 1.47278696694, 0.0146814193986, 1.21543980657, 1.47278696694, 33.1557840927;
+ Eigen::Matrix<double, 2, 4> K;
+ K << 138.52410152, 11.0779399816, 3.96842371774, 0.882728086516, 3.96842371774, 0.882728086517, 138.52410152, 11.0779399816;
+ Eigen::Matrix<double, 4, 4> A_inv;
+ A_inv << 1.0, -0.010456196092, 0.0, 8.50876166887e-05, 0.0, 1.0926521463, 0.0, -0.0175234726538, 0.0, 8.50876166887e-05, 1.0, -0.010456196092, 0.0, -0.0175234726538, 0.0, 1.0926521463;
+ return StateFeedbackController<4, 2, 2>(L, K, A_inv, MakeDrivetrainHighHighPlantCoefficients());
+}
+
+StateFeedbackPlant<4, 2, 2> MakeDrivetrainPlant() {
+ ::std::vector< ::std::unique_ptr<StateFeedbackPlantCoefficients<4, 2, 2>>> plants(4);
+ plants[0] = ::std::unique_ptr<StateFeedbackPlantCoefficients<4, 2, 2>>(new StateFeedbackPlantCoefficients<4, 2, 2>(MakeDrivetrainLowLowPlantCoefficients()));
+ plants[1] = ::std::unique_ptr<StateFeedbackPlantCoefficients<4, 2, 2>>(new StateFeedbackPlantCoefficients<4, 2, 2>(MakeDrivetrainLowHighPlantCoefficients()));
+ plants[2] = ::std::unique_ptr<StateFeedbackPlantCoefficients<4, 2, 2>>(new StateFeedbackPlantCoefficients<4, 2, 2>(MakeDrivetrainHighLowPlantCoefficients()));
+ plants[3] = ::std::unique_ptr<StateFeedbackPlantCoefficients<4, 2, 2>>(new StateFeedbackPlantCoefficients<4, 2, 2>(MakeDrivetrainHighHighPlantCoefficients()));
+ return StateFeedbackPlant<4, 2, 2>(&plants);
+}
+
+StateFeedbackLoop<4, 2, 2> MakeDrivetrainLoop() {
+ ::std::vector< ::std::unique_ptr<StateFeedbackController<4, 2, 2>>> controllers(4);
+ controllers[0] = ::std::unique_ptr<StateFeedbackController<4, 2, 2>>(new StateFeedbackController<4, 2, 2>(MakeDrivetrainLowLowController()));
+ controllers[1] = ::std::unique_ptr<StateFeedbackController<4, 2, 2>>(new StateFeedbackController<4, 2, 2>(MakeDrivetrainLowHighController()));
+ controllers[2] = ::std::unique_ptr<StateFeedbackController<4, 2, 2>>(new StateFeedbackController<4, 2, 2>(MakeDrivetrainHighLowController()));
+ controllers[3] = ::std::unique_ptr<StateFeedbackController<4, 2, 2>>(new StateFeedbackController<4, 2, 2>(MakeDrivetrainHighHighController()));
+ return StateFeedbackLoop<4, 2, 2>(&controllers);
+}
+
+} // namespace control_loops
+} // namespace frc971
diff --git a/y2014/control_loops/drivetrain/drivetrain_dog_motor_plant.h b/y2014/control_loops/drivetrain/drivetrain_dog_motor_plant.h
new file mode 100644
index 0000000..64498dd
--- /dev/null
+++ b/y2014/control_loops/drivetrain/drivetrain_dog_motor_plant.h
@@ -0,0 +1,32 @@
+#ifndef Y2014_CONTROL_LOOPS_DRIVETRAIN_DRIVETRAIN_DOG_MOTOR_PLANT_H_
+#define Y2014_CONTROL_LOOPS_DRIVETRAIN_DRIVETRAIN_DOG_MOTOR_PLANT_H_
+
+#include "frc971/control_loops/state_feedback_loop.h"
+
+namespace frc971 {
+namespace control_loops {
+
+StateFeedbackPlantCoefficients<4, 2, 2> MakeDrivetrainLowLowPlantCoefficients();
+
+StateFeedbackController<4, 2, 2> MakeDrivetrainLowLowController();
+
+StateFeedbackPlantCoefficients<4, 2, 2> MakeDrivetrainLowHighPlantCoefficients();
+
+StateFeedbackController<4, 2, 2> MakeDrivetrainLowHighController();
+
+StateFeedbackPlantCoefficients<4, 2, 2> MakeDrivetrainHighLowPlantCoefficients();
+
+StateFeedbackController<4, 2, 2> MakeDrivetrainHighLowController();
+
+StateFeedbackPlantCoefficients<4, 2, 2> MakeDrivetrainHighHighPlantCoefficients();
+
+StateFeedbackController<4, 2, 2> MakeDrivetrainHighHighController();
+
+StateFeedbackPlant<4, 2, 2> MakeDrivetrainPlant();
+
+StateFeedbackLoop<4, 2, 2> MakeDrivetrainLoop();
+
+} // namespace control_loops
+} // namespace frc971
+
+#endif // Y2014_CONTROL_LOOPS_DRIVETRAIN_DRIVETRAIN_DOG_MOTOR_PLANT_H_
diff --git a/y2014/control_loops/drivetrain/drivetrain_lib_test.cc b/y2014/control_loops/drivetrain/drivetrain_lib_test.cc
new file mode 100644
index 0000000..e9291d8
--- /dev/null
+++ b/y2014/control_loops/drivetrain/drivetrain_lib_test.cc
@@ -0,0 +1,297 @@
+#include <unistd.h>
+
+#include <memory>
+
+#include "gtest/gtest.h"
+#include "aos/common/network/team_number.h"
+#include "aos/common/queue_testutils.h"
+#include "aos/common/controls/polytope.h"
+#include "aos/common/controls/control_loop_test.h"
+
+#include "y2014/control_loops/drivetrain/drivetrain.q.h"
+#include "y2014/control_loops/drivetrain/drivetrain.h"
+#include "frc971/control_loops/state_feedback_loop.h"
+#include "frc971/control_loops/coerce_goal.h"
+#include "y2014/control_loops/drivetrain/drivetrain_dog_motor_plant.h"
+#include "frc971/queues/gyro.q.h"
+
+
+namespace frc971 {
+namespace control_loops {
+namespace testing {
+
+class Environment : public ::testing::Environment {
+ public:
+ virtual ~Environment() {}
+ // how to set up the environment.
+ virtual void SetUp() {
+ aos::controls::HPolytope<0>::Init();
+ }
+};
+::testing::Environment* const holder_env =
+ ::testing::AddGlobalTestEnvironment(new Environment);
+
+class TeamNumberEnvironment : public ::testing::Environment {
+ public:
+ // Override this to define how to set up the environment.
+ virtual void SetUp() { aos::network::OverrideTeamNumber(971); }
+};
+
+::testing::Environment* const team_number_env =
+ ::testing::AddGlobalTestEnvironment(new TeamNumberEnvironment);
+
+// Class which simulates the drivetrain and sends out queue messages containing
+// the position.
+class DrivetrainSimulation {
+ public:
+ // Constructs a motor simulation.
+ // TODO(aschuh) Do we want to test the clutch one too?
+ DrivetrainSimulation()
+ : drivetrain_plant_(
+ new StateFeedbackPlant<4, 2, 2>(MakeDrivetrainPlant())),
+ my_drivetrain_queue_(".frc971.control_loops.drivetrain",
+ 0x8a8dde77, ".frc971.control_loops.drivetrain.goal",
+ ".frc971.control_loops.drivetrain.position",
+ ".frc971.control_loops.drivetrain.output",
+ ".frc971.control_loops.drivetrain.status") {
+ Reinitialize();
+ }
+
+ // Resets the plant.
+ void Reinitialize() {
+ drivetrain_plant_->mutable_X(0, 0) = 0.0;
+ drivetrain_plant_->mutable_X(1, 0) = 0.0;
+ drivetrain_plant_->mutable_Y() =
+ drivetrain_plant_->C() * drivetrain_plant_->X();
+ last_left_position_ = drivetrain_plant_->Y(0, 0);
+ last_right_position_ = drivetrain_plant_->Y(1, 0);
+ }
+
+ // Returns the position of the drivetrain.
+ double GetLeftPosition() const { return drivetrain_plant_->Y(0, 0); }
+ double GetRightPosition() const { return drivetrain_plant_->Y(1, 0); }
+
+ // Sends out the position queue messages.
+ void SendPositionMessage() {
+ const double left_encoder = GetLeftPosition();
+ const double right_encoder = GetRightPosition();
+
+ ::aos::ScopedMessagePtr<control_loops::DrivetrainQueue::Position> position =
+ my_drivetrain_queue_.position.MakeMessage();
+ position->left_encoder = left_encoder;
+ position->right_encoder = right_encoder;
+ position.Send();
+ }
+
+ // Simulates the drivetrain moving for one timestep.
+ void Simulate() {
+ last_left_position_ = drivetrain_plant_->Y(0, 0);
+ last_right_position_ = drivetrain_plant_->Y(1, 0);
+ EXPECT_TRUE(my_drivetrain_queue_.output.FetchLatest());
+ drivetrain_plant_->mutable_U() << my_drivetrain_queue_.output->left_voltage,
+ my_drivetrain_queue_.output->right_voltage;
+ drivetrain_plant_->Update();
+ }
+
+ ::std::unique_ptr<StateFeedbackPlant<4, 2, 2>> drivetrain_plant_;
+ private:
+ DrivetrainQueue my_drivetrain_queue_;
+ double last_left_position_;
+ double last_right_position_;
+};
+
+class DrivetrainTest : public ::aos::testing::ControlLoopTest {
+ protected:
+ // Create a new instance of the test queue so that it invalidates the queue
+ // that it points to. Otherwise, we will have a pointer to shared memory that
+ // is no longer valid.
+ DrivetrainQueue my_drivetrain_queue_;
+
+ // Create a loop and simulation plant.
+ DrivetrainLoop drivetrain_motor_;
+ DrivetrainSimulation drivetrain_motor_plant_;
+
+ DrivetrainTest() : my_drivetrain_queue_(".frc971.control_loops.drivetrain",
+ 0x8a8dde77,
+ ".frc971.control_loops.drivetrain.goal",
+ ".frc971.control_loops.drivetrain.position",
+ ".frc971.control_loops.drivetrain.output",
+ ".frc971.control_loops.drivetrain.status"),
+ drivetrain_motor_(&my_drivetrain_queue_),
+ drivetrain_motor_plant_() {
+ ::frc971::sensors::gyro_reading.Clear();
+ }
+
+ void VerifyNearGoal() {
+ my_drivetrain_queue_.goal.FetchLatest();
+ my_drivetrain_queue_.position.FetchLatest();
+ EXPECT_NEAR(my_drivetrain_queue_.goal->left_goal,
+ drivetrain_motor_plant_.GetLeftPosition(),
+ 1e-2);
+ EXPECT_NEAR(my_drivetrain_queue_.goal->right_goal,
+ drivetrain_motor_plant_.GetRightPosition(),
+ 1e-2);
+ }
+
+ virtual ~DrivetrainTest() {
+ ::frc971::sensors::gyro_reading.Clear();
+ }
+};
+
+// Tests that the drivetrain converges on a goal.
+TEST_F(DrivetrainTest, ConvergesCorrectly) {
+ my_drivetrain_queue_.goal.MakeWithBuilder().control_loop_driving(true)
+ .left_goal(-1.0)
+ .right_goal(1.0).Send();
+ for (int i = 0; i < 200; ++i) {
+ drivetrain_motor_plant_.SendPositionMessage();
+ drivetrain_motor_.Iterate();
+ drivetrain_motor_plant_.Simulate();
+ SimulateTimestep(true);
+ }
+ VerifyNearGoal();
+}
+
+// Tests that it survives disabling.
+TEST_F(DrivetrainTest, SurvivesDisabling) {
+ my_drivetrain_queue_.goal.MakeWithBuilder().control_loop_driving(true)
+ .left_goal(-1.0)
+ .right_goal(1.0).Send();
+ for (int i = 0; i < 500; ++i) {
+ drivetrain_motor_plant_.SendPositionMessage();
+ drivetrain_motor_.Iterate();
+ drivetrain_motor_plant_.Simulate();
+ if (i > 20 && i < 200) {
+ SimulateTimestep(false);
+ } else {
+ SimulateTimestep(true);
+ }
+ }
+ VerifyNearGoal();
+}
+
+// Tests that never having a goal doesn't break.
+TEST_F(DrivetrainTest, NoGoalStart) {
+ for (int i = 0; i < 20; ++i) {
+ drivetrain_motor_plant_.SendPositionMessage();
+ drivetrain_motor_.Iterate();
+ drivetrain_motor_plant_.Simulate();
+ }
+}
+
+// Tests that never having a goal, but having driver's station messages, doesn't
+// break.
+TEST_F(DrivetrainTest, NoGoalWithRobotState) {
+ for (int i = 0; i < 20; ++i) {
+ drivetrain_motor_plant_.SendPositionMessage();
+ drivetrain_motor_.Iterate();
+ drivetrain_motor_plant_.Simulate();
+ SimulateTimestep(true);
+ }
+}
+
+::aos::controls::HPolytope<2> MakeBox(double x1_min, double x1_max,
+ double x2_min, double x2_max) {
+ Eigen::Matrix<double, 4, 2> box_H;
+ box_H << /*[[*/ 1.0, 0.0 /*]*/,
+ /*[*/-1.0, 0.0 /*]*/,
+ /*[*/ 0.0, 1.0 /*]*/,
+ /*[*/ 0.0,-1.0 /*]]*/;
+ Eigen::Matrix<double, 4, 1> box_k;
+ box_k << /*[[*/ x1_max /*]*/,
+ /*[*/-x1_min /*]*/,
+ /*[*/ x2_max /*]*/,
+ /*[*/-x2_min /*]]*/;
+ ::aos::controls::HPolytope<2> t_poly(box_H, box_k);
+ return t_poly;
+}
+
+class CoerceGoalTest : public ::testing::Test {
+ public:
+ EIGEN_MAKE_ALIGNED_OPERATOR_NEW
+};
+
+// WHOOOHH!
+TEST_F(CoerceGoalTest, Inside) {
+ ::aos::controls::HPolytope<2> box = MakeBox(1, 2, 1, 2);
+
+ Eigen::Matrix<double, 1, 2> K;
+ K << /*[[*/ 1, -1 /*]]*/;
+
+ Eigen::Matrix<double, 2, 1> R;
+ R << /*[[*/ 1.5, 1.5 /*]]*/;
+
+ Eigen::Matrix<double, 2, 1> output =
+ ::frc971::control_loops::CoerceGoal(box, K, 0, R);
+
+ EXPECT_EQ(R(0, 0), output(0, 0));
+ EXPECT_EQ(R(1, 0), output(1, 0));
+}
+
+TEST_F(CoerceGoalTest, Outside_Inside_Intersect) {
+ ::aos::controls::HPolytope<2> box = MakeBox(1, 2, 1, 2);
+
+ Eigen::Matrix<double, 1, 2> K;
+ K << 1, -1;
+
+ Eigen::Matrix<double, 2, 1> R;
+ R << 5, 5;
+
+ Eigen::Matrix<double, 2, 1> output =
+ ::frc971::control_loops::CoerceGoal(box, K, 0, R);
+
+ EXPECT_EQ(2.0, output(0, 0));
+ EXPECT_EQ(2.0, output(1, 0));
+}
+
+TEST_F(CoerceGoalTest, Outside_Inside_no_Intersect) {
+ ::aos::controls::HPolytope<2> box = MakeBox(3, 4, 1, 2);
+
+ Eigen::Matrix<double, 1, 2> K;
+ K << 1, -1;
+
+ Eigen::Matrix<double, 2, 1> R;
+ R << 5, 5;
+
+ Eigen::Matrix<double, 2, 1> output =
+ ::frc971::control_loops::CoerceGoal(box, K, 0, R);
+
+ EXPECT_EQ(3.0, output(0, 0));
+ EXPECT_EQ(2.0, output(1, 0));
+}
+
+TEST_F(CoerceGoalTest, Middle_Of_Edge) {
+ ::aos::controls::HPolytope<2> box = MakeBox(0, 4, 1, 2);
+
+ Eigen::Matrix<double, 1, 2> K;
+ K << -1, 1;
+
+ Eigen::Matrix<double, 2, 1> R;
+ R << 5, 5;
+
+ Eigen::Matrix<double, 2, 1> output =
+ ::frc971::control_loops::CoerceGoal(box, K, 0, R);
+
+ EXPECT_EQ(2.0, output(0, 0));
+ EXPECT_EQ(2.0, output(1, 0));
+}
+
+TEST_F(CoerceGoalTest, PerpendicularLine) {
+ ::aos::controls::HPolytope<2> box = MakeBox(1, 2, 1, 2);
+
+ Eigen::Matrix<double, 1, 2> K;
+ K << 1, 1;
+
+ Eigen::Matrix<double, 2, 1> R;
+ R << 5, 5;
+
+ Eigen::Matrix<double, 2, 1> output =
+ ::frc971::control_loops::CoerceGoal(box, K, 0, R);
+
+ EXPECT_EQ(1.0, output(0, 0));
+ EXPECT_EQ(1.0, output(1, 0));
+}
+
+} // namespace testing
+} // namespace control_loops
+} // namespace frc971
diff --git a/y2014/control_loops/drivetrain/drivetrain_main.cc b/y2014/control_loops/drivetrain/drivetrain_main.cc
new file mode 100644
index 0000000..9a2ebe1
--- /dev/null
+++ b/y2014/control_loops/drivetrain/drivetrain_main.cc
@@ -0,0 +1,11 @@
+#include "y2014/control_loops/drivetrain/drivetrain.h"
+
+#include "aos/linux_code/init.h"
+
+int main() {
+ ::aos::Init();
+ frc971::control_loops::DrivetrainLoop drivetrain;
+ drivetrain.Run();
+ ::aos::Cleanup();
+ return 0;
+}
diff --git a/y2014/control_loops/drivetrain/polydrivetrain_cim_plant.cc b/y2014/control_loops/drivetrain/polydrivetrain_cim_plant.cc
new file mode 100644
index 0000000..0ee9a7a
--- /dev/null
+++ b/y2014/control_loops/drivetrain/polydrivetrain_cim_plant.cc
@@ -0,0 +1,49 @@
+#include "y2014/control_loops/drivetrain/polydrivetrain_cim_plant.h"
+
+#include <vector>
+
+#include "frc971/control_loops/state_feedback_loop.h"
+
+namespace frc971 {
+namespace control_loops {
+
+StateFeedbackPlantCoefficients<1, 1, 1> MakeCIMPlantCoefficients() {
+ Eigen::Matrix<double, 1, 1> A;
+ A << 0.614537580221;
+ Eigen::Matrix<double, 1, 1> B;
+ B << 15.9657598852;
+ Eigen::Matrix<double, 1, 1> C;
+ C << 1;
+ Eigen::Matrix<double, 1, 1> D;
+ D << 0;
+ Eigen::Matrix<double, 1, 1> U_max;
+ U_max << 12.0;
+ Eigen::Matrix<double, 1, 1> U_min;
+ U_min << -12.0;
+ return StateFeedbackPlantCoefficients<1, 1, 1>(A, B, C, D, U_max, U_min);
+}
+
+StateFeedbackController<1, 1, 1> MakeCIMController() {
+ Eigen::Matrix<double, 1, 1> L;
+ L << 0.604537580221;
+ Eigen::Matrix<double, 1, 1> K;
+ K << 0.0378646293422;
+ Eigen::Matrix<double, 1, 1> A_inv;
+ A_inv << 1.62723978514;
+ return StateFeedbackController<1, 1, 1>(L, K, A_inv, MakeCIMPlantCoefficients());
+}
+
+StateFeedbackPlant<1, 1, 1> MakeCIMPlant() {
+ ::std::vector< ::std::unique_ptr<StateFeedbackPlantCoefficients<1, 1, 1>>> plants(1);
+ plants[0] = ::std::unique_ptr<StateFeedbackPlantCoefficients<1, 1, 1>>(new StateFeedbackPlantCoefficients<1, 1, 1>(MakeCIMPlantCoefficients()));
+ return StateFeedbackPlant<1, 1, 1>(&plants);
+}
+
+StateFeedbackLoop<1, 1, 1> MakeCIMLoop() {
+ ::std::vector< ::std::unique_ptr<StateFeedbackController<1, 1, 1>>> controllers(1);
+ controllers[0] = ::std::unique_ptr<StateFeedbackController<1, 1, 1>>(new StateFeedbackController<1, 1, 1>(MakeCIMController()));
+ return StateFeedbackLoop<1, 1, 1>(&controllers);
+}
+
+} // namespace control_loops
+} // namespace frc971
diff --git a/y2014/control_loops/drivetrain/polydrivetrain_cim_plant.h b/y2014/control_loops/drivetrain/polydrivetrain_cim_plant.h
new file mode 100644
index 0000000..62af188
--- /dev/null
+++ b/y2014/control_loops/drivetrain/polydrivetrain_cim_plant.h
@@ -0,0 +1,20 @@
+#ifndef Y2014_CONTROL_LOOPS_DRIVETRAIN_POLYDRIVETRAIN_CIM_PLANT_H_
+#define Y2014_CONTROL_LOOPS_DRIVETRAIN_POLYDRIVETRAIN_CIM_PLANT_H_
+
+#include "frc971/control_loops/state_feedback_loop.h"
+
+namespace frc971 {
+namespace control_loops {
+
+StateFeedbackPlantCoefficients<1, 1, 1> MakeCIMPlantCoefficients();
+
+StateFeedbackController<1, 1, 1> MakeCIMController();
+
+StateFeedbackPlant<1, 1, 1> MakeCIMPlant();
+
+StateFeedbackLoop<1, 1, 1> MakeCIMLoop();
+
+} // namespace control_loops
+} // namespace frc971
+
+#endif // Y2014_CONTROL_LOOPS_DRIVETRAIN_POLYDRIVETRAIN_CIM_PLANT_H_
diff --git a/y2014/control_loops/drivetrain/polydrivetrain_dog_motor_plant.cc b/y2014/control_loops/drivetrain/polydrivetrain_dog_motor_plant.cc
new file mode 100644
index 0000000..a7d80ce
--- /dev/null
+++ b/y2014/control_loops/drivetrain/polydrivetrain_dog_motor_plant.cc
@@ -0,0 +1,133 @@
+#include "y2014/control_loops/drivetrain/polydrivetrain_dog_motor_plant.h"
+
+#include <vector>
+
+#include "frc971/control_loops/state_feedback_loop.h"
+
+namespace frc971 {
+namespace control_loops {
+
+StateFeedbackPlantCoefficients<2, 2, 2> MakeVelocityDrivetrainLowLowPlantCoefficients() {
+ Eigen::Matrix<double, 2, 2> A;
+ A << 0.735841675858, 0.0410810558113, 0.0410810558113, 0.735841675858;
+ Eigen::Matrix<double, 2, 2> B;
+ B << 0.0517213538779, -0.00804353916233, -0.00804353916233, 0.0517213538779;
+ Eigen::Matrix<double, 2, 2> C;
+ C << 1.0, 0.0, 0.0, 1.0;
+ Eigen::Matrix<double, 2, 2> D;
+ D << 0.0, 0.0, 0.0, 0.0;
+ Eigen::Matrix<double, 2, 1> U_max;
+ U_max << 12.0, 12.0;
+ Eigen::Matrix<double, 2, 1> U_min;
+ U_min << -12.0, -12.0;
+ return StateFeedbackPlantCoefficients<2, 2, 2>(A, B, C, D, U_max, U_min);
+}
+
+StateFeedbackPlantCoefficients<2, 2, 2> MakeVelocityDrivetrainLowHighPlantCoefficients() {
+ Eigen::Matrix<double, 2, 2> A;
+ A << 0.735048848179, 0.0131811893199, 0.045929121897, 0.915703853642;
+ Eigen::Matrix<double, 2, 2> B;
+ B << 0.0518765869984, -0.00481755802263, -0.00899277497558, 0.0308091755839;
+ Eigen::Matrix<double, 2, 2> C;
+ C << 1.0, 0.0, 0.0, 1.0;
+ Eigen::Matrix<double, 2, 2> D;
+ D << 0.0, 0.0, 0.0, 0.0;
+ Eigen::Matrix<double, 2, 1> U_max;
+ U_max << 12.0, 12.0;
+ Eigen::Matrix<double, 2, 1> U_min;
+ U_min << -12.0, -12.0;
+ return StateFeedbackPlantCoefficients<2, 2, 2>(A, B, C, D, U_max, U_min);
+}
+
+StateFeedbackPlantCoefficients<2, 2, 2> MakeVelocityDrivetrainHighLowPlantCoefficients() {
+ Eigen::Matrix<double, 2, 2> A;
+ A << 0.915703853642, 0.045929121897, 0.0131811893199, 0.735048848179;
+ Eigen::Matrix<double, 2, 2> B;
+ B << 0.0308091755839, -0.00899277497558, -0.00481755802263, 0.0518765869984;
+ Eigen::Matrix<double, 2, 2> C;
+ C << 1.0, 0.0, 0.0, 1.0;
+ Eigen::Matrix<double, 2, 2> D;
+ D << 0.0, 0.0, 0.0, 0.0;
+ Eigen::Matrix<double, 2, 1> U_max;
+ U_max << 12.0, 12.0;
+ Eigen::Matrix<double, 2, 1> U_min;
+ U_min << -12.0, -12.0;
+ return StateFeedbackPlantCoefficients<2, 2, 2>(A, B, C, D, U_max, U_min);
+}
+
+StateFeedbackPlantCoefficients<2, 2, 2> MakeVelocityDrivetrainHighHighPlantCoefficients() {
+ Eigen::Matrix<double, 2, 2> A;
+ A << 0.915439806567, 0.0146814193986, 0.0146814193986, 0.915439806567;
+ Eigen::Matrix<double, 2, 2> B;
+ B << 0.0309056814511, -0.00536587314624, -0.00536587314624, 0.0309056814511;
+ Eigen::Matrix<double, 2, 2> C;
+ C << 1.0, 0.0, 0.0, 1.0;
+ Eigen::Matrix<double, 2, 2> D;
+ D << 0.0, 0.0, 0.0, 0.0;
+ Eigen::Matrix<double, 2, 1> U_max;
+ U_max << 12.0, 12.0;
+ Eigen::Matrix<double, 2, 1> U_min;
+ U_min << -12.0, -12.0;
+ return StateFeedbackPlantCoefficients<2, 2, 2>(A, B, C, D, U_max, U_min);
+}
+
+StateFeedbackController<2, 2, 2> MakeVelocityDrivetrainLowLowController() {
+ Eigen::Matrix<double, 2, 2> L;
+ L << 0.715841675858, 0.0410810558113, 0.0410810558113, 0.715841675858;
+ Eigen::Matrix<double, 2, 2> K;
+ K << 2.81809403994, 1.23253744933, 1.23253744933, 2.81809403994;
+ Eigen::Matrix<double, 2, 2> A_inv;
+ A_inv << 1.36323698074, -0.0761076958907, -0.0761076958907, 1.36323698074;
+ return StateFeedbackController<2, 2, 2>(L, K, A_inv, MakeVelocityDrivetrainLowLowPlantCoefficients());
+}
+
+StateFeedbackController<2, 2, 2> MakeVelocityDrivetrainLowHighController() {
+ Eigen::Matrix<double, 2, 2> L;
+ L << 0.715885457343, 0.0459077351335, 0.0459077351335, 0.894867244478;
+ Eigen::Matrix<double, 2, 2> K;
+ K << 2.81810038978, 1.23928174475, 2.31332592354, 10.6088017388;
+ Eigen::Matrix<double, 2, 2> A_inv;
+ A_inv << 1.36167854796, -0.0196008159867, -0.0682979543713, 1.09303924439;
+ return StateFeedbackController<2, 2, 2>(L, K, A_inv, MakeVelocityDrivetrainLowHighPlantCoefficients());
+}
+
+StateFeedbackController<2, 2, 2> MakeVelocityDrivetrainHighLowController() {
+ Eigen::Matrix<double, 2, 2> L;
+ L << 0.902328849033, 0.014581304798, 0.014581304798, 0.708423852788;
+ Eigen::Matrix<double, 2, 2> K;
+ K << 10.6088017388, 2.31332592354, 1.23928174475, 2.81810038978;
+ Eigen::Matrix<double, 2, 2> A_inv;
+ A_inv << 1.09303924439, -0.0682979543713, -0.0196008159867, 1.36167854796;
+ return StateFeedbackController<2, 2, 2>(L, K, A_inv, MakeVelocityDrivetrainHighLowPlantCoefficients());
+}
+
+StateFeedbackController<2, 2, 2> MakeVelocityDrivetrainHighHighController() {
+ Eigen::Matrix<double, 2, 2> L;
+ L << 0.895439806567, 0.0146814193986, 0.0146814193986, 0.895439806567;
+ Eigen::Matrix<double, 2, 2> K;
+ K << 10.6088022944, 2.31694961514, 2.31694961514, 10.6088022944;
+ Eigen::Matrix<double, 2, 2> A_inv;
+ A_inv << 1.0926521463, -0.0175234726538, -0.0175234726538, 1.0926521463;
+ return StateFeedbackController<2, 2, 2>(L, K, A_inv, MakeVelocityDrivetrainHighHighPlantCoefficients());
+}
+
+StateFeedbackPlant<2, 2, 2> MakeVelocityDrivetrainPlant() {
+ ::std::vector< ::std::unique_ptr<StateFeedbackPlantCoefficients<2, 2, 2>>> plants(4);
+ plants[0] = ::std::unique_ptr<StateFeedbackPlantCoefficients<2, 2, 2>>(new StateFeedbackPlantCoefficients<2, 2, 2>(MakeVelocityDrivetrainLowLowPlantCoefficients()));
+ plants[1] = ::std::unique_ptr<StateFeedbackPlantCoefficients<2, 2, 2>>(new StateFeedbackPlantCoefficients<2, 2, 2>(MakeVelocityDrivetrainLowHighPlantCoefficients()));
+ plants[2] = ::std::unique_ptr<StateFeedbackPlantCoefficients<2, 2, 2>>(new StateFeedbackPlantCoefficients<2, 2, 2>(MakeVelocityDrivetrainHighLowPlantCoefficients()));
+ plants[3] = ::std::unique_ptr<StateFeedbackPlantCoefficients<2, 2, 2>>(new StateFeedbackPlantCoefficients<2, 2, 2>(MakeVelocityDrivetrainHighHighPlantCoefficients()));
+ return StateFeedbackPlant<2, 2, 2>(&plants);
+}
+
+StateFeedbackLoop<2, 2, 2> MakeVelocityDrivetrainLoop() {
+ ::std::vector< ::std::unique_ptr<StateFeedbackController<2, 2, 2>>> controllers(4);
+ controllers[0] = ::std::unique_ptr<StateFeedbackController<2, 2, 2>>(new StateFeedbackController<2, 2, 2>(MakeVelocityDrivetrainLowLowController()));
+ controllers[1] = ::std::unique_ptr<StateFeedbackController<2, 2, 2>>(new StateFeedbackController<2, 2, 2>(MakeVelocityDrivetrainLowHighController()));
+ controllers[2] = ::std::unique_ptr<StateFeedbackController<2, 2, 2>>(new StateFeedbackController<2, 2, 2>(MakeVelocityDrivetrainHighLowController()));
+ controllers[3] = ::std::unique_ptr<StateFeedbackController<2, 2, 2>>(new StateFeedbackController<2, 2, 2>(MakeVelocityDrivetrainHighHighController()));
+ return StateFeedbackLoop<2, 2, 2>(&controllers);
+}
+
+} // namespace control_loops
+} // namespace frc971
diff --git a/y2014/control_loops/drivetrain/polydrivetrain_dog_motor_plant.h b/y2014/control_loops/drivetrain/polydrivetrain_dog_motor_plant.h
new file mode 100644
index 0000000..dfac7be
--- /dev/null
+++ b/y2014/control_loops/drivetrain/polydrivetrain_dog_motor_plant.h
@@ -0,0 +1,32 @@
+#ifndef Y2014_CONTROL_LOOPS_DRIVETRAIN_POLYDRIVETRAIN_DOG_MOTOR_PLANT_H_
+#define Y2014_CONTROL_LOOPS_DRIVETRAIN_POLYDRIVETRAIN_DOG_MOTOR_PLANT_H_
+
+#include "frc971/control_loops/state_feedback_loop.h"
+
+namespace frc971 {
+namespace control_loops {
+
+StateFeedbackPlantCoefficients<2, 2, 2> MakeVelocityDrivetrainLowLowPlantCoefficients();
+
+StateFeedbackController<2, 2, 2> MakeVelocityDrivetrainLowLowController();
+
+StateFeedbackPlantCoefficients<2, 2, 2> MakeVelocityDrivetrainLowHighPlantCoefficients();
+
+StateFeedbackController<2, 2, 2> MakeVelocityDrivetrainLowHighController();
+
+StateFeedbackPlantCoefficients<2, 2, 2> MakeVelocityDrivetrainHighLowPlantCoefficients();
+
+StateFeedbackController<2, 2, 2> MakeVelocityDrivetrainHighLowController();
+
+StateFeedbackPlantCoefficients<2, 2, 2> MakeVelocityDrivetrainHighHighPlantCoefficients();
+
+StateFeedbackController<2, 2, 2> MakeVelocityDrivetrainHighHighController();
+
+StateFeedbackPlant<2, 2, 2> MakeVelocityDrivetrainPlant();
+
+StateFeedbackLoop<2, 2, 2> MakeVelocityDrivetrainLoop();
+
+} // namespace control_loops
+} // namespace frc971
+
+#endif // Y2014_CONTROL_LOOPS_DRIVETRAIN_POLYDRIVETRAIN_DOG_MOTOR_PLANT_H_
diff --git a/y2014/control_loops/drivetrain/replay_drivetrain.cc b/y2014/control_loops/drivetrain/replay_drivetrain.cc
new file mode 100644
index 0000000..26209fa
--- /dev/null
+++ b/y2014/control_loops/drivetrain/replay_drivetrain.cc
@@ -0,0 +1,24 @@
+#include "aos/common/controls/replay_control_loop.h"
+#include "aos/linux_code/init.h"
+
+#include "y2014/control_loops/drivetrain/drivetrain.q.h"
+
+// Reads one or more log files and sends out all the queue messages (in the
+// correct order and at the correct time) to feed a "live" drivetrain process.
+
+int main(int argc, char **argv) {
+ if (argc <= 1) {
+ fprintf(stderr, "Need at least one file to replay!\n");
+ return EXIT_FAILURE;
+ }
+
+ ::aos::InitNRT();
+
+ ::aos::controls::ControlLoopReplayer<::frc971::control_loops::DrivetrainQueue>
+ replayer(&::frc971::control_loops::drivetrain_queue, "drivetrain");
+ for (int i = 1; i < argc; ++i) {
+ replayer.ProcessFile(argv[i]);
+ }
+
+ ::aos::Cleanup();
+}
diff --git a/y2014/control_loops/python/arm.py b/y2014/control_loops/python/arm.py
new file mode 100755
index 0000000..e17990a
--- /dev/null
+++ b/y2014/control_loops/python/arm.py
@@ -0,0 +1,409 @@
+#!/usr/bin/python
+
+import control_loop
+import controls
+import polytope
+import polydrivetrain
+import numpy
+import math
+import sys
+import matplotlib
+from matplotlib import pylab
+
+
+class Arm(control_loop.ControlLoop):
+ def __init__(self, name="Arm", mass=None):
+ super(Arm, self).__init__(name)
+ # Stall Torque in N m
+ self.stall_torque = 0.476
+ # Stall Current in Amps
+ self.stall_current = 80.730
+ # Free Speed in RPM
+ self.free_speed = 13906.0
+ # Free Current in Amps
+ self.free_current = 5.820
+ # Mass of the arm
+ if mass is None:
+ self.mass = 13.0
+ else:
+ self.mass = mass
+
+ # Resistance of the motor
+ self.R = 12.0 / self.stall_current
+ # Motor velocity constant
+ self.Kv = ((self.free_speed / 60.0 * 2.0 * numpy.pi) /
+ (12.0 - self.R * self.free_current))
+ # Torque constant
+ self.Kt = self.stall_torque / self.stall_current
+ # Gear ratio
+ self.G = (44.0 / 12.0) * (54.0 / 14.0) * (54.0 / 14.0) * (44.0 / 20.0) * (72.0 / 16.0)
+ # Fridge arm length
+ self.r = 32 * 0.0254
+ # Control loop time step
+ self.dt = 0.005
+
+ # Arm moment of inertia
+ self.J = self.r * self.mass
+
+ # Arm left/right spring constant (N*m / radian)
+ self.spring = 100.0
+
+ # State is [average position, average velocity,
+ # position difference/2, velocity difference/2]
+ # Position difference is 1 - 2
+ # Input is [Voltage 1, Voltage 2]
+
+ self.C1 = self.spring / (self.J * 0.5)
+ self.C2 = self.Kt * self.G / (self.J * 0.5 * self.R)
+ self.C3 = self.G * self.G * self.Kt / (self.R * self.J * 0.5 * self.Kv)
+
+ self.A_continuous = numpy.matrix(
+ [[0, 1, 0, 0],
+ [0, -self.C3, 0, 0],
+ [0, 0, 0, 1],
+ [0, 0, -self.C1 * 2.0, -self.C3]])
+
+ print 'Full speed is', self.C2 / self.C3 * 12.0
+
+ print 'Stall arm difference is', 12.0 * self.C2 / self.C1
+ print 'Stall arm difference first principles is', self.stall_torque * self.G / self.spring
+
+ print '5 degrees of arm error is', self.spring / self.r * (math.pi * 5.0 / 180.0)
+
+ # Start with the unmodified input
+ self.B_continuous = numpy.matrix(
+ [[0, 0],
+ [self.C2 / 2.0, self.C2 / 2.0],
+ [0, 0],
+ [self.C2 / 2.0, -self.C2 / 2.0]])
+
+ self.C = numpy.matrix([[1, 0, 1, 0],
+ [1, 0, -1, 0]])
+ self.D = numpy.matrix([[0, 0],
+ [0, 0]])
+
+ self.A, self.B = self.ContinuousToDiscrete(
+ self.A_continuous, self.B_continuous, self.dt)
+
+ controlability = controls.ctrb(self.A, self.B);
+ print 'Rank of augmented controlability matrix.', numpy.linalg.matrix_rank(
+ controlability)
+
+ q_pos = 0.02
+ q_vel = 0.300
+ q_pos_diff = 0.005
+ q_vel_diff = 0.13
+ self.Q = numpy.matrix([[(1.0 / (q_pos ** 2.0)), 0.0, 0.0, 0.0],
+ [0.0, (1.0 / (q_vel ** 2.0)), 0.0, 0.0],
+ [0.0, 0.0, (1.0 / (q_pos_diff ** 2.0)), 0.0],
+ [0.0, 0.0, 0.0, (1.0 / (q_vel_diff ** 2.0))]])
+
+ self.R = numpy.matrix([[(1.0 / (12.0 ** 2.0)), 0.0],
+ [0.0, 1.0 / (12.0 ** 2.0)]])
+ self.K = controls.dlqr(self.A, self.B, self.Q, self.R)
+ print 'Controller'
+ print self.K
+
+ print 'Controller Poles'
+ print numpy.linalg.eig(self.A - self.B * self.K)[0]
+
+ self.rpl = 0.20
+ self.ipl = 0.05
+ self.PlaceObserverPoles([self.rpl + 1j * self.ipl,
+ self.rpl + 1j * self.ipl,
+ self.rpl - 1j * self.ipl,
+ self.rpl - 1j * self.ipl])
+
+ # The box formed by U_min and U_max must encompass all possible values,
+ # or else Austin's code gets angry.
+ self.U_max = numpy.matrix([[12.0], [12.0]])
+ self.U_min = numpy.matrix([[-12.0], [-12.0]])
+
+ print 'Observer (Converted to a KF)', numpy.linalg.inv(self.A) * self.L
+
+ self.InitializeState()
+
+
+class IntegralArm(Arm):
+ def __init__(self, name="IntegralArm", mass=None):
+ super(IntegralArm, self).__init__(name=name, mass=mass)
+
+ self.A_continuous_unaugmented = self.A_continuous
+ self.A_continuous = numpy.matrix(numpy.zeros((5, 5)))
+ self.A_continuous[0:4, 0:4] = self.A_continuous_unaugmented
+ self.A_continuous[1, 4] = self.C2
+
+ # Start with the unmodified input
+ self.B_continuous_unaugmented = self.B_continuous
+ self.B_continuous = numpy.matrix(numpy.zeros((5, 2)))
+ self.B_continuous[0:4, 0:2] = self.B_continuous_unaugmented
+
+ self.C_unaugmented = self.C
+ self.C = numpy.matrix(numpy.zeros((2, 5)))
+ self.C[0:2, 0:4] = self.C_unaugmented
+
+ self.A, self.B = self.ContinuousToDiscrete(
+ self.A_continuous, self.B_continuous, self.dt)
+ print 'A cont', self.A_continuous
+ print 'B cont', self.B_continuous
+ print 'A discrete', self.A
+
+ q_pos = 0.08
+ q_vel = 0.40
+
+ q_pos_diff = 0.08
+ q_vel_diff = 0.40
+ q_voltage = 6.0
+ self.Q = numpy.matrix([[(q_pos ** 2.0), 0.0, 0.0, 0.0, 0.0],
+ [0.0, (q_vel ** 2.0), 0.0, 0.0, 0.0],
+ [0.0, 0.0, (q_pos_diff ** 2.0), 0.0, 0.0],
+ [0.0, 0.0, 0.0, (q_vel_diff ** 2.0), 0.0],
+ [0.0, 0.0, 0.0, 0.0, (q_voltage ** 2.0)]])
+
+ r_volts = 0.05
+ self.R = numpy.matrix([[(r_volts ** 2.0), 0.0],
+ [0.0, (r_volts ** 2.0)]])
+
+ self.KalmanGain, self.Q_steady = controls.kalman(
+ A=self.A, B=self.B, C=self.C, Q=self.Q, R=self.R)
+
+ self.U_max = numpy.matrix([[12.0], [12.0]])
+ self.U_min = numpy.matrix([[-12.0], [-12.0]])
+
+ self.K_unaugmented = self.K
+ self.K = numpy.matrix(numpy.zeros((2, 5)));
+ self.K[0:2, 0:4] = self.K_unaugmented
+ self.K[0, 4] = 1;
+ self.K[1, 4] = 1;
+ print 'Kal', self.KalmanGain
+ self.L = self.A * self.KalmanGain
+
+ self.InitializeState()
+
+
+def CapU(U):
+ if U[0, 0] - U[1, 0] > 24:
+ return numpy.matrix([[12], [-12]])
+ elif U[0, 0] - U[1, 0] < -24:
+ return numpy.matrix([[-12], [12]])
+ else:
+ max_u = max(U[0, 0], U[1, 0])
+ min_u = min(U[0, 0], U[1, 0])
+ if max_u > 12:
+ return U - (max_u - 12)
+ if min_u < -12:
+ return U - (min_u + 12)
+ return U
+
+
+def run_test(arm, initial_X, goal, max_separation_error=0.01,
+ show_graph=True, iterations=200, controller_arm=None,
+ observer_arm=None):
+ """Runs the arm plant with an initial condition and goal.
+
+ The tests themselves are not terribly sophisticated; I just test for
+ whether the goal has been reached and whether the separation goes
+ outside of the initial and goal values by more than max_separation_error.
+ Prints out something for a failure of either condition and returns
+ False if tests fail.
+ Args:
+ arm: arm object to use.
+ initial_X: starting state.
+ goal: goal state.
+ show_graph: Whether or not to display a graph showing the changing
+ states and voltages.
+ iterations: Number of timesteps to run the model for.
+ controller_arm: arm object to get K from, or None if we should
+ use arm.
+ observer_arm: arm object to use for the observer, or None if we should
+ use the actual state.
+ """
+
+ arm.X = initial_X
+
+ if controller_arm is None:
+ controller_arm = arm
+
+ if observer_arm is not None:
+ observer_arm.X_hat = initial_X + 0.01
+ observer_arm.X_hat = initial_X
+
+ # Various lists for graphing things.
+ t = []
+ x_avg = []
+ x_sep = []
+ x_hat_avg = []
+ x_hat_sep = []
+ v_avg = []
+ v_sep = []
+ u_left = []
+ u_right = []
+
+ sep_plot_gain = 100.0
+
+ for i in xrange(iterations):
+ X_hat = arm.X
+ if observer_arm is not None:
+ X_hat = observer_arm.X_hat
+ x_hat_avg.append(observer_arm.X_hat[0, 0])
+ x_hat_sep.append(observer_arm.X_hat[2, 0] * sep_plot_gain)
+ U = controller_arm.K * (goal - X_hat)
+ U = CapU(U)
+ x_avg.append(arm.X[0, 0])
+ v_avg.append(arm.X[1, 0])
+ x_sep.append(arm.X[2, 0] * sep_plot_gain)
+ v_sep.append(arm.X[3, 0])
+ if observer_arm is not None:
+ observer_arm.PredictObserver(U)
+ arm.Update(U)
+ if observer_arm is not None:
+ observer_arm.Y = arm.Y
+ observer_arm.CorrectObserver(U)
+
+ t.append(i * arm.dt)
+ u_left.append(U[0, 0])
+ u_right.append(U[1, 0])
+
+ print numpy.linalg.inv(arm.A)
+ print "delta time is ", arm.dt
+ print "Velocity at t=0 is ", x_avg[0], v_avg[0], x_sep[0], v_sep[0]
+ print "Velocity at t=1+dt is ", x_avg[1], v_avg[1], x_sep[1], v_sep[1]
+
+ if show_graph:
+ pylab.subplot(2, 1, 1)
+ pylab.plot(t, x_avg, label='x avg')
+ pylab.plot(t, x_sep, label='x sep')
+ if observer_arm is not None:
+ pylab.plot(t, x_hat_avg, label='x_hat avg')
+ pylab.plot(t, x_hat_sep, label='x_hat sep')
+ pylab.legend()
+
+ pylab.subplot(2, 1, 2)
+ pylab.plot(t, u_left, label='u left')
+ pylab.plot(t, u_right, label='u right')
+ pylab.legend()
+ pylab.show()
+
+
+def run_integral_test(arm, initial_X, goal, observer_arm, disturbance,
+ max_separation_error=0.01, show_graph=True,
+ iterations=400):
+ """Runs the integral control arm plant with an initial condition and goal.
+
+ The tests themselves are not terribly sophisticated; I just test for
+ whether the goal has been reached and whether the separation goes
+ outside of the initial and goal values by more than max_separation_error.
+ Prints out something for a failure of either condition and returns
+ False if tests fail.
+ Args:
+ arm: arm object to use.
+ initial_X: starting state.
+ goal: goal state.
+ observer_arm: arm object to use for the observer.
+ show_graph: Whether or not to display a graph showing the changing
+ states and voltages.
+ iterations: Number of timesteps to run the model for.
+ disturbance: Voltage missmatch between controller and model.
+ """
+
+ arm.X = initial_X
+
+ # Various lists for graphing things.
+ t = []
+ x_avg = []
+ x_sep = []
+ x_hat_avg = []
+ x_hat_sep = []
+ v_avg = []
+ v_sep = []
+ u_left = []
+ u_right = []
+ u_error = []
+
+ sep_plot_gain = 100.0
+
+ unaugmented_goal = goal
+ goal = numpy.matrix(numpy.zeros((5, 1)))
+ goal[0:4, 0] = unaugmented_goal
+
+ for i in xrange(iterations):
+ X_hat = observer_arm.X_hat[0:4]
+
+ x_hat_avg.append(observer_arm.X_hat[0, 0])
+ x_hat_sep.append(observer_arm.X_hat[2, 0] * sep_plot_gain)
+
+ U = observer_arm.K * (goal - observer_arm.X_hat)
+ u_error.append(observer_arm.X_hat[4,0])
+ U = CapU(U)
+ x_avg.append(arm.X[0, 0])
+ v_avg.append(arm.X[1, 0])
+ x_sep.append(arm.X[2, 0] * sep_plot_gain)
+ v_sep.append(arm.X[3, 0])
+
+ observer_arm.PredictObserver(U)
+
+ arm.Update(U + disturbance)
+ observer_arm.Y = arm.Y
+ observer_arm.CorrectObserver(U)
+
+ t.append(i * arm.dt)
+ u_left.append(U[0, 0])
+ u_right.append(U[1, 0])
+
+ print 'End is', observer_arm.X_hat[4, 0]
+
+ if show_graph:
+ pylab.subplot(2, 1, 1)
+ pylab.plot(t, x_avg, label='x avg')
+ pylab.plot(t, x_sep, label='x sep')
+ if observer_arm is not None:
+ pylab.plot(t, x_hat_avg, label='x_hat avg')
+ pylab.plot(t, x_hat_sep, label='x_hat sep')
+ pylab.legend()
+
+ pylab.subplot(2, 1, 2)
+ pylab.plot(t, u_left, label='u left')
+ pylab.plot(t, u_right, label='u right')
+ pylab.plot(t, u_error, label='u error')
+ pylab.legend()
+ pylab.show()
+
+
+def main(argv):
+ loaded_mass = 25
+ #loaded_mass = 0
+ arm = Arm(mass=13 + loaded_mass)
+ #arm_controller = Arm(mass=13 + 15)
+ #observer_arm = Arm(mass=13 + 15)
+ #observer_arm = None
+
+ integral_arm = IntegralArm(mass=13 + loaded_mass)
+ integral_arm.X_hat[0, 0] += 0.02
+ integral_arm.X_hat[2, 0] += 0.02
+ integral_arm.X_hat[4] = 0
+
+ # Test moving the arm with constant separation.
+ initial_X = numpy.matrix([[0.0], [0.0], [0.0], [0.0]])
+ R = numpy.matrix([[0.0], [0.0], [0.0], [0.0]])
+ run_integral_test(arm, initial_X, R, integral_arm, disturbance=2)
+
+ # Write the generated constants out to a file.
+ if len(argv) != 5:
+ print "Expected .h file name and .cc file name for the arm and augmented arm."
+ else:
+ arm = Arm("Arm", mass=13)
+ loop_writer = control_loop.ControlLoopWriter("Arm", [arm])
+ if argv[1][-3:] == '.cc':
+ loop_writer.Write(argv[2], argv[1])
+ else:
+ loop_writer.Write(argv[1], argv[2])
+
+ integral_arm = IntegralArm("IntegralArm", mass=13)
+ loop_writer = control_loop.ControlLoopWriter("IntegralArm", [integral_arm])
+ if argv[3][-3:] == '.cc':
+ loop_writer.Write(argv[4], argv[3])
+ else:
+ loop_writer.Write(argv[3], argv[4])
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv))
diff --git a/y2014/control_loops/python/claw.py b/y2014/control_loops/python/claw.py
new file mode 100755
index 0000000..b5ea6a1
--- /dev/null
+++ b/y2014/control_loops/python/claw.py
@@ -0,0 +1,491 @@
+#!/usr/bin/python
+
+import control_loop
+import controls
+import polytope
+import polydrivetrain
+import numpy
+import sys
+from matplotlib import pylab
+
+class Claw(control_loop.ControlLoop):
+ def __init__(self, name="RawClaw"):
+ super(Claw, self).__init__(name)
+ # Stall Torque in N m
+ self.stall_torque = 2.42
+ # Stall Current in Amps
+ self.stall_current = 133
+ # Free Speed in RPM
+ self.free_speed = 5500.0
+ # Free Current in Amps
+ self.free_current = 2.7
+ # Moment of inertia of the claw in kg m^2
+ self.J_top = 2.8
+ self.J_bottom = 3.0
+
+ # Resistance of the motor
+ self.R = 12.0 / self.stall_current
+ # Motor velocity constant
+ self.Kv = ((self.free_speed / 60.0 * 2.0 * numpy.pi) /
+ (13.5 - self.R * self.free_current))
+ # Torque constant
+ self.Kt = self.stall_torque / self.stall_current
+ # Gear ratio
+ self.G = 14.0 / 48.0 * 18.0 / 32.0 * 18.0 / 66.0 * 12.0 / 60.0
+ # Control loop time step
+ self.dt = 0.01
+
+ # State is [bottom position, bottom velocity, top - bottom position,
+ # top - bottom velocity]
+ # Input is [bottom power, top power - bottom power * J_top / J_bottom]
+ # Motor time constants. difference_bottom refers to the constant for how the
+ # bottom velocity affects the difference of the top and bottom velocities.
+ self.common_motor_constant = -self.Kt / self.Kv / (self.G * self.G * self.R)
+ self.bottom_bottom = self.common_motor_constant / self.J_bottom
+ self.difference_bottom = -self.common_motor_constant * (1 / self.J_bottom
+ - 1 / self.J_top)
+ self.difference_difference = self.common_motor_constant / self.J_top
+ # State feedback matrices
+
+ self.A_continuous = numpy.matrix(
+ [[0, 0, 1, 0],
+ [0, 0, 0, 1],
+ [0, 0, self.bottom_bottom, 0],
+ [0, 0, self.difference_bottom, self.difference_difference]])
+
+ self.A_bottom_cont = numpy.matrix(
+ [[0, 1],
+ [0, self.bottom_bottom]])
+
+ self.A_diff_cont = numpy.matrix(
+ [[0, 1],
+ [0, self.difference_difference]])
+
+ self.motor_feedforward = self.Kt / (self.G * self.R)
+ self.motor_feedforward_bottom = self.motor_feedforward / self.J_bottom
+ self.motor_feedforward_difference = self.motor_feedforward / self.J_top
+ self.motor_feedforward_difference_bottom = (
+ self.motor_feedforward * (1 / self.J_bottom - 1 / self.J_top))
+ self.B_continuous = numpy.matrix(
+ [[0, 0],
+ [0, 0],
+ [self.motor_feedforward_bottom, 0],
+ [-self.motor_feedforward_bottom, self.motor_feedforward_difference]])
+
+ print "Cont X_ss", self.motor_feedforward / -self.common_motor_constant
+
+ self.B_bottom_cont = numpy.matrix(
+ [[0],
+ [self.motor_feedforward_bottom]])
+
+ self.B_diff_cont = numpy.matrix(
+ [[0],
+ [self.motor_feedforward_difference]])
+
+ self.C = numpy.matrix([[1, 0, 0, 0],
+ [1, 1, 0, 0]])
+ self.D = numpy.matrix([[0, 0],
+ [0, 0]])
+
+ self.A, self.B = self.ContinuousToDiscrete(
+ self.A_continuous, self.B_continuous, self.dt)
+
+ self.A_bottom, self.B_bottom = controls.c2d(
+ self.A_bottom_cont, self.B_bottom_cont, self.dt)
+ self.A_diff, self.B_diff = controls.c2d(
+ self.A_diff_cont, self.B_diff_cont, self.dt)
+
+ self.K_bottom = controls.dplace(self.A_bottom, self.B_bottom, [.75 + 0.1j, .75 - 0.1j])
+ self.K_diff = controls.dplace(self.A_diff, self.B_diff, [.75 + 0.1j, .75 - 0.1j])
+
+ print "K_diff", self.K_diff
+ print "K_bottom", self.K_bottom
+
+ print "A"
+ print self.A
+ print "B"
+ print self.B
+
+
+ self.Q = numpy.matrix([[(1.0 / (0.10 ** 2.0)), 0.0, 0.0, 0.0],
+ [0.0, (1.0 / (0.06 ** 2.0)), 0.0, 0.0],
+ [0.0, 0.0, 0.10, 0.0],
+ [0.0, 0.0, 0.0, 0.1]])
+
+ self.R = numpy.matrix([[(1.0 / (40.0 ** 2.0)), 0.0],
+ [0.0, (1.0 / (5.0 ** 2.0))]])
+ #self.K = controls.dlqr(self.A, self.B, self.Q, self.R)
+
+ self.K = numpy.matrix([[self.K_bottom[0, 0], 0.0, self.K_bottom[0, 1], 0.0],
+ [0.0, self.K_diff[0, 0], 0.0, self.K_diff[0, 1]]])
+
+ # Compute the feed forwards aceleration term.
+ self.K[1, 0] = -self.B[1, 0] * self.K[0, 0] / self.B[1, 1]
+
+ lstsq_A = numpy.identity(2)
+ lstsq_A[0, :] = self.B[1, :]
+ lstsq_A[1, :] = self.B[3, :]
+ print "System of Equations coefficients:"
+ print lstsq_A
+ print "det", numpy.linalg.det(lstsq_A)
+
+ out_x = numpy.linalg.lstsq(
+ lstsq_A,
+ numpy.matrix([[self.A[1, 2]], [self.A[3, 2]]]))[0]
+ self.K[1, 2] = -lstsq_A[0, 0] * (self.K[0, 2] - out_x[0]) / lstsq_A[0, 1] + out_x[1]
+
+ print "K unaugmented"
+ print self.K
+ print "B * K unaugmented"
+ print self.B * self.K
+ F = self.A - self.B * self.K
+ print "A - B * K unaugmented"
+ print F
+ print "eigenvalues"
+ print numpy.linalg.eig(F)[0]
+
+ self.rpl = .05
+ self.ipl = 0.010
+ self.PlaceObserverPoles([self.rpl + 1j * self.ipl,
+ self.rpl + 1j * self.ipl,
+ self.rpl - 1j * self.ipl,
+ self.rpl - 1j * self.ipl])
+
+ # The box formed by U_min and U_max must encompass all possible values,
+ # or else Austin's code gets angry.
+ self.U_max = numpy.matrix([[12.0], [12.0]])
+ self.U_min = numpy.matrix([[-12.0], [-12.0]])
+
+ # For the tests that check the limits, these are (upper, lower) for both
+ # claws.
+ self.hard_pos_limits = None
+ self.pos_limits = None
+
+ # Compute the steady state velocities for a given applied voltage.
+ # The top and bottom of the claw should spin at the same rate if the
+ # physics is right.
+ X_ss = numpy.matrix([[0], [0], [0.0], [0]])
+
+ U = numpy.matrix([[1.0],[1.0]])
+ A = self.A
+ B = self.B
+ #X_ss[2, 0] = X_ss[2, 0] * A[2, 2] + B[2, 0] * U[0, 0]
+ X_ss[2, 0] = 1 / (1 - A[2, 2]) * B[2, 0] * U[0, 0]
+ #X_ss[3, 0] = X_ss[3, 0] * A[3, 3] + X_ss[2, 0] * A[3, 2] + B[3, 0] * U[0, 0] + B[3, 1] * U[1, 0]
+ #X_ss[3, 0] * (1 - A[3, 3]) = X_ss[2, 0] * A[3, 2] + B[3, 0] * U[0, 0] + B[3, 1] * U[1, 0]
+ X_ss[3, 0] = 1 / (1 - A[3, 3]) * (X_ss[2, 0] * A[3, 2] + B[3, 1] * U[1, 0] + B[3, 0] * U[0, 0])
+ #X_ss[3, 0] = 1 / (1 - A[3, 3]) / (1 - A[2, 2]) * B[2, 0] * U[0, 0] * A[3, 2] + B[3, 0] * U[0, 0] + B[3, 1] * U[1, 0]
+ X_ss[0, 0] = A[0, 2] * X_ss[2, 0] + B[0, 0] * U[0, 0]
+ X_ss[1, 0] = A[1, 2] * X_ss[2, 0] + A[1, 3] * X_ss[3, 0] + B[1, 0] * U[0, 0] + B[1, 1] * U[1, 0]
+
+ print "X_ss", X_ss
+
+ self.InitializeState()
+
+
+class ClawDeltaU(Claw):
+ def __init__(self, name="Claw"):
+ super(ClawDeltaU, self).__init__(name)
+ A_unaugmented = self.A
+ B_unaugmented = self.B
+ C_unaugmented = self.C
+
+ self.A = numpy.matrix([[0.0, 0.0, 0.0, 0.0, 0.0],
+ [0.0, 0.0, 0.0, 0.0, 0.0],
+ [0.0, 0.0, 0.0, 0.0, 0.0],
+ [0.0, 0.0, 0.0, 0.0, 0.0],
+ [0.0, 0.0, 0.0, 0.0, 1.0]])
+ self.A[0:4, 0:4] = A_unaugmented
+ self.A[0:4, 4] = B_unaugmented[0:4, 0]
+
+ self.B = numpy.matrix([[0.0, 0.0],
+ [0.0, 0.0],
+ [0.0, 0.0],
+ [0.0, 0.0],
+ [1.0, 0.0]])
+ self.B[0:4, 1] = B_unaugmented[0:4, 1]
+
+ self.C = numpy.concatenate((C_unaugmented, numpy.matrix([[0.0], [0.0]])),
+ axis=1)
+ self.D = numpy.matrix([[0.0, 0.0],
+ [0.0, 0.0]])
+
+ #self.PlaceControllerPoles([0.55, 0.35, 0.55, 0.35, 0.80])
+ self.Q = numpy.matrix([[(1.0 / (0.04 ** 2.0)), 0.0, 0.0, 0.0, 0.0],
+ [0.0, (1.0 / (0.01 ** 2)), 0.0, 0.0, 0.0],
+ [0.0, 0.0, 0.01, 0.0, 0.0],
+ [0.0, 0.0, 0.0, 0.08, 0.0],
+ [0.0, 0.0, 0.0, 0.0, (1.0 / (10.0 ** 2))]])
+
+ self.R = numpy.matrix([[0.000001, 0.0],
+ [0.0, 1.0 / (10.0 ** 2.0)]])
+ self.K = controls.dlqr(self.A, self.B, self.Q, self.R)
+
+ self.K = numpy.matrix([[50.0, 0.0, 10.0, 0.0, 1.0],
+ [50.0, 0.0, 10.0, 0.0, 1.0]])
+ #self.K = numpy.matrix([[50.0, -100.0, 0, -10, 0],
+ # [50.0, 100.0, 0, 10, 0]])
+
+ controlability = controls.ctrb(self.A, self.B);
+ print "Rank of augmented controlability matrix.", numpy.linalg.matrix_rank(controlability)
+
+ print "K"
+ print self.K
+ print "Placed controller poles are"
+ print numpy.linalg.eig(self.A - self.B * self.K)[0]
+ print [numpy.abs(x) for x in numpy.linalg.eig(self.A - self.B * self.K)[0]]
+
+ self.rpl = .05
+ self.ipl = 0.008
+ self.PlaceObserverPoles([self.rpl + 1j * self.ipl, 0.10, 0.09,
+ self.rpl - 1j * self.ipl, 0.90])
+ #print "A is"
+ #print self.A
+ #print "L is"
+ #print self.L
+ #print "C is"
+ #print self.C
+ #print "A - LC is"
+ #print self.A - self.L * self.C
+
+ #print "Placed observer poles are"
+ #print numpy.linalg.eig(self.A - self.L * self.C)[0]
+
+ self.U_max = numpy.matrix([[12.0], [12.0]])
+ self.U_min = numpy.matrix([[-12.0], [-12.0]])
+
+ self.InitializeState()
+
+def ScaleU(claw, U, K, error):
+ """Clips U as necessary.
+
+ Args:
+ claw: claw object containing moments of inertia and U limits.
+ U: Input matrix to clip as necessary.
+ """
+
+ bottom_u = U[0, 0]
+ top_u = U[1, 0]
+ position_error = error[0:2, 0]
+ velocity_error = error[2:, 0]
+
+ U_poly = polytope.HPolytope(
+ numpy.matrix([[1, 0],
+ [-1, 0],
+ [0, 1],
+ [0, -1]]),
+ numpy.matrix([[12],
+ [12],
+ [12],
+ [12]]))
+
+ if (bottom_u > claw.U_max[0, 0] or bottom_u < claw.U_min[0, 0] or
+ top_u > claw.U_max[0, 0] or top_u < claw.U_min[0, 0]):
+
+ position_K = K[:, 0:2]
+ velocity_K = K[:, 2:]
+
+ # H * U <= k
+ # U = UPos + UVel
+ # H * (UPos + UVel) <= k
+ # H * UPos <= k - H * UVel
+ #
+ # Now, we can do a coordinate transformation and say the following.
+ #
+ # UPos = position_K * position_error
+ # (H * position_K) * position_error <= k - H * UVel
+ #
+ # Add in the constraint that 0 <= t <= 1
+ # Now, there are 2 ways this can go. Either we have a region, or we don't
+ # have a region. If we have a region, then pick the largest t and go for it.
+ # If we don't have a region, we need to pick a good comprimise.
+
+ pos_poly = polytope.HPolytope(
+ U_poly.H * position_K,
+ U_poly.k - U_poly.H * velocity_K * velocity_error)
+
+ # The actual angle for the line we call 45.
+ angle_45 = numpy.matrix([[numpy.sqrt(3), 1]])
+ if claw.pos_limits and claw.hard_pos_limits and claw.X[0, 0] + claw.X[1, 0] > claw.pos_limits[1]:
+ angle_45 = numpy.matrix([[1, 1]])
+
+ P = position_error
+ L45 = numpy.multiply(numpy.matrix([[numpy.sign(P[1, 0]), -numpy.sign(P[0, 0])]]), angle_45)
+ if L45[0, 1] == 0:
+ L45[0, 1] = 1
+ if L45[0, 0] == 0:
+ L45[0, 0] = 1
+ w45 = numpy.matrix([[0]])
+
+ if numpy.abs(P[0, 0]) > numpy.abs(P[1, 0]):
+ LH = numpy.matrix([[0, 1]])
+ else:
+ LH = numpy.matrix([[1, 0]])
+ wh = LH * P
+ standard = numpy.concatenate((L45, LH))
+ W = numpy.concatenate((w45, wh))
+ intersection = numpy.linalg.inv(standard) * W
+ adjusted_pos_error_h, is_inside_h = polydrivetrain.DoCoerceGoal(pos_poly,
+ LH, wh, position_error)
+ adjusted_pos_error_45, is_inside_45 = polydrivetrain.DoCoerceGoal(pos_poly,
+ L45, w45, intersection)
+ if pos_poly.IsInside(intersection):
+ adjusted_pos_error = adjusted_pos_error_h
+ else:
+ if is_inside_h:
+ if numpy.linalg.norm(adjusted_pos_error_h) > numpy.linalg.norm(adjusted_pos_error_45):
+ adjusted_pos_error = adjusted_pos_error_h
+ else:
+ adjusted_pos_error = adjusted_pos_error_45
+ else:
+ adjusted_pos_error = adjusted_pos_error_45
+ #print adjusted_pos_error
+
+ #print "Actual power is ", velocity_K * velocity_error + position_K * adjusted_pos_error
+ return velocity_K * velocity_error + position_K * adjusted_pos_error
+
+ #U = Kpos * poserror + Kvel * velerror
+
+ #scalar = claw.U_max[0, 0] / max(numpy.abs(top_u), numpy.abs(bottom_u))
+
+ #top_u *= scalar
+ #bottom_u *= scalar
+
+ return numpy.matrix([[bottom_u], [top_u]])
+
+def run_test(claw, initial_X, goal, max_separation_error=0.01, show_graph=False, iterations=200):
+ """Runs the claw plant on a given claw (claw) with an initial condition (initial_X) and goal (goal).
+
+ The tests themselves are not terribly sophisticated; I just test for
+ whether the goal has been reached and whether the separation goes
+ outside of the initial and goal values by more than max_separation_error.
+ Prints out something for a failure of either condition and returns
+ False if tests fail.
+ Args:
+ claw: claw object to use.
+ initial_X: starting state.
+ goal: goal state.
+ show_graph: Whether or not to display a graph showing the changing
+ states and voltages.
+ iterations: Number of timesteps to run the model for."""
+
+ claw.X = initial_X
+
+ # Various lists for graphing things.
+ t = []
+ x_bottom = []
+ x_top = []
+ u_bottom = []
+ u_top = []
+ x_separation = []
+
+ tests_passed = True
+
+ # Bounds which separation should not exceed.
+ lower_bound = (initial_X[1, 0] if initial_X[1, 0] < goal[1, 0]
+ else goal[1, 0]) - max_separation_error
+ upper_bound = (initial_X[1, 0] if initial_X[1, 0] > goal[1, 0]
+ else goal[1, 0]) + max_separation_error
+
+ for i in xrange(iterations):
+ U = claw.K * (goal - claw.X)
+ U = ScaleU(claw, U, claw.K, goal - claw.X)
+ claw.Update(U)
+
+ if claw.X[1, 0] > upper_bound or claw.X[1, 0] < lower_bound:
+ tests_passed = False
+ print "Claw separation was", claw.X[1, 0]
+ print "Should have been between", lower_bound, "and", upper_bound
+
+ if claw.hard_pos_limits and \
+ (claw.X[0, 0] > claw.hard_pos_limits[1] or
+ claw.X[0, 0] < claw.hard_pos_limits[0] or
+ claw.X[0, 0] + claw.X[1, 0] > claw.hard_pos_limits[1] or
+ claw.X[0, 0] + claw.X[1, 0] < claw.hard_pos_limits[0]):
+ tests_passed = False
+ print "Claws at %f and %f" % (claw.X[0, 0], claw.X[0, 0] + claw.X[1, 0])
+ print "Both should be in %s, definitely %s" % \
+ (claw.pos_limits, claw.hard_pos_limits)
+
+ t.append(i * claw.dt)
+ x_bottom.append(claw.X[0, 0] * 10.0)
+ x_top.append((claw.X[1, 0] + claw.X[0, 0]) * 10.0)
+ u_bottom.append(U[0, 0])
+ u_top.append(U[1, 0])
+ x_separation.append(claw.X[1, 0] * 10.0)
+
+ if show_graph:
+ pylab.plot(t, x_bottom, label='x bottom * 10')
+ pylab.plot(t, x_top, label='x top * 10')
+ pylab.plot(t, u_bottom, label='u bottom')
+ pylab.plot(t, u_top, label='u top')
+ pylab.plot(t, x_separation, label='separation * 10')
+ pylab.legend()
+ pylab.show()
+
+ # Test to make sure that we are near the goal.
+ if numpy.max(abs(claw.X - goal)) > 1e-4:
+ tests_passed = False
+ print "X was", claw.X, "Expected", goal
+
+ return tests_passed
+
+def main(argv):
+ claw = Claw()
+
+ # Test moving the claw with constant separation.
+ initial_X = numpy.matrix([[-1.0], [0.0], [0.0], [0.0]])
+ R = numpy.matrix([[1.0], [0.0], [0.0], [0.0]])
+ run_test(claw, initial_X, R)
+
+ # Test just changing separation.
+ initial_X = numpy.matrix([[0.0], [0.0], [0.0], [0.0]])
+ R = numpy.matrix([[0.0], [1.0], [0.0], [0.0]])
+ run_test(claw, initial_X, R)
+
+ # Test changing both separation and position at once.
+ initial_X = numpy.matrix([[0.0], [0.0], [0.0], [0.0]])
+ R = numpy.matrix([[1.0], [1.0], [0.0], [0.0]])
+ run_test(claw, initial_X, R)
+
+ # Test a small separation error and a large position one.
+ initial_X = numpy.matrix([[0.0], [0.0], [0.0], [0.0]])
+ R = numpy.matrix([[2.0], [0.05], [0.0], [0.0]])
+ run_test(claw, initial_X, R)
+
+ # Test a small separation error and a large position one.
+ initial_X = numpy.matrix([[0.0], [0.0], [0.0], [0.0]])
+ R = numpy.matrix([[-0.5], [1.0], [0.0], [0.0]])
+ run_test(claw, initial_X, R)
+
+ # Test opening with the top claw at the limit.
+ initial_X = numpy.matrix([[0.0], [0.0], [0.0], [0.0]])
+ R = numpy.matrix([[-1.5], [1.5], [0.0], [0.0]])
+ claw.hard_pos_limits = (-1.6, 0.1)
+ claw.pos_limits = (-1.5, 0.0)
+ run_test(claw, initial_X, R)
+ claw.pos_limits = None
+
+ # Test opening with the bottom claw at the limit.
+ initial_X = numpy.matrix([[0.0], [0.0], [0.0], [0.0]])
+ R = numpy.matrix([[0], [1.5], [0.0], [0.0]])
+ claw.hard_pos_limits = (-0.1, 1.6)
+ claw.pos_limits = (0.0, 1.6)
+ run_test(claw, initial_X, R)
+ claw.pos_limits = None
+
+ # Write the generated constants out to a file.
+ if len(argv) != 3:
+ print "Expected .h file name and .cc file name for the claw."
+ else:
+ claw = Claw("Claw")
+ loop_writer = control_loop.ControlLoopWriter("Claw", [claw])
+ loop_writer.AddConstant(control_loop.Constant("kClawMomentOfInertiaRatio",
+ "%f", claw.J_top / claw.J_bottom))
+ if argv[1][-3:] == '.cc':
+ loop_writer.Write(argv[2], argv[1])
+ else:
+ loop_writer.Write(argv[1], argv[2])
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv))
diff --git a/y2014/control_loops/python/drivetrain.py b/y2014/control_loops/python/drivetrain.py
new file mode 100755
index 0000000..ac05a57
--- /dev/null
+++ b/y2014/control_loops/python/drivetrain.py
@@ -0,0 +1,239 @@
+#!/usr/bin/python
+
+import control_loop
+import controls
+import numpy
+import sys
+from matplotlib import pylab
+
+
+class CIM(control_loop.ControlLoop):
+ def __init__(self):
+ super(CIM, self).__init__("CIM")
+ # Stall Torque in N m
+ self.stall_torque = 2.42
+ # Stall Current in Amps
+ self.stall_current = 133
+ # Free Speed in RPM
+ self.free_speed = 4650.0
+ # Free Current in Amps
+ self.free_current = 2.7
+ # Moment of inertia of the CIM in kg m^2
+ self.J = 0.0001
+ # Resistance of the motor, divided by 2 to account for the 2 motors
+ self.R = 12.0 / self.stall_current
+ # Motor velocity constant
+ self.Kv = ((self.free_speed / 60.0 * 2.0 * numpy.pi) /
+ (12.0 - self.R * self.free_current))
+ # Torque constant
+ self.Kt = self.stall_torque / self.stall_current
+ # Control loop time step
+ self.dt = 0.005
+
+ # State feedback matrices
+ self.A_continuous = numpy.matrix(
+ [[-self.Kt / self.Kv / (self.J * self.R)]])
+ self.B_continuous = numpy.matrix(
+ [[self.Kt / (self.J * self.R)]])
+ self.C = numpy.matrix([[1]])
+ self.D = numpy.matrix([[0]])
+
+ self.A, self.B = self.ContinuousToDiscrete(self.A_continuous,
+ self.B_continuous, self.dt)
+
+ self.PlaceControllerPoles([0.01])
+ self.PlaceObserverPoles([0.01])
+
+ self.U_max = numpy.matrix([[12.0]])
+ self.U_min = numpy.matrix([[-12.0]])
+
+ self.InitializeState()
+
+
+class Drivetrain(control_loop.ControlLoop):
+ def __init__(self, name="Drivetrain", left_low=True, right_low=True):
+ super(Drivetrain, self).__init__(name)
+ # Stall Torque in N m
+ self.stall_torque = 2.42
+ # Stall Current in Amps
+ self.stall_current = 133.0
+ # Free Speed in RPM. Used number from last year.
+ self.free_speed = 4650.0
+ # Free Current in Amps
+ self.free_current = 2.7
+ # Moment of inertia of the drivetrain in kg m^2
+ # Just borrowed from last year.
+ self.J = 4.5
+ # Mass of the robot, in kg.
+ self.m = 68
+ # Radius of the robot, in meters (from last year).
+ self.rb = 0.617998644 / 2.0
+ # Radius of the wheels, in meters.
+ self.r = .04445
+ # Resistance of the motor, divided by the number of motors.
+ self.R = 12.0 / self.stall_current / 4
+ # Motor velocity constant
+ self.Kv = ((self.free_speed / 60.0 * 2.0 * numpy.pi) /
+ (12.0 - self.R * self.free_current))
+ # Torque constant
+ self.Kt = self.stall_torque / self.stall_current
+ # Gear ratios
+ self.G_low = 18.0 / 60.0 * 18.0 / 50.0
+ self.G_high = 28.0 / 50.0 * 18.0 / 50.0
+ if left_low:
+ self.Gl = self.G_low
+ else:
+ self.Gl = self.G_high
+ if right_low:
+ self.Gr = self.G_low
+ else:
+ self.Gr = self.G_high
+
+ # Control loop time step
+ self.dt = 0.005
+
+ # These describe the way that a given side of a robot will be influenced
+ # by the other side. Units of 1 / kg.
+ self.msp = 1.0 / self.m + self.rb * self.rb / self.J
+ self.msn = 1.0 / self.m - self.rb * self.rb / self.J
+ # The calculations which we will need for A and B.
+ self.tcl = -self.Kt / self.Kv / (self.Gl * self.Gl * self.R * self.r * self.r)
+ self.tcr = -self.Kt / self.Kv / (self.Gr * self.Gr * self.R * self.r * self.r)
+ self.mpl = self.Kt / (self.Gl * self.R * self.r)
+ self.mpr = self.Kt / (self.Gr * self.R * self.r)
+
+ # State feedback matrices
+ # X will be of the format
+ # [[positionl], [velocityl], [positionr], velocityr]]
+ self.A_continuous = numpy.matrix(
+ [[0, 1, 0, 0],
+ [0, self.msp * self.tcl, 0, self.msn * self.tcr],
+ [0, 0, 0, 1],
+ [0, self.msn * self.tcl, 0, self.msp * self.tcr]])
+ self.B_continuous = numpy.matrix(
+ [[0, 0],
+ [self.msp * self.mpl, self.msn * self.mpr],
+ [0, 0],
+ [self.msn * self.mpl, self.msp * self.mpr]])
+ self.C = numpy.matrix([[1, 0, 0, 0],
+ [0, 0, 1, 0]])
+ self.D = numpy.matrix([[0, 0],
+ [0, 0]])
+
+ #print "THE NUMBER I WANT" + str(numpy.linalg.inv(self.A_continuous) * -self.B_continuous * numpy.matrix([[12.0], [12.0]]))
+ self.A, self.B = self.ContinuousToDiscrete(
+ self.A_continuous, self.B_continuous, self.dt)
+
+ # Poles from last year.
+ self.hp = 0.65
+ self.lp = 0.83
+ self.PlaceControllerPoles([self.hp, self.lp, self.hp, self.lp])
+ print self.K
+ q_pos = 0.07
+ q_vel = 1.0
+ self.Q = numpy.matrix([[(1.0 / (q_pos ** 2.0)), 0.0, 0.0, 0.0],
+ [0.0, (1.0 / (q_vel ** 2.0)), 0.0, 0.0],
+ [0.0, 0.0, (1.0 / (q_pos ** 2.0)), 0.0],
+ [0.0, 0.0, 0.0, (1.0 / (q_vel ** 2.0))]])
+
+ self.R = numpy.matrix([[(1.0 / (12.0 ** 2.0)), 0.0],
+ [0.0, (1.0 / (12.0 ** 2.0))]])
+ self.K = controls.dlqr(self.A, self.B, self.Q, self.R)
+ print self.A
+ print self.B
+ print self.K
+ print numpy.linalg.eig(self.A - self.B * self.K)[0]
+
+ self.hlp = 0.3
+ self.llp = 0.4
+ self.PlaceObserverPoles([self.hlp, self.hlp, self.llp, self.llp])
+
+ self.U_max = numpy.matrix([[12.0], [12.0]])
+ self.U_min = numpy.matrix([[-12.0], [-12.0]])
+ self.InitializeState()
+
+def main(argv):
+ # Simulate the response of the system to a step input.
+ drivetrain = Drivetrain()
+ simulated_left = []
+ simulated_right = []
+ for _ in xrange(100):
+ drivetrain.Update(numpy.matrix([[12.0], [12.0]]))
+ simulated_left.append(drivetrain.X[0, 0])
+ simulated_right.append(drivetrain.X[2, 0])
+
+ #pylab.plot(range(100), simulated_left)
+ #pylab.plot(range(100), simulated_right)
+ #pylab.show()
+
+ # Simulate forwards motion.
+ drivetrain = Drivetrain()
+ close_loop_left = []
+ close_loop_right = []
+ R = numpy.matrix([[1.0], [0.0], [1.0], [0.0]])
+ for _ in xrange(100):
+ U = numpy.clip(drivetrain.K * (R - drivetrain.X_hat),
+ drivetrain.U_min, drivetrain.U_max)
+ drivetrain.UpdateObserver(U)
+ drivetrain.Update(U)
+ close_loop_left.append(drivetrain.X[0, 0])
+ close_loop_right.append(drivetrain.X[2, 0])
+
+ #pylab.plot(range(100), close_loop_left)
+ #pylab.plot(range(100), close_loop_right)
+ #pylab.show()
+
+ # Try turning in place
+ drivetrain = Drivetrain()
+ close_loop_left = []
+ close_loop_right = []
+ R = numpy.matrix([[-1.0], [0.0], [1.0], [0.0]])
+ for _ in xrange(100):
+ U = numpy.clip(drivetrain.K * (R - drivetrain.X_hat),
+ drivetrain.U_min, drivetrain.U_max)
+ drivetrain.UpdateObserver(U)
+ drivetrain.Update(U)
+ close_loop_left.append(drivetrain.X[0, 0])
+ close_loop_right.append(drivetrain.X[2, 0])
+
+ #pylab.plot(range(100), close_loop_left)
+ #pylab.plot(range(100), close_loop_right)
+ #pylab.show()
+
+ # Try turning just one side.
+ drivetrain = Drivetrain()
+ close_loop_left = []
+ close_loop_right = []
+ R = numpy.matrix([[0.0], [0.0], [1.0], [0.0]])
+ for _ in xrange(100):
+ U = numpy.clip(drivetrain.K * (R - drivetrain.X_hat),
+ drivetrain.U_min, drivetrain.U_max)
+ drivetrain.UpdateObserver(U)
+ drivetrain.Update(U)
+ close_loop_left.append(drivetrain.X[0, 0])
+ close_loop_right.append(drivetrain.X[2, 0])
+
+ #pylab.plot(range(100), close_loop_left)
+ #pylab.plot(range(100), close_loop_right)
+ #pylab.show()
+
+ # Write the generated constants out to a file.
+ print "Output one"
+ drivetrain_low_low = Drivetrain(name="DrivetrainLowLow", left_low=True, right_low=True)
+ drivetrain_low_high = Drivetrain(name="DrivetrainLowHigh", left_low=True, right_low=False)
+ drivetrain_high_low = Drivetrain(name="DrivetrainHighLow", left_low=False, right_low=True)
+ drivetrain_high_high = Drivetrain(name="DrivetrainHighHigh", left_low=False, right_low=False)
+
+ if len(argv) != 5:
+ print "Expected .h file name and .cc file name"
+ else:
+ dog_loop_writer = control_loop.ControlLoopWriter(
+ "Drivetrain", [drivetrain_low_low, drivetrain_low_high,
+ drivetrain_high_low, drivetrain_high_high])
+ if argv[1][-3:] == '.cc':
+ dog_loop_writer.Write(argv[2], argv[1])
+ else:
+ dog_loop_writer.Write(argv[1], argv[2])
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv))
diff --git a/y2014/control_loops/python/elevator.py b/y2014/control_loops/python/elevator.py
new file mode 100755
index 0000000..fba72c8
--- /dev/null
+++ b/y2014/control_loops/python/elevator.py
@@ -0,0 +1,246 @@
+#!/usr/bin/python
+
+import control_loop
+import controls
+import polytope
+import polydrivetrain
+import numpy
+import sys
+import matplotlib
+from matplotlib import pylab
+
+class Elevator(control_loop.ControlLoop):
+ def __init__(self, name="Elevator", mass=None):
+ super(Elevator, self).__init__(name)
+ # Stall Torque in N m
+ self.stall_torque = 0.476
+ # Stall Current in Amps
+ self.stall_current = 80.730
+ # Free Speed in RPM
+ self.free_speed = 13906.0
+ # Free Current in Amps
+ self.free_current = 5.820
+ # Mass of the elevator
+ if mass is None:
+ self.mass = 13.0
+ else:
+ self.mass = mass
+
+ # Resistance of the motor
+ self.R = 12.0 / self.stall_current
+ # Motor velocity constant
+ self.Kv = ((self.free_speed / 60.0 * 2.0 * numpy.pi) /
+ (12.0 - self.R * self.free_current))
+ # Torque constant
+ self.Kt = self.stall_torque / self.stall_current
+ # Gear ratio
+ self.G = (56.0 / 12.0) * (84.0 / 14.0)
+ # Pulley diameter
+ self.r = 32 * 0.005 / numpy.pi / 2.0
+ # Control loop time step
+ self.dt = 0.005
+
+ # Elevator left/right spring constant (N/m)
+ self.spring = 800.0
+
+ # State is [average position, average velocity,
+ # position difference/2, velocity difference/2]
+ # Input is [V_left, V_right]
+
+ C1 = self.spring / (self.mass * 0.5)
+ C2 = self.Kt * self.G / (self.mass * 0.5 * self.r * self.R)
+ C3 = self.G * self.G * self.Kt / (
+ self.R * self.r * self.r * self.mass * 0.5 * self.Kv)
+
+ self.A_continuous = numpy.matrix(
+ [[0, 1, 0, 0],
+ [0, -C3, 0, 0],
+ [0, 0, 0, 1],
+ [0, 0, -C1 * 2.0, -C3]])
+
+ print "Full speed is", C2 / C3 * 12.0
+
+ # Start with the unmodified input
+ self.B_continuous = numpy.matrix(
+ [[0, 0],
+ [C2 / 2.0, C2 / 2.0],
+ [0, 0],
+ [C2 / 2.0, -C2 / 2.0]])
+
+ self.C = numpy.matrix([[1, 0, 1, 0],
+ [1, 0, -1, 0]])
+ self.D = numpy.matrix([[0, 0],
+ [0, 0]])
+
+ self.A, self.B = self.ContinuousToDiscrete(
+ self.A_continuous, self.B_continuous, self.dt)
+
+ print self.A
+
+ controlability = controls.ctrb(self.A, self.B);
+ print "Rank of augmented controlability matrix.", numpy.linalg.matrix_rank(
+ controlability)
+
+ q_pos = 0.02
+ q_vel = 0.400
+ q_pos_diff = 0.01
+ q_vel_diff = 0.45
+ self.Q = numpy.matrix([[(1.0 / (q_pos ** 2.0)), 0.0, 0.0, 0.0],
+ [0.0, (1.0 / (q_vel ** 2.0)), 0.0, 0.0],
+ [0.0, 0.0, (1.0 / (q_pos_diff ** 2.0)), 0.0],
+ [0.0, 0.0, 0.0, (1.0 / (q_vel_diff ** 2.0))]])
+
+ self.R = numpy.matrix([[(1.0 / (12.0 ** 2.0)), 0.0],
+ [0.0, 1.0 / (12.0 ** 2.0)]])
+ self.K = controls.dlqr(self.A, self.B, self.Q, self.R)
+ print self.K
+
+ print numpy.linalg.eig(self.A - self.B * self.K)[0]
+
+ self.rpl = 0.20
+ self.ipl = 0.05
+ self.PlaceObserverPoles([self.rpl + 1j * self.ipl,
+ self.rpl + 1j * self.ipl,
+ self.rpl - 1j * self.ipl,
+ self.rpl - 1j * self.ipl])
+
+ # The box formed by U_min and U_max must encompass all possible values,
+ # or else Austin's code gets angry.
+ self.U_max = numpy.matrix([[12.0], [12.0]])
+ self.U_min = numpy.matrix([[-12.0], [-12.0]])
+
+ self.InitializeState()
+
+
+def CapU(U):
+ if U[0, 0] - U[1, 0] > 24:
+ return numpy.matrix([[12], [-12]])
+ elif U[0, 0] - U[1, 0] < -24:
+ return numpy.matrix([[-12], [12]])
+ else:
+ max_u = max(U[0, 0], U[1, 0])
+ min_u = min(U[0, 0], U[1, 0])
+ if max_u > 12:
+ return U - (max_u - 12)
+ if min_u < -12:
+ return U - (min_u + 12)
+ return U
+
+
+def run_test(elevator, initial_X, goal, max_separation_error=0.01,
+ show_graph=True, iterations=200, controller_elevator=None,
+ observer_elevator=None):
+ """Runs the elevator plant with an initial condition and goal.
+
+ The tests themselves are not terribly sophisticated; I just test for
+ whether the goal has been reached and whether the separation goes
+ outside of the initial and goal values by more than max_separation_error.
+ Prints out something for a failure of either condition and returns
+ False if tests fail.
+ Args:
+ elevator: elevator object to use.
+ initial_X: starting state.
+ goal: goal state.
+ show_graph: Whether or not to display a graph showing the changing
+ states and voltages.
+ iterations: Number of timesteps to run the model for.
+ controller_elevator: elevator object to get K from, or None if we should
+ use elevator.
+ observer_elevator: elevator object to use for the observer, or None if we
+ should use the actual state.
+ """
+
+ elevator.X = initial_X
+
+ if controller_elevator is None:
+ controller_elevator = elevator
+
+ if observer_elevator is not None:
+ observer_elevator.X_hat = initial_X + 0.01
+ observer_elevator.X_hat = initial_X
+
+ # Various lists for graphing things.
+ t = []
+ x_avg = []
+ x_sep = []
+ x_hat_avg = []
+ x_hat_sep = []
+ v_avg = []
+ v_sep = []
+ u_left = []
+ u_right = []
+
+ sep_plot_gain = 100.0
+
+ for i in xrange(iterations):
+ X_hat = elevator.X
+ if observer_elevator is not None:
+ X_hat = observer_elevator.X_hat
+ x_hat_avg.append(observer_elevator.X_hat[0, 0])
+ x_hat_sep.append(observer_elevator.X_hat[2, 0] * sep_plot_gain)
+ U = controller_elevator.K * (goal - X_hat)
+ U = CapU(U)
+ x_avg.append(elevator.X[0, 0])
+ v_avg.append(elevator.X[1, 0])
+ x_sep.append(elevator.X[2, 0] * sep_plot_gain)
+ v_sep.append(elevator.X[3, 0])
+ if observer_elevator is not None:
+ observer_elevator.PredictObserver(U)
+ elevator.Update(U)
+ if observer_elevator is not None:
+ observer_elevator.Y = elevator.Y
+ observer_elevator.CorrectObserver(U)
+
+ t.append(i * elevator.dt)
+ u_left.append(U[0, 0])
+ u_right.append(U[1, 0])
+
+ print numpy.linalg.inv(elevator.A)
+ print "delta time is ", elevator.dt
+ print "Velocity at t=0 is ", x_avg[0], v_avg[0], x_sep[0], v_sep[0]
+ print "Velocity at t=1+dt is ", x_avg[1], v_avg[1], x_sep[1], v_sep[1]
+
+ if show_graph:
+ pylab.subplot(2, 1, 1)
+ pylab.plot(t, x_avg, label='x avg')
+ pylab.plot(t, x_sep, label='x sep')
+ if observer_elevator is not None:
+ pylab.plot(t, x_hat_avg, label='x_hat avg')
+ pylab.plot(t, x_hat_sep, label='x_hat sep')
+ pylab.legend()
+
+ pylab.subplot(2, 1, 2)
+ pylab.plot(t, u_left, label='u left')
+ pylab.plot(t, u_right, label='u right')
+ pylab.legend()
+ pylab.show()
+
+
+def main(argv):
+ loaded_mass = 25
+ #loaded_mass = 0
+ elevator = Elevator(mass=13 + loaded_mass)
+ elevator_controller = Elevator(mass=13 + 15)
+ observer_elevator = Elevator(mass=13 + 15)
+ #observer_elevator = None
+
+ # Test moving the elevator with constant separation.
+ initial_X = numpy.matrix([[0.0], [0.0], [0.01], [0.0]])
+ #initial_X = numpy.matrix([[0.0], [0.0], [0.00], [0.0]])
+ R = numpy.matrix([[1.0], [0.0], [0.0], [0.0]])
+ run_test(elevator, initial_X, R, controller_elevator=elevator_controller,
+ observer_elevator=observer_elevator)
+
+ # Write the generated constants out to a file.
+ if len(argv) != 3:
+ print "Expected .h file name and .cc file name for the elevator."
+ else:
+ elevator = Elevator("Elevator")
+ loop_writer = control_loop.ControlLoopWriter("Elevator", [elevator])
+ if argv[1][-3:] == '.cc':
+ loop_writer.Write(argv[2], argv[1])
+ else:
+ loop_writer.Write(argv[1], argv[2])
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv))
diff --git a/y2014/control_loops/python/polydrivetrain.py b/y2014/control_loops/python/polydrivetrain.py
new file mode 100755
index 0000000..a2c63fa
--- /dev/null
+++ b/y2014/control_loops/python/polydrivetrain.py
@@ -0,0 +1,503 @@
+#!/usr/bin/python
+
+import numpy
+import sys
+import polytope
+import drivetrain
+import control_loop
+import controls
+from matplotlib import pylab
+
+__author__ = 'Austin Schuh (austin.linux@gmail.com)'
+
+
+def CoerceGoal(region, K, w, R):
+ """Intersects a line with a region, and finds the closest point to R.
+
+ Finds a point that is closest to R inside the region, and on the line
+ defined by K X = w. If it is not possible to find a point on the line,
+ finds a point that is inside the region and closest to the line. This
+ function assumes that
+
+ Args:
+ region: HPolytope, the valid goal region.
+ K: numpy.matrix (2 x 1), the matrix for the equation [K1, K2] [x1; x2] = w
+ w: float, the offset in the equation above.
+ R: numpy.matrix (2 x 1), the point to be closest to.
+
+ Returns:
+ numpy.matrix (2 x 1), the point.
+ """
+ return DoCoerceGoal(region, K, w, R)[0]
+
+def DoCoerceGoal(region, K, w, R):
+ if region.IsInside(R):
+ return (R, True)
+
+ perpendicular_vector = K.T / numpy.linalg.norm(K)
+ parallel_vector = numpy.matrix([[perpendicular_vector[1, 0]],
+ [-perpendicular_vector[0, 0]]])
+
+ # We want to impose the constraint K * X = w on the polytope H * X <= k.
+ # We do this by breaking X up into parallel and perpendicular components to
+ # the half plane. This gives us the following equation.
+ #
+ # parallel * (parallel.T \dot X) + perpendicular * (perpendicular \dot X)) = X
+ #
+ # Then, substitute this into the polytope.
+ #
+ # H * (parallel * (parallel.T \dot X) + perpendicular * (perpendicular \dot X)) <= k
+ #
+ # Substitute K * X = w
+ #
+ # H * parallel * (parallel.T \dot X) + H * perpendicular * w <= k
+ #
+ # Move all the knowns to the right side.
+ #
+ # H * parallel * ([parallel1 parallel2] * X) <= k - H * perpendicular * w
+ #
+ # Let t = parallel.T \dot X, the component parallel to the surface.
+ #
+ # H * parallel * t <= k - H * perpendicular * w
+ #
+ # This is a polytope which we can solve, and use to figure out the range of X
+ # that we care about!
+
+ t_poly = polytope.HPolytope(
+ region.H * parallel_vector,
+ region.k - region.H * perpendicular_vector * w)
+
+ vertices = t_poly.Vertices()
+
+ if vertices.shape[0]:
+ # The region exists!
+ # Find the closest vertex
+ min_distance = numpy.infty
+ closest_point = None
+ for vertex in vertices:
+ point = parallel_vector * vertex + perpendicular_vector * w
+ length = numpy.linalg.norm(R - point)
+ if length < min_distance:
+ min_distance = length
+ closest_point = point
+
+ return (closest_point, True)
+ else:
+ # Find the vertex of the space that is closest to the line.
+ region_vertices = region.Vertices()
+ min_distance = numpy.infty
+ closest_point = None
+ for vertex in region_vertices:
+ point = vertex.T
+ length = numpy.abs((perpendicular_vector.T * point)[0, 0])
+ if length < min_distance:
+ min_distance = length
+ closest_point = point
+
+ return (closest_point, False)
+
+
+class VelocityDrivetrainModel(control_loop.ControlLoop):
+ def __init__(self, left_low=True, right_low=True, name="VelocityDrivetrainModel"):
+ super(VelocityDrivetrainModel, self).__init__(name)
+ self._drivetrain = drivetrain.Drivetrain(left_low=left_low,
+ right_low=right_low)
+ self.dt = 0.01
+ self.A_continuous = numpy.matrix(
+ [[self._drivetrain.A_continuous[1, 1], self._drivetrain.A_continuous[1, 3]],
+ [self._drivetrain.A_continuous[3, 1], self._drivetrain.A_continuous[3, 3]]])
+
+ self.B_continuous = numpy.matrix(
+ [[self._drivetrain.B_continuous[1, 0], self._drivetrain.B_continuous[1, 1]],
+ [self._drivetrain.B_continuous[3, 0], self._drivetrain.B_continuous[3, 1]]])
+ self.C = numpy.matrix(numpy.eye(2));
+ self.D = numpy.matrix(numpy.zeros((2, 2)));
+
+ self.A, self.B = self.ContinuousToDiscrete(self.A_continuous,
+ self.B_continuous, self.dt)
+
+ # FF * X = U (steady state)
+ self.FF = self.B.I * (numpy.eye(2) - self.A)
+
+ self.PlaceControllerPoles([0.6, 0.6])
+ self.PlaceObserverPoles([0.02, 0.02])
+
+ self.G_high = self._drivetrain.G_high
+ self.G_low = self._drivetrain.G_low
+ self.R = self._drivetrain.R
+ self.r = self._drivetrain.r
+ self.Kv = self._drivetrain.Kv
+ self.Kt = self._drivetrain.Kt
+
+ self.U_max = self._drivetrain.U_max
+ self.U_min = self._drivetrain.U_min
+
+
+class VelocityDrivetrain(object):
+ HIGH = 'high'
+ LOW = 'low'
+ SHIFTING_UP = 'up'
+ SHIFTING_DOWN = 'down'
+
+ def __init__(self):
+ self.drivetrain_low_low = VelocityDrivetrainModel(
+ left_low=True, right_low=True, name='VelocityDrivetrainLowLow')
+ self.drivetrain_low_high = VelocityDrivetrainModel(left_low=True, right_low=False, name='VelocityDrivetrainLowHigh')
+ self.drivetrain_high_low = VelocityDrivetrainModel(left_low=False, right_low=True, name = 'VelocityDrivetrainHighLow')
+ self.drivetrain_high_high = VelocityDrivetrainModel(left_low=False, right_low=False, name = 'VelocityDrivetrainHighHigh')
+
+ # X is [lvel, rvel]
+ self.X = numpy.matrix(
+ [[0.0],
+ [0.0]])
+
+ self.U_poly = polytope.HPolytope(
+ numpy.matrix([[1, 0],
+ [-1, 0],
+ [0, 1],
+ [0, -1]]),
+ numpy.matrix([[12],
+ [12],
+ [12],
+ [12]]))
+
+ self.U_max = numpy.matrix(
+ [[12.0],
+ [12.0]])
+ self.U_min = numpy.matrix(
+ [[-12.0000000000],
+ [-12.0000000000]])
+
+ self.dt = 0.01
+
+ self.R = numpy.matrix(
+ [[0.0],
+ [0.0]])
+
+ # ttrust is the comprimise between having full throttle negative inertia,
+ # and having no throttle negative inertia. A value of 0 is full throttle
+ # inertia. A value of 1 is no throttle negative inertia.
+ self.ttrust = 1.0
+
+ self.left_gear = VelocityDrivetrain.LOW
+ self.right_gear = VelocityDrivetrain.LOW
+ self.left_shifter_position = 0.0
+ self.right_shifter_position = 0.0
+ self.left_cim = drivetrain.CIM()
+ self.right_cim = drivetrain.CIM()
+
+ def IsInGear(self, gear):
+ return gear is VelocityDrivetrain.HIGH or gear is VelocityDrivetrain.LOW
+
+ def MotorRPM(self, shifter_position, velocity):
+ if shifter_position > 0.5:
+ return (velocity / self.CurrentDrivetrain().G_high /
+ self.CurrentDrivetrain().r)
+ else:
+ return (velocity / self.CurrentDrivetrain().G_low /
+ self.CurrentDrivetrain().r)
+
+ def CurrentDrivetrain(self):
+ if self.left_shifter_position > 0.5:
+ if self.right_shifter_position > 0.5:
+ return self.drivetrain_high_high
+ else:
+ return self.drivetrain_high_low
+ else:
+ if self.right_shifter_position > 0.5:
+ return self.drivetrain_low_high
+ else:
+ return self.drivetrain_low_low
+
+ def SimShifter(self, gear, shifter_position):
+ if gear is VelocityDrivetrain.HIGH or gear is VelocityDrivetrain.SHIFTING_UP:
+ shifter_position = min(shifter_position + 0.5, 1.0)
+ else:
+ shifter_position = max(shifter_position - 0.5, 0.0)
+
+ if shifter_position == 1.0:
+ gear = VelocityDrivetrain.HIGH
+ elif shifter_position == 0.0:
+ gear = VelocityDrivetrain.LOW
+
+ return gear, shifter_position
+
+ def ComputeGear(self, wheel_velocity, should_print=False, current_gear=False, gear_name=None):
+ high_omega = (wheel_velocity / self.CurrentDrivetrain().G_high /
+ self.CurrentDrivetrain().r)
+ low_omega = (wheel_velocity / self.CurrentDrivetrain().G_low /
+ self.CurrentDrivetrain().r)
+ #print gear_name, "Motor Energy Difference.", 0.5 * 0.000140032647 * (low_omega * low_omega - high_omega * high_omega), "joules"
+ high_torque = ((12.0 - high_omega / self.CurrentDrivetrain().Kv) *
+ self.CurrentDrivetrain().Kt / self.CurrentDrivetrain().R)
+ low_torque = ((12.0 - low_omega / self.CurrentDrivetrain().Kv) *
+ self.CurrentDrivetrain().Kt / self.CurrentDrivetrain().R)
+ high_power = high_torque * high_omega
+ low_power = low_torque * low_omega
+ #if should_print:
+ # print gear_name, "High omega", high_omega, "Low omega", low_omega
+ # print gear_name, "High torque", high_torque, "Low torque", low_torque
+ # print gear_name, "High power", high_power, "Low power", low_power
+
+ # Shift algorithm improvements.
+ # TODO(aschuh):
+ # It takes time to shift. Shifting down for 1 cycle doesn't make sense
+ # because you will end up slower than without shifting. Figure out how
+ # to include that info.
+ # If the driver is still in high gear, but isn't asking for the extra power
+ # from low gear, don't shift until he asks for it.
+ goal_gear_is_high = high_power > low_power
+ #goal_gear_is_high = True
+
+ if not self.IsInGear(current_gear):
+ print gear_name, 'Not in gear.'
+ return current_gear
+ else:
+ is_high = current_gear is VelocityDrivetrain.HIGH
+ if is_high != goal_gear_is_high:
+ if goal_gear_is_high:
+ print gear_name, 'Shifting up.'
+ return VelocityDrivetrain.SHIFTING_UP
+ else:
+ print gear_name, 'Shifting down.'
+ return VelocityDrivetrain.SHIFTING_DOWN
+ else:
+ return current_gear
+
+ def FilterVelocity(self, throttle):
+ # Invert the plant to figure out how the velocity filter would have to work
+ # out in order to filter out the forwards negative inertia.
+ # This math assumes that the left and right power and velocity are equal.
+
+ # The throttle filter should filter such that the motor in the highest gear
+ # should be controlling the time constant.
+ # Do this by finding the index of FF that has the lowest value, and computing
+ # the sums using that index.
+ FF_sum = self.CurrentDrivetrain().FF.sum(axis=1)
+ min_FF_sum_index = numpy.argmin(FF_sum)
+ min_FF_sum = FF_sum[min_FF_sum_index, 0]
+ min_K_sum = self.CurrentDrivetrain().K[min_FF_sum_index, :].sum()
+ # Compute the FF sum for high gear.
+ high_min_FF_sum = self.drivetrain_high_high.FF[0, :].sum()
+
+ # U = self.K[0, :].sum() * (R - x_avg) + self.FF[0, :].sum() * R
+ # throttle * 12.0 = (self.K[0, :].sum() + self.FF[0, :].sum()) * R
+ # - self.K[0, :].sum() * x_avg
+
+ # R = (throttle * 12.0 + self.K[0, :].sum() * x_avg) /
+ # (self.K[0, :].sum() + self.FF[0, :].sum())
+
+ # U = (K + FF) * R - K * X
+ # (K + FF) ^-1 * (U + K * X) = R
+
+ # Scale throttle by min_FF_sum / high_min_FF_sum. This will make low gear
+ # have the same velocity goal as high gear, and so that the robot will hold
+ # the same speed for the same throttle for all gears.
+ adjusted_ff_voltage = numpy.clip(throttle * 12.0 * min_FF_sum / high_min_FF_sum, -12.0, 12.0)
+ return ((adjusted_ff_voltage + self.ttrust * min_K_sum * (self.X[0, 0] + self.X[1, 0]) / 2.0)
+ / (self.ttrust * min_K_sum + min_FF_sum))
+
+ def Update(self, throttle, steering):
+ # Shift into the gear which sends the most power to the floor.
+ # This is the same as sending the most torque down to the floor at the
+ # wheel.
+
+ self.left_gear = self.right_gear = True
+ if True:
+ self.left_gear = self.ComputeGear(self.X[0, 0], should_print=True,
+ current_gear=self.left_gear,
+ gear_name="left")
+ self.right_gear = self.ComputeGear(self.X[1, 0], should_print=True,
+ current_gear=self.right_gear,
+ gear_name="right")
+ if self.IsInGear(self.left_gear):
+ self.left_cim.X[0, 0] = self.MotorRPM(self.left_shifter_position, self.X[0, 0])
+
+ if self.IsInGear(self.right_gear):
+ self.right_cim.X[0, 0] = self.MotorRPM(self.right_shifter_position, self.X[0, 0])
+
+ if self.IsInGear(self.left_gear) and self.IsInGear(self.right_gear):
+ # Filter the throttle to provide a nicer response.
+ fvel = self.FilterVelocity(throttle)
+
+ # Constant radius means that angualar_velocity / linear_velocity = constant.
+ # Compute the left and right velocities.
+ steering_velocity = numpy.abs(fvel) * steering
+ left_velocity = fvel - steering_velocity
+ right_velocity = fvel + steering_velocity
+
+ # Write this constraint in the form of K * R = w
+ # angular velocity / linear velocity = constant
+ # (left - right) / (left + right) = constant
+ # left - right = constant * left + constant * right
+
+ # (fvel - steering * numpy.abs(fvel) - fvel - steering * numpy.abs(fvel)) /
+ # (fvel - steering * numpy.abs(fvel) + fvel + steering * numpy.abs(fvel)) =
+ # constant
+ # (- 2 * steering * numpy.abs(fvel)) / (2 * fvel) = constant
+ # (-steering * sign(fvel)) = constant
+ # (-steering * sign(fvel)) * (left + right) = left - right
+ # (steering * sign(fvel) + 1) * left + (steering * sign(fvel) - 1) * right = 0
+
+ equality_k = numpy.matrix(
+ [[1 + steering * numpy.sign(fvel), -(1 - steering * numpy.sign(fvel))]])
+ equality_w = 0.0
+
+ self.R[0, 0] = left_velocity
+ self.R[1, 0] = right_velocity
+
+ # Construct a constraint on R by manipulating the constraint on U
+ # Start out with H * U <= k
+ # U = FF * R + K * (R - X)
+ # H * (FF * R + K * R - K * X) <= k
+ # H * (FF + K) * R <= k + H * K * X
+ R_poly = polytope.HPolytope(
+ self.U_poly.H * (self.CurrentDrivetrain().K + self.CurrentDrivetrain().FF),
+ self.U_poly.k + self.U_poly.H * self.CurrentDrivetrain().K * self.X)
+
+ # Limit R back inside the box.
+ self.boxed_R = CoerceGoal(R_poly, equality_k, equality_w, self.R)
+
+ FF_volts = self.CurrentDrivetrain().FF * self.boxed_R
+ self.U_ideal = self.CurrentDrivetrain().K * (self.boxed_R - self.X) + FF_volts
+ else:
+ print 'Not all in gear'
+ if not self.IsInGear(self.left_gear) and not self.IsInGear(self.right_gear):
+ # TODO(austin): Use battery volts here.
+ R_left = self.MotorRPM(self.left_shifter_position, self.X[0, 0])
+ self.U_ideal[0, 0] = numpy.clip(
+ self.left_cim.K * (R_left - self.left_cim.X) + R_left / self.left_cim.Kv,
+ self.left_cim.U_min, self.left_cim.U_max)
+ self.left_cim.Update(self.U_ideal[0, 0])
+
+ R_right = self.MotorRPM(self.right_shifter_position, self.X[1, 0])
+ self.U_ideal[1, 0] = numpy.clip(
+ self.right_cim.K * (R_right - self.right_cim.X) + R_right / self.right_cim.Kv,
+ self.right_cim.U_min, self.right_cim.U_max)
+ self.right_cim.Update(self.U_ideal[1, 0])
+ else:
+ assert False
+
+ self.U = numpy.clip(self.U_ideal, self.U_min, self.U_max)
+
+ # TODO(austin): Model the robot as not accelerating when you shift...
+ # This hack only works when you shift at the same time.
+ if self.IsInGear(self.left_gear) and self.IsInGear(self.right_gear):
+ self.X = self.CurrentDrivetrain().A * self.X + self.CurrentDrivetrain().B * self.U
+
+ self.left_gear, self.left_shifter_position = self.SimShifter(
+ self.left_gear, self.left_shifter_position)
+ self.right_gear, self.right_shifter_position = self.SimShifter(
+ self.right_gear, self.right_shifter_position)
+
+ print "U is", self.U[0, 0], self.U[1, 0]
+ print "Left shifter", self.left_gear, self.left_shifter_position, "Right shifter", self.right_gear, self.right_shifter_position
+
+
+def main(argv):
+ vdrivetrain = VelocityDrivetrain()
+
+ if len(argv) != 7:
+ print "Expected .h file name and .cc file name"
+ else:
+ dog_loop_writer = control_loop.ControlLoopWriter(
+ "VelocityDrivetrain", [vdrivetrain.drivetrain_low_low,
+ vdrivetrain.drivetrain_low_high,
+ vdrivetrain.drivetrain_high_low,
+ vdrivetrain.drivetrain_high_high])
+
+ if argv[1][-3:] == '.cc':
+ dog_loop_writer.Write(argv[2], argv[1])
+ else:
+ dog_loop_writer.Write(argv[1], argv[2])
+
+ cim_writer = control_loop.ControlLoopWriter(
+ "CIM", [drivetrain.CIM()])
+
+ if argv[5][-3:] == '.cc':
+ cim_writer.Write(argv[6], argv[5])
+ else:
+ cim_writer.Write(argv[5], argv[6])
+ return
+
+ vl_plot = []
+ vr_plot = []
+ ul_plot = []
+ ur_plot = []
+ radius_plot = []
+ t_plot = []
+ left_gear_plot = []
+ right_gear_plot = []
+ vdrivetrain.left_shifter_position = 0.0
+ vdrivetrain.right_shifter_position = 0.0
+ vdrivetrain.left_gear = VelocityDrivetrain.LOW
+ vdrivetrain.right_gear = VelocityDrivetrain.LOW
+
+ print "K is", vdrivetrain.CurrentDrivetrain().K
+
+ if vdrivetrain.left_gear is VelocityDrivetrain.HIGH:
+ print "Left is high"
+ else:
+ print "Left is low"
+ if vdrivetrain.right_gear is VelocityDrivetrain.HIGH:
+ print "Right is high"
+ else:
+ print "Right is low"
+
+ for t in numpy.arange(0, 1.7, vdrivetrain.dt):
+ if t < 0.5:
+ vdrivetrain.Update(throttle=0.00, steering=1.0)
+ elif t < 1.2:
+ vdrivetrain.Update(throttle=0.5, steering=1.0)
+ else:
+ vdrivetrain.Update(throttle=0.00, steering=1.0)
+ t_plot.append(t)
+ vl_plot.append(vdrivetrain.X[0, 0])
+ vr_plot.append(vdrivetrain.X[1, 0])
+ ul_plot.append(vdrivetrain.U[0, 0])
+ ur_plot.append(vdrivetrain.U[1, 0])
+ left_gear_plot.append((vdrivetrain.left_gear is VelocityDrivetrain.HIGH) * 2.0 - 10.0)
+ right_gear_plot.append((vdrivetrain.right_gear is VelocityDrivetrain.HIGH) * 2.0 - 10.0)
+
+ fwd_velocity = (vdrivetrain.X[1, 0] + vdrivetrain.X[0, 0]) / 2
+ turn_velocity = (vdrivetrain.X[1, 0] - vdrivetrain.X[0, 0])
+ if abs(fwd_velocity) < 0.0000001:
+ radius_plot.append(turn_velocity)
+ else:
+ radius_plot.append(turn_velocity / fwd_velocity)
+
+ cim_velocity_plot = []
+ cim_voltage_plot = []
+ cim_time = []
+ cim = drivetrain.CIM()
+ R = numpy.matrix([[300]])
+ for t in numpy.arange(0, 0.5, cim.dt):
+ U = numpy.clip(cim.K * (R - cim.X) + R / cim.Kv, cim.U_min, cim.U_max)
+ cim.Update(U)
+ cim_velocity_plot.append(cim.X[0, 0])
+ cim_voltage_plot.append(U[0, 0] * 10)
+ cim_time.append(t)
+ pylab.plot(cim_time, cim_velocity_plot, label='cim spinup')
+ pylab.plot(cim_time, cim_voltage_plot, label='cim voltage')
+ pylab.legend()
+ pylab.show()
+
+ # TODO(austin):
+ # Shifting compensation.
+
+ # Tighten the turn.
+ # Closed loop drive.
+
+ pylab.plot(t_plot, vl_plot, label='left velocity')
+ pylab.plot(t_plot, vr_plot, label='right velocity')
+ pylab.plot(t_plot, ul_plot, label='left voltage')
+ pylab.plot(t_plot, ur_plot, label='right voltage')
+ pylab.plot(t_plot, radius_plot, label='radius')
+ pylab.plot(t_plot, left_gear_plot, label='left gear high')
+ pylab.plot(t_plot, right_gear_plot, label='right gear high')
+ pylab.legend()
+ pylab.show()
+ return 0
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv))
diff --git a/y2014/control_loops/python/polydrivetrain_test.py b/y2014/control_loops/python/polydrivetrain_test.py
new file mode 100755
index 0000000..434cdca
--- /dev/null
+++ b/y2014/control_loops/python/polydrivetrain_test.py
@@ -0,0 +1,82 @@
+#!/usr/bin/python
+
+import polydrivetrain
+import numpy
+from numpy.testing import *
+import polytope
+import unittest
+
+__author__ = 'Austin Schuh (austin.linux@gmail.com)'
+
+
+class TestVelocityDrivetrain(unittest.TestCase):
+ def MakeBox(self, x1_min, x1_max, x2_min, x2_max):
+ H = numpy.matrix([[1, 0],
+ [-1, 0],
+ [0, 1],
+ [0, -1]])
+ K = numpy.matrix([[x1_max],
+ [-x1_min],
+ [x2_max],
+ [-x2_min]])
+ return polytope.HPolytope(H, K)
+
+ def test_coerce_inside(self):
+ """Tests coercion when the point is inside the box."""
+ box = self.MakeBox(1, 2, 1, 2)
+
+ # x1 = x2
+ K = numpy.matrix([[1, -1]])
+ w = 0
+
+ assert_array_equal(polydrivetrain.CoerceGoal(box, K, w,
+ numpy.matrix([[1.5], [1.5]])),
+ numpy.matrix([[1.5], [1.5]]))
+
+ def test_coerce_outside_intersect(self):
+ """Tests coercion when the line intersects the box."""
+ box = self.MakeBox(1, 2, 1, 2)
+
+ # x1 = x2
+ K = numpy.matrix([[1, -1]])
+ w = 0
+
+ assert_array_equal(polydrivetrain.CoerceGoal(box, K, w, numpy.matrix([[5], [5]])),
+ numpy.matrix([[2.0], [2.0]]))
+
+ def test_coerce_outside_no_intersect(self):
+ """Tests coercion when the line does not intersect the box."""
+ box = self.MakeBox(3, 4, 1, 2)
+
+ # x1 = x2
+ K = numpy.matrix([[1, -1]])
+ w = 0
+
+ assert_array_equal(polydrivetrain.CoerceGoal(box, K, w, numpy.matrix([[5], [5]])),
+ numpy.matrix([[3.0], [2.0]]))
+
+ def test_coerce_middle_of_edge(self):
+ """Tests coercion when the line intersects the middle of an edge."""
+ box = self.MakeBox(0, 4, 1, 2)
+
+ # x1 = x2
+ K = numpy.matrix([[-1, 1]])
+ w = 0
+
+ assert_array_equal(polydrivetrain.CoerceGoal(box, K, w, numpy.matrix([[5], [5]])),
+ numpy.matrix([[2.0], [2.0]]))
+
+ def test_coerce_perpendicular_line(self):
+ """Tests coercion when the line does not intersect and is in quadrant 2."""
+ box = self.MakeBox(1, 2, 1, 2)
+
+ # x1 = -x2
+ K = numpy.matrix([[1, 1]])
+ w = 0
+
+ assert_array_equal(polydrivetrain.CoerceGoal(box, K, w, numpy.matrix([[5], [5]])),
+ numpy.matrix([[1.0], [1.0]]))
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/y2014/control_loops/python/shooter.py b/y2014/control_loops/python/shooter.py
new file mode 100755
index 0000000..69f2599
--- /dev/null
+++ b/y2014/control_loops/python/shooter.py
@@ -0,0 +1,254 @@
+#!/usr/bin/python
+
+import control_loop
+import numpy
+import sys
+from matplotlib import pylab
+
+class SprungShooter(control_loop.ControlLoop):
+ def __init__(self, name="RawSprungShooter"):
+ super(SprungShooter, self).__init__(name)
+ # Stall Torque in N m
+ self.stall_torque = .4982
+ # Stall Current in Amps
+ self.stall_current = 85
+ # Free Speed in RPM
+ self.free_speed = 19300.0
+ # Free Current in Amps
+ self.free_current = 1.2
+ # Effective mass of the shooter in kg.
+ # This rough estimate should about include the effect of the masses
+ # of the gears. If this number is too low, the eigen values of self.A
+ # will start to become extremely small.
+ self.J = 200
+ # Resistance of the motor, divided by the number of motors.
+ self.R = 12.0 / self.stall_current / 2.0
+ # Motor velocity constant
+ self.Kv = ((self.free_speed / 60.0 * 2.0 * numpy.pi) /
+ (12.0 - self.R * self.free_current))
+ # Torque constant
+ self.Kt = self.stall_torque / self.stall_current
+ # Spring constant for the springs, N/m
+ self.Ks = 2800.0
+ # Maximum extension distance (Distance from the 0 force point on the
+ # spring to the latch position.)
+ self.max_extension = 0.32385
+ # Gear ratio multiplied by radius of final sprocket.
+ self.G = 10.0 / 40.0 * 20.0 / 54.0 * 24.0 / 54.0 * 20.0 / 84.0 * 16.0 * (3.0 / 8.0) / (2.0 * numpy.pi) * 0.0254
+
+ # Control loop time step
+ self.dt = 0.01
+
+ # State feedback matrices
+ self.A_continuous = numpy.matrix(
+ [[0, 1],
+ [-self.Ks / self.J,
+ -self.Kt / self.Kv / (self.J * self.G * self.G * self.R)]])
+ self.B_continuous = numpy.matrix(
+ [[0],
+ [self.Kt / (self.J * self.G * self.R)]])
+ self.C = numpy.matrix([[1, 0]])
+ self.D = numpy.matrix([[0]])
+
+ self.A, self.B = self.ContinuousToDiscrete(
+ self.A_continuous, self.B_continuous, self.dt)
+
+ self.PlaceControllerPoles([0.45, 0.45])
+
+ self.rpl = .05
+ self.ipl = 0.008
+ self.PlaceObserverPoles([self.rpl,
+ self.rpl])
+
+ self.U_max = numpy.matrix([[12.0]])
+ self.U_min = numpy.matrix([[-12.0]])
+
+ self.InitializeState()
+
+
+class Shooter(SprungShooter):
+ def __init__(self, name="RawShooter"):
+ super(Shooter, self).__init__(name)
+
+ # State feedback matrices
+ self.A_continuous = numpy.matrix(
+ [[0, 1],
+ [0, -self.Kt / self.Kv / (self.J * self.G * self.G * self.R)]])
+ self.B_continuous = numpy.matrix(
+ [[0],
+ [self.Kt / (self.J * self.G * self.R)]])
+
+ self.A, self.B = self.ContinuousToDiscrete(
+ self.A_continuous, self.B_continuous, self.dt)
+
+ self.PlaceControllerPoles([0.45, 0.45])
+
+ self.rpl = .05
+ self.ipl = 0.008
+ self.PlaceObserverPoles([self.rpl,
+ self.rpl])
+
+ self.U_max = numpy.matrix([[12.0]])
+ self.U_min = numpy.matrix([[-12.0]])
+
+ self.InitializeState()
+
+
+class SprungShooterDeltaU(SprungShooter):
+ def __init__(self, name="SprungShooter"):
+ super(SprungShooterDeltaU, self).__init__(name)
+ A_unaugmented = self.A
+ B_unaugmented = self.B
+
+ self.A = numpy.matrix([[0.0, 0.0, 0.0],
+ [0.0, 0.0, 0.0],
+ [0.0, 0.0, 1.0]])
+ self.A[0:2, 0:2] = A_unaugmented
+ self.A[0:2, 2] = B_unaugmented
+
+ self.B = numpy.matrix([[0.0],
+ [0.0],
+ [1.0]])
+
+ self.C = numpy.matrix([[1.0, 0.0, 0.0]])
+ self.D = numpy.matrix([[0.0]])
+
+ self.PlaceControllerPoles([0.50, 0.35, 0.80])
+
+ print "K"
+ print self.K
+ print "Placed controller poles are"
+ print numpy.linalg.eig(self.A - self.B * self.K)[0]
+
+ self.rpl = .05
+ self.ipl = 0.008
+ self.PlaceObserverPoles([self.rpl + 1j * self.ipl,
+ self.rpl - 1j * self.ipl, 0.90])
+ print "Placed observer poles are"
+ print numpy.linalg.eig(self.A - self.L * self.C)[0]
+
+ self.U_max = numpy.matrix([[12.0]])
+ self.U_min = numpy.matrix([[-12.0]])
+
+ self.InitializeState()
+
+
+class ShooterDeltaU(Shooter):
+ def __init__(self, name="Shooter"):
+ super(ShooterDeltaU, self).__init__(name)
+ A_unaugmented = self.A
+ B_unaugmented = self.B
+
+ self.A = numpy.matrix([[0.0, 0.0, 0.0],
+ [0.0, 0.0, 0.0],
+ [0.0, 0.0, 1.0]])
+ self.A[0:2, 0:2] = A_unaugmented
+ self.A[0:2, 2] = B_unaugmented
+
+ self.B = numpy.matrix([[0.0],
+ [0.0],
+ [1.0]])
+
+ self.C = numpy.matrix([[1.0, 0.0, 0.0]])
+ self.D = numpy.matrix([[0.0]])
+
+ self.PlaceControllerPoles([0.55, 0.45, 0.80])
+
+ print "K"
+ print self.K
+ print "Placed controller poles are"
+ print numpy.linalg.eig(self.A - self.B * self.K)[0]
+
+ self.rpl = .05
+ self.ipl = 0.008
+ self.PlaceObserverPoles([self.rpl + 1j * self.ipl,
+ self.rpl - 1j * self.ipl, 0.90])
+ print "Placed observer poles are"
+ print numpy.linalg.eig(self.A - self.L * self.C)[0]
+
+ self.U_max = numpy.matrix([[12.0]])
+ self.U_min = numpy.matrix([[-12.0]])
+
+ self.InitializeState()
+
+
+def ClipDeltaU(shooter, old_voltage, delta_u):
+ old_u = old_voltage
+ new_u = numpy.clip(old_u + delta_u, shooter.U_min, shooter.U_max)
+ return new_u - old_u
+
+def main(argv):
+ # Simulate the response of the system to a goal.
+ sprung_shooter = SprungShooterDeltaU()
+ raw_sprung_shooter = SprungShooter()
+ close_loop_x = []
+ close_loop_u = []
+ goal_position = -0.3
+ R = numpy.matrix([[goal_position], [0.0], [-sprung_shooter.A[1, 0] / sprung_shooter.A[1, 2] * goal_position]])
+ voltage = numpy.matrix([[0.0]])
+ for _ in xrange(500):
+ U = sprung_shooter.K * (R - sprung_shooter.X_hat)
+ U = ClipDeltaU(sprung_shooter, voltage, U)
+ sprung_shooter.Y = raw_sprung_shooter.Y + 0.01
+ sprung_shooter.UpdateObserver(U)
+ voltage += U;
+ raw_sprung_shooter.Update(voltage)
+ close_loop_x.append(raw_sprung_shooter.X[0, 0] * 10)
+ close_loop_u.append(voltage[0, 0])
+
+ pylab.plot(range(500), close_loop_x)
+ pylab.plot(range(500), close_loop_u)
+ pylab.show()
+
+ shooter = ShooterDeltaU()
+ raw_shooter = Shooter()
+ close_loop_x = []
+ close_loop_u = []
+ goal_position = -0.3
+ R = numpy.matrix([[goal_position], [0.0], [-shooter.A[1, 0] / shooter.A[1, 2] * goal_position]])
+ voltage = numpy.matrix([[0.0]])
+ for _ in xrange(500):
+ U = shooter.K * (R - shooter.X_hat)
+ U = ClipDeltaU(shooter, voltage, U)
+ shooter.Y = raw_shooter.Y + 0.01
+ shooter.UpdateObserver(U)
+ voltage += U;
+ raw_shooter.Update(voltage)
+ close_loop_x.append(raw_shooter.X[0, 0] * 10)
+ close_loop_u.append(voltage[0, 0])
+
+ pylab.plot(range(500), close_loop_x)
+ pylab.plot(range(500), close_loop_u)
+ pylab.show()
+
+ # Write the generated constants out to a file.
+ if len(argv) != 5:
+ print "Expected .h file name and .cc file name for"
+ print "both the plant and unaugmented plant"
+ else:
+ unaug_sprung_shooter = SprungShooter("RawSprungShooter")
+ unaug_shooter = Shooter("RawShooter")
+ unaug_loop_writer = control_loop.ControlLoopWriter("RawShooter",
+ [unaug_sprung_shooter,
+ unaug_shooter])
+ if argv[3][-3:] == '.cc':
+ unaug_loop_writer.Write(argv[4], argv[3])
+ else:
+ unaug_loop_writer.Write(argv[3], argv[4])
+
+ sprung_shooter = SprungShooterDeltaU()
+ shooter = ShooterDeltaU()
+ loop_writer = control_loop.ControlLoopWriter("Shooter", [sprung_shooter,
+ shooter])
+
+ loop_writer.AddConstant(control_loop.Constant("kMaxExtension", "%f",
+ sprung_shooter.max_extension))
+ loop_writer.AddConstant(control_loop.Constant("kSpringConstant", "%f",
+ sprung_shooter.Ks))
+ if argv[1][-3:] == '.cc':
+ loop_writer.Write(argv[2], argv[1])
+ else:
+ loop_writer.Write(argv[1], argv[2])
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv))
diff --git a/y2014/control_loops/shooter/replay_shooter.cc b/y2014/control_loops/shooter/replay_shooter.cc
new file mode 100644
index 0000000..20c953f
--- /dev/null
+++ b/y2014/control_loops/shooter/replay_shooter.cc
@@ -0,0 +1,24 @@
+#include "aos/common/controls/replay_control_loop.h"
+#include "aos/linux_code/init.h"
+
+#include "y2014/control_loops/shooter/shooter.q.h"
+
+// Reads one or more log files and sends out all the queue messages (in the
+// correct order and at the correct time) to feed a "live" shooter process.
+
+int main(int argc, char **argv) {
+ if (argc <= 1) {
+ fprintf(stderr, "Need at least one file to replay!\n");
+ return EXIT_FAILURE;
+ }
+
+ ::aos::InitNRT();
+
+ ::aos::controls::ControlLoopReplayer<::frc971::control_loops::ShooterQueue>
+ replayer(&::frc971::control_loops::shooter_queue, "shooter");
+ for (int i = 1; i < argc; ++i) {
+ replayer.ProcessFile(argv[i]);
+ }
+
+ ::aos::Cleanup();
+}
diff --git a/y2014/control_loops/shooter/shooter.cc b/y2014/control_loops/shooter/shooter.cc
new file mode 100644
index 0000000..5211967
--- /dev/null
+++ b/y2014/control_loops/shooter/shooter.cc
@@ -0,0 +1,691 @@
+#include "y2014/control_loops/shooter/shooter.h"
+
+#include <stdio.h>
+
+#include <algorithm>
+#include <limits>
+
+#include "aos/common/controls/control_loops.q.h"
+#include "aos/common/logging/logging.h"
+#include "aos/common/logging/queue_logging.h"
+
+#include "y2014/constants.h"
+#include "y2014/control_loops/shooter/shooter_motor_plant.h"
+
+namespace frc971 {
+namespace control_loops {
+
+using ::aos::time::Time;
+
+void ZeroedStateFeedbackLoop::CapU() {
+ const double old_voltage = voltage_;
+ voltage_ += U(0, 0);
+
+ uncapped_voltage_ = voltage_;
+
+ // Make sure that reality and the observer can't get too far off. There is a
+ // delay by one cycle between the applied voltage and X_hat(2, 0), so compare
+ // against last cycle's voltage.
+ if (X_hat(2, 0) > last_voltage_ + 4.0) {
+ voltage_ -= X_hat(2, 0) - (last_voltage_ + 4.0);
+ LOG(DEBUG, "Capping due to runaway\n");
+ } else if (X_hat(2, 0) < last_voltage_ - 4.0) {
+ voltage_ += X_hat(2, 0) - (last_voltage_ - 4.0);
+ LOG(DEBUG, "Capping due to runaway\n");
+ }
+
+ voltage_ = std::min(max_voltage_, voltage_);
+ voltage_ = std::max(-max_voltage_, voltage_);
+ mutable_U(0, 0) = voltage_ - old_voltage;
+
+ LOG_STRUCT(DEBUG, "output", ShooterVoltageToLog(X_hat(2, 0), voltage_));
+
+ last_voltage_ = voltage_;
+ capped_goal_ = false;
+}
+
+void ZeroedStateFeedbackLoop::CapGoal() {
+ if (uncapped_voltage() > max_voltage_) {
+ double dx;
+ if (controller_index() == 0) {
+ dx = (uncapped_voltage() - max_voltage_) /
+ (K(0, 0) - A(1, 0) * K(0, 2) / A(1, 2));
+ mutable_R(0, 0) -= dx;
+ mutable_R(2, 0) -= -A(1, 0) / A(1, 2) * dx;
+ } else {
+ dx = (uncapped_voltage() - max_voltage_) / K(0, 0);
+ mutable_R(0, 0) -= dx;
+ }
+ capped_goal_ = true;
+ LOG_STRUCT(DEBUG, "to prevent windup", ShooterMovingGoal(dx));
+ } else if (uncapped_voltage() < -max_voltage_) {
+ double dx;
+ if (controller_index() == 0) {
+ dx = (uncapped_voltage() + max_voltage_) /
+ (K(0, 0) - A(1, 0) * K(0, 2) / A(1, 2));
+ mutable_R(0, 0) -= dx;
+ mutable_R(2, 0) -= -A(1, 0) / A(1, 2) * dx;
+ } else {
+ dx = (uncapped_voltage() + max_voltage_) / K(0, 0);
+ mutable_R(0, 0) -= dx;
+ }
+ capped_goal_ = true;
+ LOG_STRUCT(DEBUG, "to prevent windup", ShooterMovingGoal(dx));
+ } else {
+ capped_goal_ = false;
+ }
+}
+
+void ZeroedStateFeedbackLoop::RecalculatePowerGoal() {
+ if (controller_index() == 0) {
+ mutable_R(2, 0) = (-A(1, 0) / A(1, 2) * R(0, 0) - A(1, 1) / A(1, 2) * R(1, 0));
+ } else {
+ mutable_R(2, 0) = -A(1, 1) / A(1, 2) * R(1, 0);
+ }
+}
+
+void ZeroedStateFeedbackLoop::SetCalibration(double encoder_val,
+ double known_position) {
+ double old_position = absolute_position();
+ double previous_offset = offset_;
+ offset_ = known_position - encoder_val;
+ double doffset = offset_ - previous_offset;
+ mutable_X_hat(0, 0) += doffset;
+ // Offset the goal so we don't move.
+ mutable_R(0, 0) += doffset;
+ if (controller_index() == 0) {
+ mutable_R(2, 0) += -A(1, 0) / A(1, 2) * (doffset);
+ }
+ LOG_STRUCT(
+ DEBUG, "sensor edge (fake?)",
+ ShooterChangeCalibration(encoder_val, known_position, old_position,
+ absolute_position(), previous_offset, offset_));
+}
+
+ShooterMotor::ShooterMotor(control_loops::ShooterQueue *my_shooter)
+ : aos::controls::ControlLoop<control_loops::ShooterQueue>(my_shooter),
+ shooter_(MakeShooterLoop()),
+ state_(STATE_INITIALIZE),
+ loading_problem_end_time_(0, 0),
+ load_timeout_(0, 0),
+ shooter_brake_set_time_(0, 0),
+ unload_timeout_(0, 0),
+ shot_end_time_(0, 0),
+ cycles_not_moved_(0),
+ shot_count_(0),
+ zeroed_(false),
+ distal_posedge_validation_cycles_left_(0),
+ proximal_posedge_validation_cycles_left_(0),
+ last_distal_current_(true),
+ last_proximal_current_(true) {}
+
+double ShooterMotor::PowerToPosition(double power) {
+ const frc971::constants::Values &values = constants::GetValues();
+ double maxpower = 0.5 * kSpringConstant *
+ (kMaxExtension * kMaxExtension -
+ (kMaxExtension - values.shooter.upper_limit) *
+ (kMaxExtension - values.shooter.upper_limit));
+ if (power < 0) {
+ LOG_STRUCT(WARNING, "negative power", PowerAdjustment(power, 0));
+ power = 0;
+ } else if (power > maxpower) {
+ LOG_STRUCT(WARNING, "power too high", PowerAdjustment(power, maxpower));
+ power = maxpower;
+ }
+
+ double mp = kMaxExtension * kMaxExtension - (power + power) / kSpringConstant;
+ double new_pos = 0.10;
+ if (mp < 0) {
+ LOG(ERROR,
+ "Power calculation has negative number before square root (%f).\n", mp);
+ } else {
+ new_pos = kMaxExtension - ::std::sqrt(mp);
+ }
+
+ new_pos = ::std::min(::std::max(new_pos, values.shooter.lower_limit),
+ values.shooter.upper_limit);
+ return new_pos;
+}
+
+double ShooterMotor::PositionToPower(double position) {
+ double power = kSpringConstant * position * (kMaxExtension - position / 2.0);
+ return power;
+}
+
+void ShooterMotor::CheckCalibrations(
+ const control_loops::ShooterQueue::Position *position) {
+ CHECK_NOTNULL(position);
+ const frc971::constants::Values &values = constants::GetValues();
+
+ // TODO(austin): Validate that this is the right edge.
+ // If we see a posedge on any of the hall effects,
+ if (position->pusher_proximal.posedge_count != last_proximal_posedge_count_ &&
+ !last_proximal_current_) {
+ proximal_posedge_validation_cycles_left_ = 2;
+ }
+ if (proximal_posedge_validation_cycles_left_ > 0) {
+ if (position->pusher_proximal.current) {
+ --proximal_posedge_validation_cycles_left_;
+ if (proximal_posedge_validation_cycles_left_ == 0) {
+ shooter_.SetCalibration(
+ position->pusher_proximal.posedge_value,
+ values.shooter.pusher_proximal.upper_angle);
+
+ LOG(DEBUG, "Setting calibration using proximal sensor\n");
+ zeroed_ = true;
+ }
+ } else {
+ proximal_posedge_validation_cycles_left_ = 0;
+ }
+ }
+
+ if (position->pusher_distal.posedge_count != last_distal_posedge_count_ &&
+ !last_distal_current_) {
+ distal_posedge_validation_cycles_left_ = 2;
+ }
+ if (distal_posedge_validation_cycles_left_ > 0) {
+ if (position->pusher_distal.current) {
+ --distal_posedge_validation_cycles_left_;
+ if (distal_posedge_validation_cycles_left_ == 0) {
+ shooter_.SetCalibration(
+ position->pusher_distal.posedge_value,
+ values.shooter.pusher_distal.upper_angle);
+
+ LOG(DEBUG, "Setting calibration using distal sensor\n");
+ zeroed_ = true;
+ }
+ } else {
+ distal_posedge_validation_cycles_left_ = 0;
+ }
+ }
+}
+
+// Positive is out, and positive power is out.
+void ShooterMotor::RunIteration(
+ const control_loops::ShooterQueue::Goal *goal,
+ const control_loops::ShooterQueue::Position *position,
+ control_loops::ShooterQueue::Output *output,
+ control_loops::ShooterQueue::Status *status) {
+ constexpr double dt = 0.01;
+
+ if (goal && ::std::isnan(goal->shot_power)) {
+ state_ = STATE_ESTOP;
+ LOG(ERROR, "Estopping because got a shot power of NAN.\n");
+ }
+
+ // we must always have these or we have issues.
+ if (status == NULL) {
+ if (output) output->voltage = 0;
+ LOG(ERROR, "Thought I would just check for null and die.\n");
+ return;
+ }
+ status->ready = false;
+
+ if (WasReset()) {
+ state_ = STATE_INITIALIZE;
+ last_distal_current_ = position->pusher_distal.current;
+ last_proximal_current_ = position->pusher_proximal.current;
+ }
+ if (position) {
+ shooter_.CorrectPosition(position->position);
+ }
+
+ // Disable the motors now so that all early returns will return with the
+ // motors disabled.
+ if (output) output->voltage = 0;
+
+ const frc971::constants::Values &values = constants::GetValues();
+
+ // Don't even let the control loops run.
+ bool shooter_loop_disable = false;
+
+ const bool disabled =
+ !::aos::joystick_state.get() || !::aos::joystick_state->enabled;
+
+ // If true, move the goal if we saturate.
+ bool cap_goal = false;
+
+ // TODO(austin): Move the offset if we see or don't see a hall effect when we
+ // expect to see one.
+ // Probably not needed yet.
+
+ if (position) {
+ int last_controller_index = shooter_.controller_index();
+ if (position->plunger && position->latch) {
+ // Use the controller without the spring if the latch is set and the
+ // plunger is back
+ shooter_.set_controller_index(1);
+ } else {
+ // Otherwise use the controller with the spring.
+ shooter_.set_controller_index(0);
+ }
+ if (shooter_.controller_index() != last_controller_index) {
+ shooter_.RecalculatePowerGoal();
+ }
+ }
+
+ switch (state_) {
+ case STATE_INITIALIZE:
+ if (position) {
+ // Reinitialize the internal filter state.
+ shooter_.InitializeState(position->position);
+
+ // Start off with the assumption that we are at the value
+ // futhest back given our sensors.
+ if (position->pusher_distal.current) {
+ shooter_.SetCalibration(position->position,
+ values.shooter.pusher_distal.lower_angle);
+ } else if (position->pusher_proximal.current) {
+ shooter_.SetCalibration(position->position,
+ values.shooter.pusher_proximal.upper_angle);
+ } else {
+ shooter_.SetCalibration(position->position,
+ values.shooter.upper_limit);
+ }
+
+ // Go to the current position.
+ shooter_.SetGoalPosition(shooter_.absolute_position(), 0.0);
+ // If the plunger is all the way back, we want to be latched.
+ latch_piston_ = position->plunger;
+ brake_piston_ = false;
+ if (position->latch == latch_piston_) {
+ state_ = STATE_REQUEST_LOAD;
+ } else {
+ shooter_loop_disable = true;
+ LOG(DEBUG,
+ "Not moving on until the latch has moved to avoid a crash\n");
+ }
+ } else {
+ // If we can't start yet because we don't know where we are, set the
+ // latch and brake to their defaults.
+ latch_piston_ = true;
+ brake_piston_ = true;
+ }
+ break;
+ case STATE_REQUEST_LOAD:
+ if (position) {
+ zeroed_ = false;
+ if (position->pusher_distal.current ||
+ position->pusher_proximal.current) {
+ // We started on the sensor, back up until we are found.
+ // If the plunger is all the way back and not latched, it won't be
+ // there for long.
+ state_ = STATE_LOAD_BACKTRACK;
+
+ // The plunger is already back and latched. Don't release it.
+ if (position->plunger && position->latch) {
+ latch_piston_ = true;
+ } else {
+ latch_piston_ = false;
+ }
+ } else if (position->plunger && position->latch) {
+ // The plunger is back and we are latched. We most likely got here
+ // from Initialize, in which case we want to 'load' again anyways to
+ // zero.
+ Load();
+ latch_piston_ = true;
+ } else {
+ // Off the sensor, start loading.
+ Load();
+ latch_piston_ = false;
+ }
+ }
+
+ // Hold our current position.
+ shooter_.SetGoalPosition(shooter_.absolute_position(), 0.0);
+ brake_piston_ = false;
+ break;
+ case STATE_LOAD_BACKTRACK:
+ // If we are here, then that means we started past the edge where we want
+ // to zero. Move backwards until we don't see the sensor anymore.
+ // The plunger is contacting the pusher (or will be shortly).
+
+ if (!disabled) {
+ shooter_.SetGoalPosition(
+ shooter_.goal_position() + values.shooter.zeroing_speed * dt,
+ values.shooter.zeroing_speed);
+ }
+ cap_goal = true;
+ shooter_.set_max_voltage(4.0);
+
+ if (position) {
+ if (!position->pusher_distal.current &&
+ !position->pusher_proximal.current) {
+ Load();
+ }
+ latch_piston_ = position->plunger;
+ }
+
+ brake_piston_ = false;
+ break;
+ case STATE_LOAD:
+ // If we are disabled right now, reset the timer.
+ if (disabled) {
+ Load();
+ // Latch defaults to true when disabled. Leave it latched until we have
+ // useful sensor data.
+ latch_piston_ = true;
+ }
+ if (output == nullptr) {
+ load_timeout_ += ::aos::controls::kLoopFrequency;
+ }
+ // Go to 0, which should be the latch position, or trigger a hall effect
+ // on the way. If we don't see edges where we are supposed to, the
+ // offset will be updated by code above.
+ shooter_.SetGoalPosition(0.0, 0.0);
+
+ if (position) {
+ CheckCalibrations(position);
+
+ // Latch if the plunger is far enough back to trigger the hall effect.
+ // This happens when the distal sensor is triggered.
+ latch_piston_ = position->pusher_distal.current || position->plunger;
+
+ // Check if we are latched and back. Make sure the plunger is all the
+ // way back as well.
+ if (position->plunger && position->latch &&
+ position->pusher_distal.current) {
+ if (!zeroed_) {
+ state_ = STATE_REQUEST_LOAD;
+ } else {
+ state_ = STATE_PREPARE_SHOT;
+ }
+ } else if (position->plunger &&
+ ::std::abs(shooter_.absolute_position() -
+ shooter_.goal_position()) < 0.001) {
+ // We are at the goal, but not latched.
+ state_ = STATE_LOADING_PROBLEM;
+ loading_problem_end_time_ = Time::Now() + kLoadProblemEndTimeout;
+ }
+ }
+ if (load_timeout_ < Time::Now()) {
+ if (position) {
+ if (!position->pusher_distal.current ||
+ !position->pusher_proximal.current) {
+ state_ = STATE_ESTOP;
+ LOG(ERROR, "Estopping because took too long to load.\n");
+ }
+ }
+ }
+ brake_piston_ = false;
+ break;
+ case STATE_LOADING_PROBLEM:
+ if (disabled) {
+ state_ = STATE_REQUEST_LOAD;
+ break;
+ }
+ // We got to the goal, but the latch hasn't registered as down. It might
+ // be stuck, or on it's way but not there yet.
+ if (Time::Now() > loading_problem_end_time_) {
+ // Timeout by unloading.
+ Unload();
+ } else if (position && position->plunger && position->latch) {
+ // If both trigger, we are latched.
+ state_ = STATE_PREPARE_SHOT;
+ }
+ // Move a bit further back to help it trigger.
+ // If the latch is slow due to the air flowing through the tubes or
+ // inertia, but is otherwise free, this won't have much time to do
+ // anything and is safe. Otherwise this gives us a bit more room to free
+ // up the latch.
+ shooter_.SetGoalPosition(values.shooter.lower_limit, 0.0);
+ if (position) {
+ LOG(DEBUG, "Waiting on latch: plunger %d, latch: %d\n",
+ position->plunger, position->latch);
+ }
+
+ latch_piston_ = true;
+ brake_piston_ = false;
+ break;
+ case STATE_PREPARE_SHOT:
+ // Move the shooter to the shot power set point and then lock the brake.
+ // TODO(austin): Timeout. Low priority.
+
+ if (goal) {
+ shooter_.SetGoalPosition(PowerToPosition(goal->shot_power), 0.0);
+ }
+
+ LOG(DEBUG, "PDIFF: absolute_position: %.2f, pow: %.2f\n",
+ shooter_.absolute_position(),
+ goal ? PowerToPosition(goal->shot_power)
+ : ::std::numeric_limits<double>::quiet_NaN());
+ if (goal &&
+ ::std::abs(shooter_.absolute_position() -
+ PowerToPosition(goal->shot_power)) < 0.001 &&
+ ::std::abs(shooter_.absolute_velocity()) < 0.005) {
+ // We are there, set the brake and move on.
+ latch_piston_ = true;
+ brake_piston_ = true;
+ shooter_brake_set_time_ = Time::Now() + kShooterBrakeSetTime;
+ state_ = STATE_READY;
+ } else {
+ latch_piston_ = true;
+ brake_piston_ = false;
+ }
+ if (goal && goal->unload_requested) {
+ Unload();
+ }
+ break;
+ case STATE_READY:
+ LOG(DEBUG, "In ready\n");
+ // Wait until the brake is set, and a shot is requested or the shot power
+ // is changed.
+ if (Time::Now() > shooter_brake_set_time_) {
+ status->ready = true;
+ // We have waited long enough for the brake to set, turn the shooter
+ // control loop off.
+ shooter_loop_disable = true;
+ LOG(DEBUG, "Brake is now set\n");
+ if (goal && goal->shot_requested && !disabled) {
+ LOG(DEBUG, "Shooting now\n");
+ shooter_loop_disable = true;
+ shot_end_time_ = Time::Now() + kShotEndTimeout;
+ firing_starting_position_ = shooter_.absolute_position();
+ state_ = STATE_FIRE;
+ }
+ }
+ if (state_ == STATE_READY && goal &&
+ ::std::abs(shooter_.absolute_position() -
+ PowerToPosition(goal->shot_power)) > 0.002) {
+ // TODO(austin): Add a state to release the brake.
+
+ // TODO(austin): Do we want to set the brake here or after shooting?
+ // Depends on air usage.
+ status->ready = false;
+ LOG(DEBUG, "Preparing shot again.\n");
+ state_ = STATE_PREPARE_SHOT;
+ }
+
+ if (goal) {
+ shooter_.SetGoalPosition(PowerToPosition(goal->shot_power), 0.0);
+ }
+
+ latch_piston_ = true;
+ brake_piston_ = true;
+
+ if (goal && goal->unload_requested) {
+ Unload();
+ }
+ break;
+
+ case STATE_FIRE:
+ if (disabled) {
+ if (position) {
+ if (position->plunger) {
+ // If disabled and the plunger is still back there, reset the
+ // timeout.
+ shot_end_time_ = Time::Now() + kShotEndTimeout;
+ }
+ }
+ }
+ shooter_loop_disable = true;
+ // Count the number of contiguous cycles during which we haven't moved.
+ if (::std::abs(last_position_.position - shooter_.absolute_position()) <
+ 0.0005) {
+ ++cycles_not_moved_;
+ } else {
+ cycles_not_moved_ = 0;
+ }
+
+ // If we have moved any amount since the start and the shooter has now
+ // been still for a couple cycles, the shot finished.
+ // Also move on if it times out.
+ if ((::std::abs(firing_starting_position_ -
+ shooter_.absolute_position()) > 0.0005 &&
+ cycles_not_moved_ > 3) ||
+ Time::Now() > shot_end_time_) {
+ state_ = STATE_REQUEST_LOAD;
+ ++shot_count_;
+ }
+ latch_piston_ = false;
+ brake_piston_ = true;
+ break;
+ case STATE_UNLOAD:
+ // Reset the timeouts.
+ if (disabled) Unload();
+
+ // If it is latched and the plunger is back, move the pusher back to catch
+ // the plunger.
+ bool all_back;
+ if (position) {
+ all_back = position->plunger && position->latch;
+ } else {
+ all_back = last_position_.plunger && last_position_.latch;
+ }
+
+ if (all_back) {
+ // Pull back to 0, 0.
+ shooter_.SetGoalPosition(0.0, 0.0);
+ if (shooter_.absolute_position() < 0.005) {
+ // When we are close enough, 'fire'.
+ latch_piston_ = false;
+ } else {
+ latch_piston_ = true;
+
+ if (position) {
+ CheckCalibrations(position);
+ }
+ }
+ } else {
+ // The plunger isn't all the way back, or it is and it is unlatched, so
+ // we can now unload.
+ shooter_.SetGoalPosition(shooter_.absolute_position(), 0.0);
+ latch_piston_ = false;
+ state_ = STATE_UNLOAD_MOVE;
+ unload_timeout_ = Time::Now() + kUnloadTimeout;
+ }
+
+ if (Time::Now() > unload_timeout_) {
+ // We have been stuck trying to unload for way too long, give up and
+ // turn everything off.
+ state_ = STATE_ESTOP;
+ LOG(ERROR, "Estopping because took too long to unload.\n");
+ }
+
+ brake_piston_ = false;
+ break;
+ case STATE_UNLOAD_MOVE: {
+ if (disabled) {
+ unload_timeout_ = Time::Now() + kUnloadTimeout;
+ shooter_.SetGoalPosition(shooter_.absolute_position(), 0.0);
+ }
+ cap_goal = true;
+ shooter_.set_max_voltage(6.0);
+
+ // Slowly move back until we hit the upper limit.
+ // If we were at the limit last cycle, we are done unloading.
+ // This is because if we saturate, we might hit the limit before we are
+ // actually there.
+ if (shooter_.goal_position() >= values.shooter.upper_limit) {
+ shooter_.SetGoalPosition(values.shooter.upper_limit, 0.0);
+ // We don't want the loop fighting the spring when we are unloaded.
+ // Turn it off.
+ shooter_loop_disable = true;
+ state_ = STATE_READY_UNLOAD;
+ } else {
+ shooter_.SetGoalPosition(
+ ::std::min(
+ values.shooter.upper_limit,
+ shooter_.goal_position() + values.shooter.unload_speed * dt),
+ values.shooter.unload_speed);
+ }
+
+ latch_piston_ = false;
+ brake_piston_ = false;
+ } break;
+ case STATE_READY_UNLOAD:
+ if (goal && goal->load_requested) {
+ state_ = STATE_REQUEST_LOAD;
+ }
+ // If we are ready to load again,
+ shooter_loop_disable = true;
+
+ latch_piston_ = false;
+ brake_piston_ = false;
+ break;
+
+ case STATE_ESTOP:
+ LOG(WARNING, "estopped\n");
+ // Totally lost, go to a safe state.
+ shooter_loop_disable = true;
+ latch_piston_ = true;
+ brake_piston_ = true;
+ break;
+ }
+
+ if (!shooter_loop_disable) {
+ LOG_STRUCT(DEBUG, "running the loop",
+ ShooterStatusToLog(shooter_.goal_position(),
+ shooter_.absolute_position()));
+ if (!cap_goal) {
+ shooter_.set_max_voltage(12.0);
+ }
+ shooter_.Update(output == NULL);
+ if (cap_goal) {
+ shooter_.CapGoal();
+ }
+ // We don't really want to output anything if we went through everything
+ // assuming the motors weren't working.
+ if (output) output->voltage = shooter_.voltage();
+ } else {
+ shooter_.Update(true);
+ shooter_.ZeroPower();
+ if (output) output->voltage = 0.0;
+ }
+
+ status->hard_stop_power = PositionToPower(shooter_.absolute_position());
+
+ if (output) {
+ output->latch_piston = latch_piston_;
+ output->brake_piston = brake_piston_;
+ }
+
+ if (position) {
+ LOG_STRUCT(DEBUG, "internal state",
+ ShooterStateToLog(
+ shooter_.absolute_position(), shooter_.absolute_velocity(),
+ state_, position->latch, position->pusher_proximal.current,
+ position->pusher_distal.current, position->plunger,
+ brake_piston_, latch_piston_));
+
+ last_position_ = *position;
+
+ last_distal_posedge_count_ = position->pusher_distal.posedge_count;
+ last_proximal_posedge_count_ = position->pusher_proximal.posedge_count;
+ last_distal_current_ = position->pusher_distal.current;
+ last_proximal_current_ = position->pusher_proximal.current;
+ }
+
+ status->shots = shot_count_;
+}
+
+void ShooterMotor::ZeroOutputs() {
+ queue_group()->output.MakeWithBuilder()
+ .voltage(0)
+ .latch_piston(latch_piston_)
+ .brake_piston(brake_piston_)
+ .Send();
+}
+
+} // namespace control_loops
+} // namespace frc971
diff --git a/y2014/control_loops/shooter/shooter.gyp b/y2014/control_loops/shooter/shooter.gyp
new file mode 100644
index 0000000..e96b592
--- /dev/null
+++ b/y2014/control_loops/shooter/shooter.gyp
@@ -0,0 +1,82 @@
+{
+ 'targets': [
+ {
+ 'target_name': 'replay_shooter',
+ 'type': 'executable',
+ 'variables': {
+ 'no_rsync': 1,
+ },
+ 'sources': [
+ 'replay_shooter.cc',
+ ],
+ 'dependencies': [
+ 'shooter_queue',
+ '<(AOS)/common/controls/controls.gyp:replay_control_loop',
+ '<(AOS)/linux_code/linux_code.gyp:init',
+ ],
+ },
+ {
+ 'target_name': 'shooter_queue',
+ 'type': 'static_library',
+ 'sources': ['shooter.q'],
+ 'variables': {
+ 'header_path': 'y2014/control_loops/shooter',
+ },
+ 'dependencies': [
+ '<(AOS)/common/controls/controls.gyp:control_loop_queues',
+ '<(DEPTH)/frc971/control_loops/control_loops.gyp:queues',
+ ],
+ 'export_dependent_settings': [
+ '<(AOS)/common/controls/controls.gyp:control_loop_queues',
+ '<(DEPTH)/frc971/control_loops/control_loops.gyp:queues',
+ ],
+ 'includes': ['../../../aos/build/queues.gypi'],
+ },
+ {
+ 'target_name': 'shooter_lib',
+ 'type': 'static_library',
+ 'sources': [
+ 'shooter.cc',
+ 'shooter_motor_plant.cc',
+ 'unaugmented_shooter_motor_plant.cc',
+ ],
+ 'dependencies': [
+ 'shooter_queue',
+ '<(AOS)/common/controls/controls.gyp:control_loop',
+ '<(DEPTH)/y2014/y2014.gyp:constants',
+ '<(DEPTH)/frc971/control_loops/control_loops.gyp:state_feedback_loop',
+ '<(AOS)/common/logging/logging.gyp:queue_logging',
+ ],
+ 'export_dependent_settings': [
+ 'shooter_queue',
+ '<(AOS)/common/controls/controls.gyp:control_loop',
+ '<(DEPTH)/frc971/control_loops/control_loops.gyp:state_feedback_loop',
+ ],
+ },
+ {
+ 'target_name': 'shooter_lib_test',
+ 'type': 'executable',
+ 'sources': [
+ 'shooter_lib_test.cc',
+ ],
+ 'dependencies': [
+ '<(EXTERNALS):gtest',
+ 'shooter_queue',
+ 'shooter_lib',
+ '<(AOS)/common/controls/controls.gyp:control_loop_test',
+ '<(DEPTH)/frc971/control_loops/control_loops.gyp:state_feedback_loop',
+ ],
+ },
+ {
+ 'target_name': 'shooter',
+ 'type': 'executable',
+ 'sources': [
+ 'shooter_main.cc',
+ ],
+ 'dependencies': [
+ '<(AOS)/linux_code/linux_code.gyp:init',
+ 'shooter_lib',
+ ],
+ },
+ ],
+}
diff --git a/y2014/control_loops/shooter/shooter.h b/y2014/control_loops/shooter/shooter.h
new file mode 100644
index 0000000..71347cb
--- /dev/null
+++ b/y2014/control_loops/shooter/shooter.h
@@ -0,0 +1,224 @@
+#ifndef Y2014_CONTROL_LOOPS_shooter_shooter_H_
+#define Y2014_CONTROL_LOOPS_shooter_shooter_H_
+
+#include <memory>
+
+#include "aos/common/controls/control_loop.h"
+#include "frc971/control_loops/state_feedback_loop.h"
+#include "aos/common/time.h"
+
+#include "y2014/constants.h"
+#include "y2014/control_loops/shooter/shooter_motor_plant.h"
+#include "y2014/control_loops/shooter/shooter.q.h"
+
+namespace frc971 {
+namespace control_loops {
+namespace testing {
+class ShooterTest_UnloadWindupPositive_Test;
+class ShooterTest_UnloadWindupNegative_Test;
+class ShooterTest_RezeroWhileUnloading_Test;
+};
+
+using ::aos::time::Time;
+
+// Note: Everything in this file assumes that there is a 1 cycle delay between
+// power being requested and it showing up at the motor. It assumes that
+// X_hat(2, 1) is the voltage being applied as well. It will go unstable if
+// that isn't true.
+
+// This class implements the CapU function correctly given all the extra
+// information that we know about.
+// It does not have any zeroing logic in it, only logic to deal with a delta U
+// controller.
+class ZeroedStateFeedbackLoop : public StateFeedbackLoop<3, 1, 1> {
+ public:
+ ZeroedStateFeedbackLoop(StateFeedbackLoop<3, 1, 1> &&loop)
+ : StateFeedbackLoop<3, 1, 1>(::std::move(loop)),
+ voltage_(0.0),
+ last_voltage_(0.0),
+ uncapped_voltage_(0.0),
+ offset_(0.0),
+ max_voltage_(12.0),
+ capped_goal_(false) {}
+
+ const static int kZeroingMaxVoltage = 5;
+
+ virtual void CapU();
+
+ // Returns the accumulated voltage.
+ double voltage() const { return voltage_; }
+
+ // Returns the uncapped voltage.
+ double uncapped_voltage() const { return uncapped_voltage_; }
+
+ // Zeros the accumulator.
+ void ZeroPower() { voltage_ = 0.0; }
+
+ // Sets the calibration offset given the absolute angle and the corrisponding
+ // encoder value.
+ void SetCalibration(double encoder_val, double known_position);
+
+ double offset() const { return offset_; }
+
+ double absolute_position() const { return X_hat(0, 0) + kPositionOffset; }
+ double absolute_velocity() const { return X_hat(1, 0); }
+
+ void CorrectPosition(double position) {
+ Eigen::Matrix<double, 1, 1> Y;
+ Y << position + offset_ - kPositionOffset;
+ Correct(Y);
+ }
+
+ // Recomputes the power goal for the current controller and position/velocity.
+ void RecalculatePowerGoal();
+
+ double goal_position() const { return R(0, 0) + kPositionOffset; }
+ double goal_velocity() const { return R(1, 0); }
+ void InitializeState(double position) {
+ mutable_X_hat(0, 0) = position - kPositionOffset;
+ mutable_X_hat(1, 0) = 0.0;
+ mutable_X_hat(2, 0) = 0.0;
+ }
+
+ void SetGoalPosition(double desired_position, double desired_velocity) {
+ LOG(DEBUG, "Goal position: %f Goal velocity: %f\n", desired_position,
+ desired_velocity);
+
+ mutable_R() << desired_position - kPositionOffset, desired_velocity,
+ (-A(1, 0) / A(1, 2) * (desired_position - kPositionOffset) -
+ A(1, 1) / A(1, 2) * desired_velocity);
+ }
+
+ double position() const { return X_hat(0, 0) - offset_ + kPositionOffset; }
+
+ void set_max_voltage(double max_voltage) { max_voltage_ = max_voltage; }
+ bool capped_goal() const { return capped_goal_; }
+
+ void CapGoal();
+
+ // Friend the test classes for acces to the internal state.
+ friend class testing::ShooterTest_RezeroWhileUnloading_Test;
+
+ private:
+ // The offset between what is '0' (0 rate on the spring) and the 0 (all the
+ // way cocked).
+ constexpr static double kPositionOffset = kMaxExtension;
+ // The accumulated voltage to apply to the motor.
+ double voltage_;
+ double last_voltage_;
+ double uncapped_voltage_;
+ double offset_;
+ double max_voltage_;
+ bool capped_goal_;
+};
+
+const Time kUnloadTimeout = Time::InSeconds(10);
+const Time kLoadTimeout = Time::InSeconds(2);
+const Time kLoadProblemEndTimeout = Time::InSeconds(1.0);
+const Time kShooterBrakeSetTime = Time::InSeconds(0.05);
+// Time to wait after releasing the latch piston before winching back again.
+const Time kShotEndTimeout = Time::InSeconds(0.2);
+const Time kPrepareFireEndTime = Time::InMS(40);
+
+class ShooterMotor
+ : public aos::controls::ControlLoop<control_loops::ShooterQueue> {
+ public:
+ explicit ShooterMotor(control_loops::ShooterQueue *my_shooter =
+ &control_loops::shooter_queue);
+
+ // True if the goal was moved to avoid goal windup.
+ bool capped_goal() const { return shooter_.capped_goal(); }
+
+ double PowerToPosition(double power);
+ double PositionToPower(double position);
+ void CheckCalibrations(const control_loops::ShooterQueue::Position *position);
+
+ typedef enum {
+ STATE_INITIALIZE = 0,
+ STATE_REQUEST_LOAD = 1,
+ STATE_LOAD_BACKTRACK = 2,
+ STATE_LOAD = 3,
+ STATE_LOADING_PROBLEM = 4,
+ STATE_PREPARE_SHOT = 5,
+ STATE_READY = 6,
+ STATE_FIRE = 8,
+ STATE_UNLOAD = 9,
+ STATE_UNLOAD_MOVE = 10,
+ STATE_READY_UNLOAD = 11,
+ STATE_ESTOP = 12
+ } State;
+
+ State state() { return state_; }
+
+ protected:
+ virtual void RunIteration(
+ const ShooterQueue::Goal *goal,
+ const control_loops::ShooterQueue::Position *position,
+ ShooterQueue::Output *output, ShooterQueue::Status *status);
+
+ private:
+ // We have to override this to keep the pistons in the correct positions.
+ virtual void ZeroOutputs();
+
+ // Friend the test classes for acces to the internal state.
+ friend class testing::ShooterTest_UnloadWindupPositive_Test;
+ friend class testing::ShooterTest_UnloadWindupNegative_Test;
+ friend class testing::ShooterTest_RezeroWhileUnloading_Test;
+
+ // Enter state STATE_UNLOAD
+ void Unload() {
+ state_ = STATE_UNLOAD;
+ unload_timeout_ = Time::Now() + kUnloadTimeout;
+ }
+ // Enter state STATE_LOAD
+ void Load() {
+ state_ = STATE_LOAD;
+ load_timeout_ = Time::Now() + kLoadTimeout;
+ }
+
+ control_loops::ShooterQueue::Position last_position_;
+
+ ZeroedStateFeedbackLoop shooter_;
+
+ // state machine state
+ State state_;
+
+ // time to giving up on loading problem
+ Time loading_problem_end_time_;
+
+ // The end time when loading for it to timeout.
+ Time load_timeout_;
+
+ // wait for brake to set
+ Time shooter_brake_set_time_;
+
+ // The timeout for unloading.
+ Time unload_timeout_;
+
+ // time that shot must have completed
+ Time shot_end_time_;
+
+ // track cycles that we are stuck to detect errors
+ int cycles_not_moved_;
+
+ double firing_starting_position_;
+
+ // True if the latch should be engaged and the brake should be engaged.
+ bool latch_piston_;
+ bool brake_piston_;
+ int32_t last_distal_posedge_count_;
+ int32_t last_proximal_posedge_count_;
+ uint32_t shot_count_;
+ bool zeroed_;
+ int distal_posedge_validation_cycles_left_;
+ int proximal_posedge_validation_cycles_left_;
+ bool last_distal_current_;
+ bool last_proximal_current_;
+
+ DISALLOW_COPY_AND_ASSIGN(ShooterMotor);
+};
+
+} // namespace control_loops
+} // namespace frc971
+
+#endif // Y2014_CONTROL_LOOPS_shooter_shooter_H_
diff --git a/y2014/control_loops/shooter/shooter.q b/y2014/control_loops/shooter/shooter.q
new file mode 100644
index 0000000..009e20e
--- /dev/null
+++ b/y2014/control_loops/shooter/shooter.q
@@ -0,0 +1,100 @@
+package frc971.control_loops;
+
+import "aos/common/controls/control_loops.q";
+import "frc971/control_loops/control_loops.q";
+
+queue_group ShooterQueue {
+ implements aos.control_loops.ControlLoop;
+
+ message Output {
+ double voltage;
+ // true: latch engaged, false: latch open
+ bool latch_piston;
+ // true: brake engaged false: brake released
+ bool brake_piston;
+ };
+ message Goal {
+ // Shot power in joules.
+ double shot_power;
+ // Shoots as soon as this is true.
+ bool shot_requested;
+ bool unload_requested;
+ bool load_requested;
+ };
+
+ // Back is when the springs are all the way stretched.
+ message Position {
+ // In meters, out is positive.
+ double position;
+
+ // If the latch piston is fired and this hall effect has been triggered, the
+ // plunger is all the way back and latched.
+ bool plunger;
+ // Gets triggered when the pusher is all the way back.
+ PosedgeOnlyCountedHallEffectStruct pusher_distal;
+ // Triggers just before pusher_distal.
+ PosedgeOnlyCountedHallEffectStruct pusher_proximal;
+ // Triggers when the latch engages.
+ bool latch;
+ };
+ message Status {
+ // Whether it's ready to shoot right now.
+ bool ready;
+ // Whether the plunger is in and out of the way of grabbing a ball.
+ // TODO(ben): Populate these!
+ bool cocked;
+ // How many times we've shot.
+ int32_t shots;
+ bool done;
+ // What we think the current position of the hard stop on the shooter is, in
+ // shot power (Joules).
+ double hard_stop_power;
+ };
+
+ queue Goal goal;
+ queue Position position;
+ queue Output output;
+ queue Status status;
+};
+
+queue_group ShooterQueue shooter_queue;
+
+struct ShooterStateToLog {
+ double absolute_position;
+ double absolute_velocity;
+ uint32_t state;
+ bool latch_sensor;
+ bool proximal;
+ bool distal;
+ bool plunger;
+ bool brake;
+ bool latch_piston;
+};
+
+struct ShooterVoltageToLog {
+ double X_hat;
+ double applied;
+};
+
+struct ShooterMovingGoal {
+ double dx;
+};
+
+struct ShooterChangeCalibration {
+ double encoder;
+ double real_position;
+ double old_position;
+ double new_position;
+ double old_offset;
+ double new_offset;
+};
+
+struct ShooterStatusToLog {
+ double goal;
+ double position;
+};
+
+struct PowerAdjustment {
+ double requested_power;
+ double actual_power;
+};
diff --git a/y2014/control_loops/shooter/shooter_lib_test.cc b/y2014/control_loops/shooter/shooter_lib_test.cc
new file mode 100644
index 0000000..2beb705
--- /dev/null
+++ b/y2014/control_loops/shooter/shooter_lib_test.cc
@@ -0,0 +1,723 @@
+#include <unistd.h>
+
+#include <memory>
+
+#include "gtest/gtest.h"
+#include "aos/common/network/team_number.h"
+#include "aos/common/controls/control_loop_test.h"
+#include "y2014/control_loops/shooter/shooter.q.h"
+#include "y2014/control_loops/shooter/shooter.h"
+#include "y2014/control_loops/shooter/unaugmented_shooter_motor_plant.h"
+#include "y2014/constants.h"
+
+using ::aos::time::Time;
+
+namespace frc971 {
+namespace control_loops {
+namespace testing {
+
+static const int kTestTeam = 1;
+
+class TeamNumberEnvironment : public ::testing::Environment {
+ public:
+ // Override this to define how to set up the environment.
+ virtual void SetUp() { aos::network::OverrideTeamNumber(kTestTeam); }
+};
+
+::testing::Environment *const team_number_env =
+ ::testing::AddGlobalTestEnvironment(new TeamNumberEnvironment);
+
+
+// Class which simulates the shooter and sends out queue messages containing the
+// position.
+class ShooterSimulation {
+ public:
+ // Constructs a motor simulation.
+ ShooterSimulation(double initial_position)
+ : shooter_plant_(new StateFeedbackPlant<2, 1, 1>(MakeRawShooterPlant())),
+ latch_piston_state_(false),
+ latch_delay_count_(0),
+ plunger_latched_(false),
+ brake_piston_state_(true),
+ brake_delay_count_(0),
+ shooter_queue_(
+ ".frc971.control_loops.shooter_queue", 0xcbf22ba9,
+ ".frc971.control_loops.shooter_queue.goal",
+ ".frc971.control_loops.shooter_queue.position",
+ ".frc971.control_loops.shooter_queue.output",
+ ".frc971.control_loops.shooter_queue.status") {
+ Reinitialize(initial_position);
+ }
+
+ // The difference between the position with 0 at the back, and the position
+ // with 0 measured where the spring has 0 force.
+ constexpr static double kPositionOffset = kMaxExtension;
+
+ void Reinitialize(double initial_position) {
+ LOG(INFO, "Reinitializing to {pos: %f}\n", initial_position);
+ StateFeedbackPlant<2, 1, 1> *plant = shooter_plant_.get();
+ initial_position_ = initial_position;
+ plant->mutable_X(0, 0) = initial_position_ - kPositionOffset;
+ plant->mutable_X(1, 0) = 0.0;
+ plant->mutable_Y() = plant->C() * plant->X();
+ last_voltage_ = 0.0;
+ last_plant_position_ = 0.0;
+ SetPhysicalSensors(&last_position_message_);
+ }
+
+ // Returns the absolute angle of the shooter.
+ double GetAbsolutePosition() const {
+ return shooter_plant_->Y(0, 0) + kPositionOffset;
+ }
+
+ // Returns the adjusted angle of the shooter.
+ double GetPosition() const {
+ return GetAbsolutePosition() - initial_position_;
+ }
+
+ // Makes sure pos is inside range (inclusive)
+ bool CheckRange(double pos, struct constants::Values::AnglePair pair) {
+ return (pos >= pair.lower_angle && pos <= pair.upper_angle);
+ }
+
+ // Sets the values of the physical sensors that can be directly observed
+ // (encoder, hall effect).
+ void SetPhysicalSensors(control_loops::ShooterQueue::Position *position) {
+ const frc971::constants::Values &values = constants::GetValues();
+
+ position->position = GetPosition();
+
+ LOG(DEBUG, "Physical shooter at {%f}\n", GetAbsolutePosition());
+
+ // Signal that the hall effect sensor has been triggered if it is within
+ // the correct range.
+ if (plunger_latched_) {
+ position->plunger = true;
+ // Only disengage the spring if we are greater than 0, which is where the
+ // latch will take the load off the pusher.
+ if (GetAbsolutePosition() > 0.0) {
+ shooter_plant_->set_plant_index(1);
+ } else {
+ shooter_plant_->set_plant_index(0);
+ }
+ } else {
+ shooter_plant_->set_plant_index(0);
+ position->plunger =
+ CheckRange(GetAbsolutePosition(), values.shooter.plunger_back);
+ }
+ position->pusher_distal.current =
+ CheckRange(GetAbsolutePosition(), values.shooter.pusher_distal);
+ position->pusher_proximal.current =
+ CheckRange(GetAbsolutePosition(), values.shooter.pusher_proximal);
+ }
+
+ void UpdateEffectEdge(
+ PosedgeOnlyCountedHallEffectStruct *sensor,
+ const PosedgeOnlyCountedHallEffectStruct &last_sensor,
+ const constants::Values::AnglePair &limits,
+ const control_loops::ShooterQueue::Position &last_position) {
+ sensor->posedge_count = last_sensor.posedge_count;
+ sensor->negedge_count = last_sensor.negedge_count;
+
+ sensor->posedge_value = last_sensor.posedge_value;
+
+ if (sensor->current && !last_sensor.current) {
+ ++sensor->posedge_count;
+ if (last_position.position + initial_position_ < limits.lower_angle) {
+ LOG(DEBUG, "Posedge value on lower edge of sensor, count is now %d\n",
+ sensor->posedge_count);
+ sensor->posedge_value = limits.lower_angle - initial_position_;
+ } else {
+ LOG(DEBUG, "Posedge value on upper edge of sensor, count is now %d\n",
+ sensor->posedge_count);
+ sensor->posedge_value = limits.upper_angle - initial_position_;
+ }
+ }
+ if (!sensor->current && last_sensor.current) {
+ ++sensor->negedge_count;
+ }
+ }
+
+ void SendPositionMessage() {
+ // the first bool is false
+ SendPositionMessage(false, false, false, false);
+ }
+
+ // Sends out the position queue messages.
+ // if the first bool is false then this is
+ // just the default state, otherwise will force
+ // it into a state using the passed values
+ void SendPositionMessage(bool use_passed, bool plunger_in,
+ bool latch_in, bool brake_in) {
+ const frc971::constants::Values &values = constants::GetValues();
+ ::aos::ScopedMessagePtr<control_loops::ShooterQueue::Position> position =
+ shooter_queue_.position.MakeMessage();
+
+ if (use_passed) {
+ plunger_latched_ = latch_in && plunger_in;
+ latch_piston_state_ = plunger_latched_;
+ brake_piston_state_ = brake_in;
+ }
+
+ SetPhysicalSensors(position.get());
+
+ position->latch = latch_piston_state_;
+
+ // Handle pusher distal hall effect
+ UpdateEffectEdge(&position->pusher_distal,
+ last_position_message_.pusher_distal,
+ values.shooter.pusher_distal, last_position_message_);
+
+ // Handle pusher proximal hall effect
+ UpdateEffectEdge(&position->pusher_proximal,
+ last_position_message_.pusher_proximal,
+ values.shooter.pusher_proximal, last_position_message_);
+
+ last_position_message_ = *position;
+ position.Send();
+ }
+
+ // Simulates the claw moving for one timestep.
+ void Simulate() {
+ last_plant_position_ = GetAbsolutePosition();
+ EXPECT_TRUE(shooter_queue_.output.FetchLatest());
+ if (shooter_queue_.output->latch_piston && !latch_piston_state_ &&
+ latch_delay_count_ <= 0) {
+ ASSERT_EQ(0, latch_delay_count_) << "The test doesn't support that.";
+ latch_delay_count_ = 6;
+ } else if (!shooter_queue_.output->latch_piston &&
+ latch_piston_state_ && latch_delay_count_ >= 0) {
+ ASSERT_EQ(0, latch_delay_count_) << "The test doesn't support that.";
+ latch_delay_count_ = -6;
+ }
+
+ if (shooter_queue_.output->brake_piston && !brake_piston_state_ &&
+ brake_delay_count_ <= 0) {
+ ASSERT_EQ(0, brake_delay_count_) << "The test doesn't support that.";
+ brake_delay_count_ = 5;
+ } else if (!shooter_queue_.output->brake_piston &&
+ brake_piston_state_ && brake_delay_count_ >= 0) {
+ ASSERT_EQ(0, brake_delay_count_) << "The test doesn't support that.";
+ brake_delay_count_ = -5;
+ }
+
+ // Handle brake internal state
+ if (!brake_piston_state_ && brake_delay_count_ > 0) {
+ if (brake_delay_count_ == 1) {
+ brake_piston_state_ = true;
+ }
+ brake_delay_count_--;
+ } else if (brake_piston_state_ && brake_delay_count_ < 0) {
+ if (brake_delay_count_ == -1) {
+ brake_piston_state_ = false;
+ }
+ brake_delay_count_++;
+ }
+
+ if (brake_piston_state_) {
+ shooter_plant_->mutable_U() << 0.0;
+ shooter_plant_->mutable_X(1, 0) = 0.0;
+ shooter_plant_->mutable_Y() = shooter_plant_->C() * shooter_plant_->X() +
+ shooter_plant_->D() * shooter_plant_->U();
+ } else {
+ shooter_plant_->mutable_U() << last_voltage_;
+ //shooter_plant_->U << shooter_queue_.output->voltage;
+ shooter_plant_->Update();
+ }
+ LOG(DEBUG, "Plant index is %d\n", shooter_plant_->plant_index());
+
+ // Handle latch hall effect
+ if (!latch_piston_state_ && latch_delay_count_ > 0) {
+ LOG(DEBUG, "latching simulation: %dp\n", latch_delay_count_);
+ if (latch_delay_count_ == 1) {
+ latch_piston_state_ = true;
+ EXPECT_GE(constants::GetValues().shooter.latch_max_safe_position,
+ GetAbsolutePosition());
+ plunger_latched_ = true;
+ }
+ latch_delay_count_--;
+ } else if (latch_piston_state_ && latch_delay_count_ < 0) {
+ LOG(DEBUG, "latching simulation: %dn\n", latch_delay_count_);
+ if (latch_delay_count_ == -1) {
+ latch_piston_state_ = false;
+ if (GetAbsolutePosition() > 0.002) {
+ EXPECT_TRUE(brake_piston_state_) << "Must have the brake set when "
+ "releasing the latch for "
+ "powerful shots.";
+ }
+ plunger_latched_ = false;
+ // TODO(austin): The brake should be set for a number of cycles after
+ // this as well.
+ shooter_plant_->mutable_X(0, 0) += 0.005;
+ }
+ latch_delay_count_++;
+ }
+
+ EXPECT_GE(constants::GetValues().shooter.upper_hard_limit,
+ GetAbsolutePosition());
+ EXPECT_LE(constants::GetValues().shooter.lower_hard_limit,
+ GetAbsolutePosition());
+
+ last_voltage_ = shooter_queue_.output->voltage;
+ ::aos::time::Time::IncrementMockTime(::aos::time::Time::InMS(10.0));
+ }
+
+ // pointer to plant
+ const ::std::unique_ptr<StateFeedbackPlant<2, 1, 1>> shooter_plant_;
+
+ // true latch closed
+ bool latch_piston_state_;
+ // greater than zero, delaying close. less than zero delaying open
+ int latch_delay_count_;
+
+ // Goes to true after latch_delay_count_ hits 0 while the plunger is back.
+ bool plunger_latched_;
+
+ // true brake locked
+ bool brake_piston_state_;
+ // greater than zero, delaying close. less than zero delaying open
+ int brake_delay_count_;
+
+ private:
+ ShooterQueue shooter_queue_;
+ double initial_position_;
+ double last_voltage_;
+
+ control_loops::ShooterQueue::Position last_position_message_;
+ double last_plant_position_;
+};
+
+class ShooterTest : public ::aos::testing::ControlLoopTest {
+
+ protected:
+ // Create a new instance of the test queue so that it invalidates the queue
+ // that it points to. Otherwise, we will have a pointer to shared memory that
+ // is no longer valid.
+ ShooterQueue shooter_queue_;
+
+ // Create a loop and simulation plant.
+ ShooterMotor shooter_motor_;
+ ShooterSimulation shooter_motor_plant_;
+
+ void Reinitialize(double position) {
+ shooter_motor_plant_.Reinitialize(position);
+ }
+
+ ShooterTest()
+ : shooter_queue_(
+ ".frc971.control_loops.shooter_queue", 0xcbf22ba9,
+ ".frc971.control_loops.shooter_queue.goal",
+ ".frc971.control_loops.shooter_queue.position",
+ ".frc971.control_loops.shooter_queue.output",
+ ".frc971.control_loops.shooter_queue.status"),
+ shooter_motor_(&shooter_queue_),
+ shooter_motor_plant_(0.2) {
+ }
+
+ void VerifyNearGoal() {
+ shooter_queue_.goal.FetchLatest();
+ shooter_queue_.position.FetchLatest();
+ double pos = shooter_motor_plant_.GetAbsolutePosition();
+ EXPECT_NEAR(shooter_queue_.goal->shot_power, pos, 1e-4);
+ }
+};
+
+TEST_F(ShooterTest, PowerConversion) {
+ const frc971::constants::Values &values = constants::GetValues();
+ // test a couple of values return the right thing
+ EXPECT_NEAR(0.254001, shooter_motor_.PowerToPosition(140.0), 0.00001);
+ EXPECT_NEAR(0.00058, shooter_motor_.PowerToPosition(0.53), 0.00001);
+ EXPECT_NEAR(0.095251238129837101, shooter_motor_.PowerToPosition(73.67),
+ 0.00001);
+
+ // value too large should get max
+ EXPECT_NEAR(values.shooter.upper_limit,
+ shooter_motor_.PowerToPosition(505050.99), 0.00001);
+ // negative values should zero
+ EXPECT_NEAR(0, shooter_motor_.PowerToPosition(-123.4), 0.00001);
+}
+
+// Test that PowerToPosition and PositionToPower are inverses of each other.
+// Note that PowerToPosition will cap position whereas PositionToPower will not
+// cap power.
+TEST_F(ShooterTest, InversePowerConversion) {
+ // Test a few values.
+ double power = 140.0;
+ double position = shooter_motor_.PowerToPosition(power);
+ EXPECT_NEAR(power, shooter_motor_.PositionToPower(position), 1e-5);
+ power = .53;
+ position = shooter_motor_.PowerToPosition(power);
+ EXPECT_NEAR(power, shooter_motor_.PositionToPower(position), 1e-5);
+ power = 71.971;
+ position = shooter_motor_.PowerToPosition(power);
+ EXPECT_NEAR(power, shooter_motor_.PositionToPower(position), 1e-5);
+}
+
+// Tests that the shooter zeros correctly and goes to a position.
+TEST_F(ShooterTest, GoesToValue) {
+ shooter_queue_.goal.MakeWithBuilder().shot_power(70.0).Send();
+ for (int i = 0; i < 200; ++i) {
+ shooter_motor_plant_.SendPositionMessage();
+ shooter_motor_.Iterate();
+ shooter_motor_plant_.Simulate();
+ SimulateTimestep(true);
+ }
+ // EXPECT_NEAR(0.0, shooter_motor_.GetPosition(), 0.01);
+ double pos = shooter_motor_plant_.GetAbsolutePosition();
+ EXPECT_NEAR(
+ shooter_motor_.PowerToPosition(shooter_queue_.goal->shot_power),
+ pos, 0.05);
+ EXPECT_EQ(ShooterMotor::STATE_READY, shooter_motor_.state());
+}
+
+// Tests that the shooter zeros correctly and goes to a position.
+TEST_F(ShooterTest, Fire) {
+ shooter_queue_.goal.MakeWithBuilder().shot_power(70.0).Send();
+ for (int i = 0; i < 120; ++i) {
+ shooter_motor_plant_.SendPositionMessage();
+ shooter_motor_.Iterate();
+ shooter_motor_plant_.Simulate();
+ SimulateTimestep(true);
+ }
+ EXPECT_EQ(ShooterMotor::STATE_READY, shooter_motor_.state());
+ shooter_queue_.goal.MakeWithBuilder()
+ .shot_power(35.0)
+ .shot_requested(true)
+ .Send();
+
+ bool hit_fire = false;
+ for (int i = 0; i < 400; ++i) {
+ shooter_motor_plant_.SendPositionMessage();
+ shooter_motor_.Iterate();
+ shooter_motor_plant_.Simulate();
+ SimulateTimestep(true);
+ if (shooter_motor_.state() == ShooterMotor::STATE_FIRE) {
+ if (!hit_fire) {
+ shooter_queue_.goal.MakeWithBuilder()
+ .shot_power(17.0)
+ .shot_requested(false)
+ .Send();
+ }
+ hit_fire = true;
+ }
+ }
+
+ double pos = shooter_motor_plant_.GetAbsolutePosition();
+ EXPECT_NEAR(
+ shooter_motor_.PowerToPosition(shooter_queue_.goal->shot_power),
+ pos, 0.05);
+ EXPECT_EQ(ShooterMotor::STATE_READY, shooter_motor_.state());
+ EXPECT_TRUE(hit_fire);
+}
+
+// Tests that the shooter zeros correctly and goes to a position.
+TEST_F(ShooterTest, FireLong) {
+ shooter_queue_.goal.MakeWithBuilder().shot_power(70.0).Send();
+ for (int i = 0; i < 150; ++i) {
+ shooter_motor_plant_.SendPositionMessage();
+ shooter_motor_.Iterate();
+ shooter_motor_plant_.Simulate();
+ SimulateTimestep(true);
+ }
+ EXPECT_EQ(ShooterMotor::STATE_READY, shooter_motor_.state());
+ shooter_queue_.goal.MakeWithBuilder().shot_requested(true).Send();
+
+ bool hit_fire = false;
+ for (int i = 0; i < 400; ++i) {
+ shooter_motor_plant_.SendPositionMessage();
+ shooter_motor_.Iterate();
+ shooter_motor_plant_.Simulate();
+ SimulateTimestep(true);
+ if (shooter_motor_.state() == ShooterMotor::STATE_FIRE) {
+ if (!hit_fire) {
+ shooter_queue_.goal.MakeWithBuilder()
+ .shot_requested(false)
+ .Send();
+ }
+ hit_fire = true;
+ }
+ }
+
+ double pos = shooter_motor_plant_.GetAbsolutePosition();
+ EXPECT_NEAR(shooter_motor_.PowerToPosition(shooter_queue_.goal->shot_power), pos, 0.05);
+ EXPECT_EQ(ShooterMotor::STATE_READY, shooter_motor_.state());
+ EXPECT_TRUE(hit_fire);
+}
+
+// Verifies that it doesn't try to go out too far if you give it a ridicilous
+// power.
+TEST_F(ShooterTest, LoadTooFar) {
+ shooter_queue_.goal.MakeWithBuilder().shot_power(500.0).Send();
+ for (int i = 0; i < 160; ++i) {
+ shooter_motor_plant_.SendPositionMessage();
+ shooter_motor_.Iterate();
+ shooter_motor_plant_.Simulate();
+ SimulateTimestep(true);
+ EXPECT_LT(
+ shooter_motor_plant_.GetAbsolutePosition(),
+ constants::GetValuesForTeam(kTestTeam).shooter.upper_limit);
+ }
+ EXPECT_EQ(ShooterMotor::STATE_READY, shooter_motor_.state());
+}
+
+// Tests that the shooter zeros correctly and goes to a position.
+TEST_F(ShooterTest, MoveGoal) {
+ shooter_queue_.goal.MakeWithBuilder().shot_power(70.0).Send();
+ for (int i = 0; i < 150; ++i) {
+ shooter_motor_plant_.SendPositionMessage();
+ shooter_motor_.Iterate();
+ shooter_motor_plant_.Simulate();
+ SimulateTimestep(true);
+ }
+ EXPECT_EQ(ShooterMotor::STATE_READY, shooter_motor_.state());
+ shooter_queue_.goal.MakeWithBuilder().shot_power(14.0).Send();
+
+ for (int i = 0; i < 100; ++i) {
+ shooter_motor_plant_.SendPositionMessage();
+ shooter_motor_.Iterate();
+ shooter_motor_plant_.Simulate();
+ SimulateTimestep(true);
+ }
+
+ double pos = shooter_motor_plant_.GetAbsolutePosition();
+ EXPECT_NEAR(
+ shooter_motor_.PowerToPosition(shooter_queue_.goal->shot_power),
+ pos, 0.05);
+ EXPECT_EQ(ShooterMotor::STATE_READY, shooter_motor_.state());
+}
+
+
+TEST_F(ShooterTest, Unload) {
+ shooter_queue_.goal.MakeWithBuilder().shot_power(70.0).Send();
+ for (int i = 0; i < 150; ++i) {
+ shooter_motor_plant_.SendPositionMessage();
+ shooter_motor_.Iterate();
+ shooter_motor_plant_.Simulate();
+ SimulateTimestep(true);
+ }
+ EXPECT_EQ(ShooterMotor::STATE_READY, shooter_motor_.state());
+ shooter_queue_.goal.MakeWithBuilder().unload_requested(true).Send();
+
+ for (int i = 0;
+ i < 800 && shooter_motor_.state() != ShooterMotor::STATE_READY_UNLOAD;
+ ++i) {
+ shooter_motor_plant_.SendPositionMessage();
+ shooter_motor_.Iterate();
+ shooter_motor_plant_.Simulate();
+ SimulateTimestep(true);
+ }
+
+ EXPECT_NEAR(constants::GetValues().shooter.upper_limit,
+ shooter_motor_plant_.GetAbsolutePosition(), 0.015);
+ EXPECT_EQ(ShooterMotor::STATE_READY_UNLOAD, shooter_motor_.state());
+}
+
+// Tests that it rezeros while unloading.
+TEST_F(ShooterTest, RezeroWhileUnloading) {
+ shooter_queue_.goal.MakeWithBuilder().shot_power(70.0).Send();
+ for (int i = 0; i < 150; ++i) {
+ shooter_motor_plant_.SendPositionMessage();
+ shooter_motor_.Iterate();
+ shooter_motor_plant_.Simulate();
+ SimulateTimestep(true);
+ }
+ EXPECT_EQ(ShooterMotor::STATE_READY, shooter_motor_.state());
+
+ shooter_motor_.shooter_.offset_ += 0.01;
+ for (int i = 0; i < 50; ++i) {
+ shooter_motor_plant_.SendPositionMessage();
+ shooter_motor_.Iterate();
+ shooter_motor_plant_.Simulate();
+ SimulateTimestep(true);
+ }
+
+ shooter_queue_.goal.MakeWithBuilder().unload_requested(true).Send();
+
+ for (int i = 0;
+ i < 800 && shooter_motor_.state() != ShooterMotor::STATE_READY_UNLOAD;
+ ++i) {
+ shooter_motor_plant_.SendPositionMessage();
+ shooter_motor_.Iterate();
+ shooter_motor_plant_.Simulate();
+ SimulateTimestep(true);
+ }
+
+ EXPECT_NEAR(constants::GetValues().shooter.upper_limit,
+ shooter_motor_plant_.GetAbsolutePosition(), 0.015);
+ EXPECT_EQ(ShooterMotor::STATE_READY_UNLOAD, shooter_motor_.state());
+}
+
+// Tests that the shooter zeros correctly and goes to a position.
+TEST_F(ShooterTest, UnloadWindupNegative) {
+ shooter_queue_.goal.MakeWithBuilder().shot_power(70.0).Send();
+ for (int i = 0; i < 150; ++i) {
+ shooter_motor_plant_.SendPositionMessage();
+ shooter_motor_.Iterate();
+ shooter_motor_plant_.Simulate();
+ SimulateTimestep(true);
+ }
+ EXPECT_EQ(ShooterMotor::STATE_READY, shooter_motor_.state());
+ shooter_queue_.goal.MakeWithBuilder().unload_requested(true).Send();
+
+ int kicked_delay = 20;
+ int capped_goal_count = 0;
+ for (int i = 0;
+ i < 800 && shooter_motor_.state() != ShooterMotor::STATE_READY_UNLOAD;
+ ++i) {
+ shooter_motor_plant_.SendPositionMessage();
+ shooter_motor_.Iterate();
+ if (shooter_motor_.state() == ShooterMotor::STATE_UNLOAD_MOVE) {
+ LOG(DEBUG, "State is UnloadMove\n");
+ --kicked_delay;
+ if (kicked_delay == 0) {
+ shooter_motor_.shooter_.mutable_R(0, 0) -= 100;
+ }
+ }
+ if (shooter_motor_.capped_goal() && kicked_delay < 0) {
+ ++capped_goal_count;
+ }
+ shooter_motor_plant_.Simulate();
+ SimulateTimestep(true);
+ }
+
+ EXPECT_NEAR(constants::GetValues().shooter.upper_limit,
+ shooter_motor_plant_.GetAbsolutePosition(), 0.05);
+ EXPECT_EQ(ShooterMotor::STATE_READY_UNLOAD, shooter_motor_.state());
+ EXPECT_LE(1, capped_goal_count);
+ EXPECT_GE(3, capped_goal_count);
+}
+
+// Tests that the shooter zeros correctly and goes to a position.
+TEST_F(ShooterTest, UnloadWindupPositive) {
+ shooter_queue_.goal.MakeWithBuilder().shot_power(70.0).Send();
+ for (int i = 0; i < 150; ++i) {
+ shooter_motor_plant_.SendPositionMessage();
+ shooter_motor_.Iterate();
+ shooter_motor_plant_.Simulate();
+ SimulateTimestep(true);
+ }
+ EXPECT_EQ(ShooterMotor::STATE_READY, shooter_motor_.state());
+ shooter_queue_.goal.MakeWithBuilder().unload_requested(true).Send();
+
+ int kicked_delay = 20;
+ int capped_goal_count = 0;
+ for (int i = 0;
+ i < 800 && shooter_motor_.state() != ShooterMotor::STATE_READY_UNLOAD;
+ ++i) {
+ shooter_motor_plant_.SendPositionMessage();
+ shooter_motor_.Iterate();
+ if (shooter_motor_.state() == ShooterMotor::STATE_UNLOAD_MOVE) {
+ LOG(DEBUG, "State is UnloadMove\n");
+ --kicked_delay;
+ if (kicked_delay == 0) {
+ shooter_motor_.shooter_.mutable_R(0, 0) += 0.1;
+ }
+ }
+ if (shooter_motor_.capped_goal() && kicked_delay < 0) {
+ ++capped_goal_count;
+ }
+ shooter_motor_plant_.Simulate();
+ SimulateTimestep(true);
+ }
+
+ EXPECT_NEAR(constants::GetValues().shooter.upper_limit,
+ shooter_motor_plant_.GetAbsolutePosition(), 0.05);
+ EXPECT_EQ(ShooterMotor::STATE_READY_UNLOAD, shooter_motor_.state());
+ EXPECT_LE(1, capped_goal_count);
+ EXPECT_GE(3, capped_goal_count);
+}
+
+double HallEffectMiddle(constants::Values::AnglePair pair) {
+ return (pair.lower_angle + pair.upper_angle) / 2.0;
+}
+
+// Tests that the shooter zeros correctly and goes to a position.
+TEST_F(ShooterTest, StartsOnDistal) {
+ Reinitialize(HallEffectMiddle(constants::GetValues().shooter.pusher_distal));
+ shooter_queue_.goal.MakeWithBuilder().shot_power(70.0).Send();
+ for (int i = 0; i < 200; ++i) {
+ shooter_motor_plant_.SendPositionMessage();
+ shooter_motor_.Iterate();
+ shooter_motor_plant_.Simulate();
+ SimulateTimestep(true);
+ }
+ // EXPECT_NEAR(0.0, shooter_motor_.GetPosition(), 0.01);
+ double pos = shooter_motor_plant_.GetAbsolutePosition();
+ EXPECT_NEAR(
+ shooter_motor_.PowerToPosition(shooter_queue_.goal->shot_power),
+ pos, 0.05);
+ EXPECT_EQ(ShooterMotor::STATE_READY, shooter_motor_.state());
+}
+
+
+// Tests that the shooter zeros correctly and goes to a position.
+TEST_F(ShooterTest, StartsOnProximal) {
+ Reinitialize(
+ HallEffectMiddle(constants::GetValues().shooter.pusher_proximal));
+ shooter_queue_.goal.MakeWithBuilder().shot_power(70.0).Send();
+ for (int i = 0; i < 300; ++i) {
+ shooter_motor_plant_.SendPositionMessage();
+ shooter_motor_.Iterate();
+ shooter_motor_plant_.Simulate();
+ SimulateTimestep(true);
+ }
+ // EXPECT_NEAR(0.0, shooter_motor_.GetPosition(), 0.01);
+ double pos = shooter_motor_plant_.GetAbsolutePosition();
+ EXPECT_NEAR(
+ shooter_motor_.PowerToPosition(shooter_queue_.goal->shot_power),
+ pos, 0.05);
+ EXPECT_EQ(ShooterMotor::STATE_READY, shooter_motor_.state());
+}
+
+class ShooterZeroingTest : public ShooterTest,
+ public ::testing::WithParamInterface<
+ ::std::tr1::tuple<bool, bool, bool, double>> {};
+
+TEST_P(ShooterZeroingTest, AllDisparateStartingZero) {
+ bool latch = ::std::tr1::get<0>(GetParam());
+ bool brake = ::std::tr1::get<1>(GetParam());
+ bool plunger_back = ::std::tr1::get<2>(GetParam());
+ double start_pos = ::std::tr1::get<3>(GetParam());
+ // flag to initialize test
+ //printf("@@@@ l= %d b= %d p= %d s= %.3f\n",
+ // latch, brake, plunger_back, start_pos);
+ bool initialized = false;
+ Reinitialize(start_pos);
+ shooter_queue_.goal.MakeWithBuilder().shot_power(120.0).Send();
+ for (int i = 0; i < 200; ++i) {
+ shooter_motor_plant_.SendPositionMessage(!initialized, plunger_back, latch, brake);
+ initialized = true;
+ shooter_motor_.Iterate();
+ shooter_motor_plant_.Simulate();
+ SimulateTimestep(true);
+ }
+ // EXPECT_NEAR(0.0, shooter_motor_.GetPosition(), 0.01);
+ double pos = shooter_motor_plant_.GetAbsolutePosition();
+ EXPECT_NEAR(
+ shooter_motor_.PowerToPosition(shooter_queue_.goal->shot_power),
+ pos, 0.05);
+ ASSERT_EQ(ShooterMotor::STATE_READY, shooter_motor_.state());
+}
+
+INSTANTIATE_TEST_CASE_P(
+ ShooterZeroingTest, ShooterZeroingTest,
+ ::testing::Combine(
+ ::testing::Bool(), ::testing::Bool(), ::testing::Bool(),
+ ::testing::Values(
+ 0.05,
+ constants::GetValuesForTeam(kTestTeam).shooter.upper_limit - 0.05,
+ HallEffectMiddle(constants::GetValuesForTeam(kTestTeam)
+ .shooter.pusher_proximal),
+ HallEffectMiddle(constants::GetValuesForTeam(kTestTeam)
+ .shooter.pusher_distal),
+ constants::GetValuesForTeam(kTestTeam)
+ .shooter.latch_max_safe_position -
+ 0.001)));
+
+// TODO(austin): Slip the encoder somewhere.
+
+// TODO(austin): Test all the timeouts...
+
+} // namespace testing
+} // namespace control_loops
+} // namespace frc971
diff --git a/y2014/control_loops/shooter/shooter_main.cc b/y2014/control_loops/shooter/shooter_main.cc
new file mode 100644
index 0000000..31b24bf
--- /dev/null
+++ b/y2014/control_loops/shooter/shooter_main.cc
@@ -0,0 +1,11 @@
+#include "y2014/control_loops/shooter/shooter.h"
+
+#include "aos/linux_code/init.h"
+
+int main() {
+ ::aos::Init();
+ frc971::control_loops::ShooterMotor shooter;
+ shooter.Run();
+ ::aos::Cleanup();
+ return 0;
+}
diff --git a/y2014/control_loops/shooter/shooter_motor_plant.cc b/y2014/control_loops/shooter/shooter_motor_plant.cc
new file mode 100644
index 0000000..7da6407
--- /dev/null
+++ b/y2014/control_loops/shooter/shooter_motor_plant.cc
@@ -0,0 +1,77 @@
+#include "y2014/control_loops/shooter/shooter_motor_plant.h"
+
+#include <vector>
+
+#include "frc971/control_loops/state_feedback_loop.h"
+
+namespace frc971 {
+namespace control_loops {
+
+StateFeedbackPlantCoefficients<3, 1, 1> MakeSprungShooterPlantCoefficients() {
+ Eigen::Matrix<double, 3, 3> A;
+ A << 0.999391114909, 0.00811316740387, 7.59766686183e-05, -0.113584343654, 0.64780421498, 0.0141730519709, 0.0, 0.0, 1.0;
+ Eigen::Matrix<double, 3, 1> B;
+ B << 0.0, 0.0, 1.0;
+ Eigen::Matrix<double, 1, 3> C;
+ C << 1.0, 0.0, 0.0;
+ Eigen::Matrix<double, 1, 1> D;
+ D << 0.0;
+ Eigen::Matrix<double, 1, 1> U_max;
+ U_max << 12.0;
+ Eigen::Matrix<double, 1, 1> U_min;
+ U_min << -12.0;
+ return StateFeedbackPlantCoefficients<3, 1, 1>(A, B, C, D, U_max, U_min);
+}
+
+StateFeedbackPlantCoefficients<3, 1, 1> MakeShooterPlantCoefficients() {
+ Eigen::Matrix<double, 3, 3> A;
+ A << 1.0, 0.00811505488455, 7.59852687598e-05, 0.0, 0.648331305446, 0.0141763492481, 0.0, 0.0, 1.0;
+ Eigen::Matrix<double, 3, 1> B;
+ B << 0.0, 0.0, 1.0;
+ Eigen::Matrix<double, 1, 3> C;
+ C << 1.0, 0.0, 0.0;
+ Eigen::Matrix<double, 1, 1> D;
+ D << 0.0;
+ Eigen::Matrix<double, 1, 1> U_max;
+ U_max << 12.0;
+ Eigen::Matrix<double, 1, 1> U_min;
+ U_min << -12.0;
+ return StateFeedbackPlantCoefficients<3, 1, 1>(A, B, C, D, U_max, U_min);
+}
+
+StateFeedbackController<3, 1, 1> MakeSprungShooterController() {
+ Eigen::Matrix<double, 3, 1> L;
+ L << 1.64719532989, 57.0572680832, 636.74290365;
+ Eigen::Matrix<double, 1, 3> K;
+ K << 450.571849185, 11.8404918938, 0.997195329889;
+ Eigen::Matrix<double, 3, 3> A_inv;
+ A_inv << 0.99918700445, -0.0125139220268, 0.00010144556732, 0.175194908375, 1.54148211958, -0.0218608169185, 0.0, 0.0, 1.0;
+ return StateFeedbackController<3, 1, 1>(L, K, A_inv, MakeSprungShooterPlantCoefficients());
+}
+
+StateFeedbackController<3, 1, 1> MakeShooterController() {
+ Eigen::Matrix<double, 3, 1> L;
+ L << 1.64833130545, 57.2417604572, 636.668851906;
+ Eigen::Matrix<double, 1, 3> K;
+ K << 349.173113146, 8.65077618169, 0.848331305446;
+ Eigen::Matrix<double, 3, 3> A_inv;
+ A_inv << 1.0, -0.0125168333171, 0.000101457731824, 0.0, 1.5424212769, -0.021865902709, 0.0, 0.0, 1.0;
+ return StateFeedbackController<3, 1, 1>(L, K, A_inv, MakeShooterPlantCoefficients());
+}
+
+StateFeedbackPlant<3, 1, 1> MakeShooterPlant() {
+ ::std::vector< ::std::unique_ptr<StateFeedbackPlantCoefficients<3, 1, 1>>> plants(2);
+ plants[0] = ::std::unique_ptr<StateFeedbackPlantCoefficients<3, 1, 1>>(new StateFeedbackPlantCoefficients<3, 1, 1>(MakeSprungShooterPlantCoefficients()));
+ plants[1] = ::std::unique_ptr<StateFeedbackPlantCoefficients<3, 1, 1>>(new StateFeedbackPlantCoefficients<3, 1, 1>(MakeShooterPlantCoefficients()));
+ return StateFeedbackPlant<3, 1, 1>(&plants);
+}
+
+StateFeedbackLoop<3, 1, 1> MakeShooterLoop() {
+ ::std::vector< ::std::unique_ptr<StateFeedbackController<3, 1, 1>>> controllers(2);
+ controllers[0] = ::std::unique_ptr<StateFeedbackController<3, 1, 1>>(new StateFeedbackController<3, 1, 1>(MakeSprungShooterController()));
+ controllers[1] = ::std::unique_ptr<StateFeedbackController<3, 1, 1>>(new StateFeedbackController<3, 1, 1>(MakeShooterController()));
+ return StateFeedbackLoop<3, 1, 1>(&controllers);
+}
+
+} // namespace control_loops
+} // namespace frc971
diff --git a/y2014/control_loops/shooter/shooter_motor_plant.h b/y2014/control_loops/shooter/shooter_motor_plant.h
new file mode 100644
index 0000000..45d6ed1
--- /dev/null
+++ b/y2014/control_loops/shooter/shooter_motor_plant.h
@@ -0,0 +1,28 @@
+#ifndef Y2014_CONTROL_LOOPS_SHOOTER_SHOOTER_MOTOR_PLANT_H_
+#define Y2014_CONTROL_LOOPS_SHOOTER_SHOOTER_MOTOR_PLANT_H_
+
+#include "frc971/control_loops/state_feedback_loop.h"
+
+namespace frc971 {
+namespace control_loops {
+static constexpr double kMaxExtension = 0.323850;
+
+static constexpr double kSpringConstant = 2800.000000;
+
+
+StateFeedbackPlantCoefficients<3, 1, 1> MakeSprungShooterPlantCoefficients();
+
+StateFeedbackController<3, 1, 1> MakeSprungShooterController();
+
+StateFeedbackPlantCoefficients<3, 1, 1> MakeShooterPlantCoefficients();
+
+StateFeedbackController<3, 1, 1> MakeShooterController();
+
+StateFeedbackPlant<3, 1, 1> MakeShooterPlant();
+
+StateFeedbackLoop<3, 1, 1> MakeShooterLoop();
+
+} // namespace control_loops
+} // namespace frc971
+
+#endif // Y2014_CONTROL_LOOPS_SHOOTER_SHOOTER_MOTOR_PLANT_H_
diff --git a/y2014/control_loops/shooter/unaugmented_shooter_motor_plant.cc b/y2014/control_loops/shooter/unaugmented_shooter_motor_plant.cc
new file mode 100644
index 0000000..8311948
--- /dev/null
+++ b/y2014/control_loops/shooter/unaugmented_shooter_motor_plant.cc
@@ -0,0 +1,77 @@
+#include "y2014/control_loops/shooter/unaugmented_shooter_motor_plant.h"
+
+#include <vector>
+
+#include "frc971/control_loops/state_feedback_loop.h"
+
+namespace frc971 {
+namespace control_loops {
+
+StateFeedbackPlantCoefficients<2, 1, 1> MakeRawSprungShooterPlantCoefficients() {
+ Eigen::Matrix<double, 2, 2> A;
+ A << 0.999391114909, 0.00811316740387, -0.113584343654, 0.64780421498;
+ Eigen::Matrix<double, 2, 1> B;
+ B << 7.59766686183e-05, 0.0141730519709;
+ Eigen::Matrix<double, 1, 2> C;
+ C << 1, 0;
+ Eigen::Matrix<double, 1, 1> D;
+ D << 0;
+ Eigen::Matrix<double, 1, 1> U_max;
+ U_max << 12.0;
+ Eigen::Matrix<double, 1, 1> U_min;
+ U_min << -12.0;
+ return StateFeedbackPlantCoefficients<2, 1, 1>(A, B, C, D, U_max, U_min);
+}
+
+StateFeedbackPlantCoefficients<2, 1, 1> MakeRawShooterPlantCoefficients() {
+ Eigen::Matrix<double, 2, 2> A;
+ A << 1.0, 0.00811505488455, 0.0, 0.648331305446;
+ Eigen::Matrix<double, 2, 1> B;
+ B << 7.59852687598e-05, 0.0141763492481;
+ Eigen::Matrix<double, 1, 2> C;
+ C << 1, 0;
+ Eigen::Matrix<double, 1, 1> D;
+ D << 0;
+ Eigen::Matrix<double, 1, 1> U_max;
+ U_max << 12.0;
+ Eigen::Matrix<double, 1, 1> U_min;
+ U_min << -12.0;
+ return StateFeedbackPlantCoefficients<2, 1, 1>(A, B, C, D, U_max, U_min);
+}
+
+StateFeedbackController<2, 1, 1> MakeRawSprungShooterController() {
+ Eigen::Matrix<double, 2, 1> L;
+ L << 1.54719532989, 43.9345489758;
+ Eigen::Matrix<double, 1, 2> K;
+ K << 2126.06977433, 41.3223370936;
+ Eigen::Matrix<double, 2, 2> A_inv;
+ A_inv << 0.99918700445, -0.0125139220268, 0.175194908375, 1.54148211958;
+ return StateFeedbackController<2, 1, 1>(L, K, A_inv, MakeRawSprungShooterPlantCoefficients());
+}
+
+StateFeedbackController<2, 1, 1> MakeRawShooterController() {
+ Eigen::Matrix<double, 2, 1> L;
+ L << 1.54833130545, 44.1155797675;
+ Eigen::Matrix<double, 1, 2> K;
+ K << 2133.83569145, 41.3499425476;
+ Eigen::Matrix<double, 2, 2> A_inv;
+ A_inv << 1.0, -0.0125168333171, 0.0, 1.5424212769;
+ return StateFeedbackController<2, 1, 1>(L, K, A_inv, MakeRawShooterPlantCoefficients());
+}
+
+StateFeedbackPlant<2, 1, 1> MakeRawShooterPlant() {
+ ::std::vector< ::std::unique_ptr<StateFeedbackPlantCoefficients<2, 1, 1>>> plants(2);
+ plants[0] = ::std::unique_ptr<StateFeedbackPlantCoefficients<2, 1, 1>>(new StateFeedbackPlantCoefficients<2, 1, 1>(MakeRawSprungShooterPlantCoefficients()));
+ plants[1] = ::std::unique_ptr<StateFeedbackPlantCoefficients<2, 1, 1>>(new StateFeedbackPlantCoefficients<2, 1, 1>(MakeRawShooterPlantCoefficients()));
+ return StateFeedbackPlant<2, 1, 1>(&plants);
+}
+
+StateFeedbackLoop<2, 1, 1> MakeRawShooterLoop() {
+ ::std::vector< ::std::unique_ptr<StateFeedbackController<2, 1, 1>>> controllers(2);
+ controllers[0] = ::std::unique_ptr<StateFeedbackController<2, 1, 1>>(new StateFeedbackController<2, 1, 1>(MakeRawSprungShooterController()));
+ controllers[1] = ::std::unique_ptr<StateFeedbackController<2, 1, 1>>(new StateFeedbackController<2, 1, 1>(MakeRawShooterController()));
+ return StateFeedbackLoop<2, 1, 1>(&controllers);
+}
+
+} // namespace control_loops
+} // namespace frc971
diff --git a/y2014/control_loops/shooter/unaugmented_shooter_motor_plant.h b/y2014/control_loops/shooter/unaugmented_shooter_motor_plant.h
new file mode 100644
index 0000000..5b7de85
--- /dev/null
+++ b/y2014/control_loops/shooter/unaugmented_shooter_motor_plant.h
@@ -0,0 +1,24 @@
+#ifndef Y2014_CONTROL_LOOPS_SHOOTER_UNAUGMENTED_SHOOTER_MOTOR_PLANT_H_
+#define Y2014_CONTROL_LOOPS_SHOOTER_UNAUGMENTED_SHOOTER_MOTOR_PLANT_H_
+
+#include "frc971/control_loops/state_feedback_loop.h"
+
+namespace frc971 {
+namespace control_loops {
+
+StateFeedbackPlantCoefficients<2, 1, 1> MakeRawSprungShooterPlantCoefficients();
+
+StateFeedbackController<2, 1, 1> MakeRawSprungShooterController();
+
+StateFeedbackPlantCoefficients<2, 1, 1> MakeRawShooterPlantCoefficients();
+
+StateFeedbackController<2, 1, 1> MakeRawShooterController();
+
+StateFeedbackPlant<2, 1, 1> MakeRawShooterPlant();
+
+StateFeedbackLoop<2, 1, 1> MakeRawShooterLoop();
+
+} // namespace control_loops
+} // namespace frc971
+
+#endif // Y2014_CONTROL_LOOPS_SHOOTER_UNAUGMENTED_SHOOTER_MOTOR_PLANT_H_
diff --git a/y2014/control_loops/update_arm.sh b/y2014/control_loops/update_arm.sh
new file mode 100755
index 0000000..c4c1ab9
--- /dev/null
+++ b/y2014/control_loops/update_arm.sh
@@ -0,0 +1,10 @@
+#!/bin/bash
+#
+# Updates the arm controllers (for both robots).
+
+cd $(dirname $0)
+
+./python/arm.py fridge/arm_motor_plant.cc \
+ fridge/arm_motor_plant.h \
+ fridge/integral_arm_plant.cc \
+ fridge/integral_arm_plant.h
diff --git a/y2014/control_loops/update_claw.sh b/y2014/control_loops/update_claw.sh
new file mode 100755
index 0000000..2800d2a
--- /dev/null
+++ b/y2014/control_loops/update_claw.sh
@@ -0,0 +1,8 @@
+#!/bin/bash
+#
+# Updates the claw controllers.
+
+cd $(dirname $0)
+
+./python/claw.py claw/claw_motor_plant.h \
+ claw/claw_motor_plant.cc
diff --git a/y2014/control_loops/update_drivetrain.sh b/y2014/control_loops/update_drivetrain.sh
new file mode 100755
index 0000000..bad1074
--- /dev/null
+++ b/y2014/control_loops/update_drivetrain.sh
@@ -0,0 +1,10 @@
+#!/bin/bash
+#
+# Updates the drivetrain controllers (for both robots).
+
+cd $(dirname $0)
+
+./python/drivetrain.py drivetrain/drivetrain_dog_motor_plant.h \
+ drivetrain/drivetrain_dog_motor_plant.cc \
+ drivetrain/drivetrain_clutch_motor_plant.h \
+ drivetrain/drivetrain_clutch_motor_plant.cc
diff --git a/y2014/control_loops/update_polydrivetrain.sh b/y2014/control_loops/update_polydrivetrain.sh
new file mode 100755
index 0000000..2e2748e
--- /dev/null
+++ b/y2014/control_loops/update_polydrivetrain.sh
@@ -0,0 +1,12 @@
+#!/bin/bash
+#
+# Updates the polydrivetrain controllers (for both robots) and CIM models.
+
+cd $(dirname $0)
+
+./python/polydrivetrain.py drivetrain/polydrivetrain_dog_motor_plant.h \
+ drivetrain/polydrivetrain_dog_motor_plant.cc \
+ drivetrain/polydrivetrain_clutch_motor_plant.h \
+ drivetrain/polydrivetrain_clutch_motor_plant.cc \
+ drivetrain/polydrivetrain_cim_plant.h \
+ drivetrain/polydrivetrain_cim_plant.cc
diff --git a/y2014/control_loops/update_shooter.sh b/y2014/control_loops/update_shooter.sh
new file mode 100755
index 0000000..a9c5807
--- /dev/null
+++ b/y2014/control_loops/update_shooter.sh
@@ -0,0 +1,10 @@
+#!/bin/bash
+#
+# Updates the shooter controller.
+
+cd $(dirname $0)
+
+./python/shooter.py shooter/shooter_motor_plant.h \
+ shooter/shooter_motor_plant.cc \
+ shooter/unaugmented_shooter_motor_plant.h \
+ shooter/unaugmented_shooter_motor_plant.cc
diff --git a/y2014/joystick_reader.cc b/y2014/joystick_reader.cc
new file mode 100644
index 0000000..8bbf452
--- /dev/null
+++ b/y2014/joystick_reader.cc
@@ -0,0 +1,529 @@
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <math.h>
+
+#include "aos/linux_code/init.h"
+#include "aos/prime/input/joystick_input.h"
+#include "aos/common/input/driver_station_data.h"
+#include "aos/common/logging/logging.h"
+#include "aos/common/util/log_interval.h"
+#include "aos/common/time.h"
+#include "aos/common/actions/actions.h"
+
+#include "y2014/control_loops/drivetrain/drivetrain.q.h"
+#include "y2014/constants.h"
+#include "frc971/queues/gyro.q.h"
+#include "frc971/autonomous/auto.q.h"
+#include "y2014/control_loops/claw/claw.q.h"
+#include "y2014/control_loops/shooter/shooter.q.h"
+#include "y2014/actors/shoot_actor.h"
+
+using ::frc971::control_loops::drivetrain_queue;
+using ::frc971::sensors::gyro_reading;
+
+using ::aos::input::driver_station::ButtonLocation;
+using ::aos::input::driver_station::JoystickAxis;
+using ::aos::input::driver_station::ControlBit;
+
+#define OLD_DS 0
+
+namespace frc971 {
+namespace input {
+namespace joysticks {
+
+const ButtonLocation kDriveControlLoopEnable1(1, 7),
+ kDriveControlLoopEnable2(1, 11);
+const JoystickAxis kSteeringWheel(1, 1), kDriveThrottle(2, 2);
+const ButtonLocation kShiftHigh(2, 1), kShiftLow(2, 3);
+const ButtonLocation kQuickTurn(1, 5);
+
+const ButtonLocation kCatch(3, 10);
+
+#if OLD_DS
+const ButtonLocation kFire(3, 11);
+const ButtonLocation kUnload(1, 4);
+const ButtonLocation kReload(1, 2);
+
+const ButtonLocation kRollersOut(3, 12);
+const ButtonLocation kRollersIn(3, 7);
+
+const ButtonLocation kTuck(3, 9);
+const ButtonLocation kIntakePosition(3, 8);
+const ButtonLocation kIntakeOpenPosition(3, 10);
+const ButtonLocation kVerticalTuck(3, 1);
+const JoystickAxis kFlipRobot(3, 3);
+
+const ButtonLocation kLongShot(3, 5);
+const ButtonLocation kCloseShot(3, 2);
+const ButtonLocation kFenderShot(3, 3);
+const ButtonLocation kTrussShot(2, 11);
+const ButtonLocation kHumanPlayerShot(3, 2);
+#else
+const ButtonLocation kFire(3, 9);
+const ButtonLocation kUnload(1, 4);
+const ButtonLocation kReload(1, 2);
+
+const ButtonLocation kRollersOut(3, 8);
+const ButtonLocation kRollersIn(3, 3);
+
+const ButtonLocation kTuck(3, 4);
+const ButtonLocation kIntakePosition(3, 5);
+const ButtonLocation kIntakeOpenPosition(3, 11);
+const ButtonLocation kVerticalTuck(2, 6);
+const JoystickAxis kFlipRobot(3, 3);
+
+const ButtonLocation kLongShot(3, 7);
+const ButtonLocation kCloseShot(3, 6);
+const ButtonLocation kFenderShot(3, 2);
+const ButtonLocation kTrussShot(2, 11);
+const ButtonLocation kHumanPlayerShot(3, 1);
+#endif
+
+const ButtonLocation kUserLeft(2, 7);
+const ButtonLocation kUserRight(2, 10);
+
+const JoystickAxis kAdjustClawGoal(3, 2);
+const JoystickAxis kAdjustClawSeparation(3, 1);
+
+struct ClawGoal {
+ double angle;
+ double separation;
+};
+
+struct ShotGoal {
+ ClawGoal claw;
+ double shot_power;
+ double velocity_compensation;
+ double intake_power;
+};
+
+const double kIntakePower = 4.0;
+// In case we have to quickly adjust it.
+const double kGrabSeparation = 0;
+const double kShootSeparation = 0.11 + kGrabSeparation;
+
+const ClawGoal kTuckGoal = {-2.273474, -0.749484};
+const ClawGoal kVerticalTuckGoal = {0, kGrabSeparation};
+const ClawGoal kIntakeGoal = {-2.24, kGrabSeparation};
+const ClawGoal kIntakeOpenGoal = {-2.0, 1.1};
+
+// TODO(austin): Tune these by hand...
+const ClawGoal kFlippedTuckGoal = {2.733474, -0.75};
+const ClawGoal kFlippedIntakeGoal = {2.0, kGrabSeparation};
+const ClawGoal kFlippedIntakeOpenGoal = {0.95, 1.0};
+
+// 34" between near edge of colored line and rear edge of bumper.
+// Only works running?
+const ShotGoal kLongShotGoal = {
+ {-1.08, kShootSeparation}, 145, 0.04, kIntakePower};
+// old 34" {-1.06, kShootSeparation}, 140, 0.04, kIntakePower};
+const ShotGoal kFlippedLongShotGoal = {
+ {0.96, kShootSeparation}, 145, 0.09, kIntakePower};
+// old 34" {0.96, kShootSeparation}, 140, 0.09, kIntakePower};
+
+// 78" between near edge of colored line and rear edge of bumper.
+const ShotGoal kCloseShotGoal = {
+ {-0.95, kShootSeparation}, 105, 0.2, kIntakePower};
+// 3/4" plunger {-0.90, kShootSeparation}, 105, 0.2, kIntakePower};
+const ShotGoal kFlippedMediumShotGoal = {
+ {0.865, kShootSeparation}, 120, 0.2, kIntakePower};
+// 3/4" plunger {0.80, kShootSeparation}, 105, 0.2, kIntakePower};
+
+// Shot from the fender.
+const ShotGoal kFenderShotGoal = {
+ {-0.68, kShootSeparation}, 115.0, 0.0, kIntakePower};
+const ShotGoal kFlippedShortShotGoal = {
+ {0.63, kShootSeparation}, 115.0, 0.0, kIntakePower};
+
+const ShotGoal kHumanShotGoal = {
+ {-0.90, kShootSeparation}, 140, 0.04, kIntakePower};
+const ShotGoal kFlippedHumanShotGoal = {
+ {0.90, kShootSeparation}, 140, 0, kIntakePower};
+const ShotGoal kTrussShotGoal = {
+ {-0.68, kShootSeparation}, 88.0, 0.4, kIntakePower};
+const ShotGoal kFlippedTrussShotGoal = {
+ {0.68, kShootSeparation}, 92.0, 0.4, kIntakePower};
+
+const ShotGoal kFlippedDemoShotGoal = {
+ {1.0, kShootSeparation}, 65.0, 0.0, kIntakePower};
+const ShotGoal kDemoShotGoal = {
+ {-1.0, kShootSeparation}, 50.0, 0.0, kIntakePower};
+
+const ClawGoal k254PassGoal = {-1.95, kGrabSeparation};
+const ClawGoal kFlipped254PassGoal = {1.96, kGrabSeparation};
+
+class Reader : public ::aos::input::JoystickInput {
+ public:
+ Reader()
+ : is_high_gear_(false),
+ shot_power_(80.0),
+ goal_angle_(0.0),
+ separation_angle_(kGrabSeparation),
+ velocity_compensation_(0.0),
+ intake_power_(0.0),
+ was_running_(false) {}
+
+ void RunIteration(const ::aos::input::driver_station::Data &data) override {
+ bool last_auto_running = auto_running_;
+ auto_running_ = data.GetControlBit(ControlBit::kAutonomous) &&
+ data.GetControlBit(ControlBit::kEnabled);
+ if (auto_running_ != last_auto_running) {
+ if (auto_running_) {
+ StartAuto();
+ } else {
+ StopAuto();
+ }
+ }
+
+ if (!data.GetControlBit(ControlBit::kAutonomous)) {
+ HandleDrivetrain(data);
+ HandleTeleop(data);
+ }
+ }
+
+ void HandleDrivetrain(const ::aos::input::driver_station::Data &data) {
+ bool is_control_loop_driving = false;
+ double left_goal = 0.0;
+ double right_goal = 0.0;
+ const double wheel = -data.GetAxis(kSteeringWheel);
+ const double throttle = -data.GetAxis(kDriveThrottle);
+ const double kThrottleGain = 1.0 / 2.5;
+ if (false && (data.IsPressed(kDriveControlLoopEnable1) ||
+ data.IsPressed(kDriveControlLoopEnable2))) {
+ // TODO(austin): Static sucks!
+ static double distance = 0.0;
+ static double angle = 0.0;
+ static double filtered_goal_distance = 0.0;
+ if (data.PosEdge(kDriveControlLoopEnable1) ||
+ data.PosEdge(kDriveControlLoopEnable2)) {
+ if (drivetrain_queue.position.FetchLatest() &&
+ gyro_reading.FetchLatest()) {
+ distance = (drivetrain_queue.position->left_encoder +
+ drivetrain_queue.position->right_encoder) /
+ 2.0 -
+ throttle * kThrottleGain / 2.0;
+ angle = gyro_reading->angle;
+ filtered_goal_distance = distance;
+ }
+ }
+ is_control_loop_driving = true;
+
+ // const double gyro_angle = Gyro.View().angle;
+ const double goal_theta = angle - wheel * 0.27;
+ const double goal_distance = distance + throttle * kThrottleGain;
+ const double robot_width = 22.0 / 100.0 * 2.54;
+ const double kMaxVelocity = 0.6;
+ if (goal_distance > kMaxVelocity * 0.02 + filtered_goal_distance) {
+ filtered_goal_distance += kMaxVelocity * 0.02;
+ } else if (goal_distance <
+ -kMaxVelocity * 0.02 + filtered_goal_distance) {
+ filtered_goal_distance -= kMaxVelocity * 0.02;
+ } else {
+ filtered_goal_distance = goal_distance;
+ }
+ left_goal = filtered_goal_distance - robot_width * goal_theta / 2.0;
+ right_goal = filtered_goal_distance + robot_width * goal_theta / 2.0;
+ is_high_gear_ = false;
+
+ LOG(DEBUG, "Left goal %f Right goal %f\n", left_goal, right_goal);
+ }
+ if (!drivetrain_queue.goal.MakeWithBuilder()
+ .steering(wheel)
+ .throttle(throttle)
+ .highgear(is_high_gear_)
+ .quickturn(data.IsPressed(kQuickTurn))
+ .control_loop_driving(is_control_loop_driving)
+ .left_goal(left_goal)
+ .right_goal(right_goal)
+ .left_velocity_goal(0)
+ .right_velocity_goal(0)
+ .Send()) {
+ LOG(WARNING, "sending stick values failed\n");
+ }
+ if (data.PosEdge(kShiftHigh)) {
+ is_high_gear_ = false;
+ }
+ if (data.PosEdge(kShiftLow)) {
+ is_high_gear_ = true;
+ }
+ }
+
+ void SetGoal(ClawGoal goal) {
+ goal_angle_ = goal.angle;
+ separation_angle_ = goal.separation;
+ moving_for_shot_ = false;
+ velocity_compensation_ = 0.0;
+ intake_power_ = 0.0;
+ }
+
+ void SetGoal(ShotGoal goal) {
+ goal_angle_ = goal.claw.angle;
+ shot_separation_angle_ = goal.claw.separation;
+ separation_angle_ = kGrabSeparation;
+ moving_for_shot_ = true;
+ shot_power_ = goal.shot_power;
+ velocity_compensation_ = goal.velocity_compensation;
+ intake_power_ = goal.intake_power;
+ }
+
+ void HandleTeleop(const ::aos::input::driver_station::Data &data) {
+ if (!data.GetControlBit(ControlBit::kEnabled)) {
+ action_queue_.CancelAllActions();
+ LOG(DEBUG, "Canceling\n");
+ }
+ if (data.IsPressed(kRollersIn) || data.IsPressed(kRollersOut)) {
+ intake_power_ = 0.0;
+ separation_angle_ = kGrabSeparation;
+ moving_for_shot_ = false;
+ }
+
+ static const double kAdjustClawGoalDeadband = 0.08;
+ double claw_goal_adjust = data.GetAxis(kAdjustClawGoal);
+ if (OLD_DS || ::std::abs(claw_goal_adjust) < kAdjustClawGoalDeadband) {
+ claw_goal_adjust = 0;
+ } else {
+ claw_goal_adjust = (claw_goal_adjust -
+ ((claw_goal_adjust < 0) ? -kAdjustClawGoalDeadband
+ : kAdjustClawGoalDeadband)) *
+ 0.035;
+ }
+ double claw_separation_adjust = data.GetAxis(kAdjustClawSeparation);
+ if (OLD_DS ||
+ ::std::abs(claw_separation_adjust) < kAdjustClawGoalDeadband) {
+ claw_separation_adjust = 0;
+ } else {
+ claw_separation_adjust =
+ (claw_separation_adjust -
+ ((claw_separation_adjust < 0) ? -kAdjustClawGoalDeadband
+ : kAdjustClawGoalDeadband)) *
+ -0.035;
+ }
+
+#if OLD_DS
+ if (data.IsPressed(kFenderShot)) {
+#else
+ if (data.GetAxis(kFlipRobot) > 0.9) {
+#endif
+ claw_goal_adjust += claw_separation_adjust;
+ claw_goal_adjust *= -1;
+
+ if (data.IsPressed(kIntakeOpenPosition)) {
+ action_queue_.CancelAllActions();
+ LOG(DEBUG, "Canceling\n");
+ SetGoal(kFlippedIntakeOpenGoal);
+ } else if (data.IsPressed(kIntakePosition)) {
+ action_queue_.CancelAllActions();
+ LOG(DEBUG, "Canceling\n");
+ SetGoal(kFlippedIntakeGoal);
+ } else if (data.IsPressed(kVerticalTuck)) {
+ action_queue_.CancelAllActions();
+ LOG(DEBUG, "Canceling\n");
+ SetGoal(kVerticalTuckGoal);
+ } else if (data.IsPressed(kTuck)) {
+ action_queue_.CancelAllActions();
+ LOG(DEBUG, "Canceling\n");
+ SetGoal(kFlippedTuckGoal);
+ } else if (data.PosEdge(kLongShot)) {
+ action_queue_.CancelAllActions();
+ LOG(DEBUG, "Canceling\n");
+ SetGoal(kFlippedLongShotGoal);
+ } else if (data.PosEdge(kCloseShot)) {
+ action_queue_.CancelAllActions();
+ LOG(DEBUG, "Canceling\n");
+ SetGoal(kFlippedMediumShotGoal);
+ } else if (data.PosEdge(kFenderShot)) {
+ action_queue_.CancelAllActions();
+ LOG(DEBUG, "Canceling\n");
+ SetGoal(kFlippedShortShotGoal);
+ } else if (data.PosEdge(kHumanPlayerShot)) {
+ action_queue_.CancelAllActions();
+ LOG(DEBUG, "Canceling\n");
+ SetGoal(kFlippedHumanShotGoal);
+ } else if (data.PosEdge(kUserLeft)) {
+ action_queue_.CancelAllActions();
+ LOG(DEBUG, "Canceling\n");
+ SetGoal(kFlipped254PassGoal);
+ } else if (data.PosEdge(kUserRight)) {
+ action_queue_.CancelAllActions();
+ LOG(DEBUG, "Canceling\n");
+ SetGoal(kFlippedDemoShotGoal);
+ } else if (data.PosEdge(kTrussShot)) {
+ action_queue_.CancelAllActions();
+ LOG(DEBUG, "Canceling\n");
+ SetGoal(kFlippedTrussShotGoal);
+ }
+ } else {
+ if (data.IsPressed(kIntakeOpenPosition)) {
+ action_queue_.CancelAllActions();
+ LOG(DEBUG, "Canceling\n");
+ SetGoal(kIntakeOpenGoal);
+ } else if (data.IsPressed(kIntakePosition)) {
+ action_queue_.CancelAllActions();
+ LOG(DEBUG, "Canceling\n");
+ SetGoal(kIntakeGoal);
+ } else if (data.IsPressed(kVerticalTuck)) {
+ action_queue_.CancelAllActions();
+ LOG(DEBUG, "Canceling\n");
+ SetGoal(kVerticalTuckGoal);
+ } else if (data.IsPressed(kTuck)) {
+ action_queue_.CancelAllActions();
+ LOG(DEBUG, "Canceling\n");
+ SetGoal(kTuckGoal);
+ } else if (data.PosEdge(kLongShot)) {
+ action_queue_.CancelAllActions();
+ LOG(DEBUG, "Canceling\n");
+ SetGoal(kLongShotGoal);
+ } else if (data.PosEdge(kCloseShot)) {
+ action_queue_.CancelAllActions();
+ LOG(DEBUG, "Canceling\n");
+ SetGoal(kCloseShotGoal);
+ } else if (data.PosEdge(kFenderShot)) {
+ action_queue_.CancelAllActions();
+ LOG(DEBUG, "Canceling\n");
+ SetGoal(kFenderShotGoal);
+ } else if (data.PosEdge(kHumanPlayerShot)) {
+ action_queue_.CancelAllActions();
+ LOG(DEBUG, "Canceling\n");
+ SetGoal(kHumanShotGoal);
+ } else if (data.PosEdge(kUserLeft)) {
+ action_queue_.CancelAllActions();
+ LOG(DEBUG, "Canceling\n");
+ SetGoal(k254PassGoal);
+ } else if (data.PosEdge(kUserRight)) {
+ action_queue_.CancelAllActions();
+ LOG(DEBUG, "Canceling\n");
+ SetGoal(kDemoShotGoal);
+ } else if (data.PosEdge(kTrussShot)) {
+ action_queue_.CancelAllActions();
+ LOG(DEBUG, "Canceling\n");
+ SetGoal(kTrussShotGoal);
+ }
+ }
+
+ if (data.PosEdge(kFire)) {
+ action_queue_.EnqueueAction(actors::MakeShootAction());
+ } else if (data.NegEdge(kFire)) {
+ action_queue_.CancelCurrentAction();
+ }
+
+ action_queue_.Tick();
+ if (data.IsPressed(kUnload) || data.IsPressed(kReload)) {
+ action_queue_.CancelAllActions();
+ LOG(DEBUG, "Canceling\n");
+ intake_power_ = 0.0;
+ velocity_compensation_ = 0.0;
+ }
+
+ // Send out the claw and shooter goals if no actions are running.
+ if (!action_queue_.Running()) {
+ goal_angle_ += claw_goal_adjust;
+ separation_angle_ += claw_separation_adjust;
+
+ // If the action just ended, turn the intake off and stop velocity
+ // compensating.
+ if (was_running_) {
+ intake_power_ = 0.0;
+ velocity_compensation_ = 0.0;
+ }
+
+ control_loops::drivetrain_queue.status.FetchLatest();
+ double goal_angle = goal_angle_;
+ if (control_loops::drivetrain_queue.status.get()) {
+ goal_angle += SpeedToAngleOffset(
+ control_loops::drivetrain_queue.status->robot_speed);
+ } else {
+ LOG_INTERVAL(no_drivetrain_status_);
+ }
+
+ if (moving_for_shot_) {
+ auto &claw_status = control_loops::claw_queue.status;
+ claw_status.FetchLatest();
+ if (claw_status.get()) {
+ if (::std::abs(claw_status->bottom - goal_angle) < 0.2) {
+ moving_for_shot_ = false;
+ separation_angle_ = shot_separation_angle_;
+ }
+ }
+ }
+
+ double separation_angle = separation_angle_;
+
+ if (data.IsPressed(kCatch)) {
+ const double kCatchSeparation = 1.0;
+ goal_angle -= kCatchSeparation / 2.0;
+ separation_angle = kCatchSeparation;
+ }
+
+ bool intaking =
+ data.IsPressed(kRollersIn) || data.IsPressed(kIntakePosition) ||
+ data.IsPressed(kIntakeOpenPosition) || data.IsPressed(kCatch);
+ if (!control_loops::claw_queue.goal.MakeWithBuilder()
+ .bottom_angle(goal_angle)
+ .separation_angle(separation_angle)
+ .intake(intaking ? 12.0
+ : (data.IsPressed(kRollersOut) ? -12.0
+ : intake_power_))
+ .centering(intaking ? 12.0 : 0.0)
+ .Send()) {
+ LOG(WARNING, "sending claw goal failed\n");
+ }
+
+ if (!control_loops::shooter_queue.goal.MakeWithBuilder()
+ .shot_power(shot_power_)
+ .shot_requested(data.IsPressed(kFire))
+ .unload_requested(data.IsPressed(kUnload))
+ .load_requested(data.IsPressed(kReload))
+ .Send()) {
+ LOG(WARNING, "sending shooter goal failed\n");
+ }
+ }
+ was_running_ = action_queue_.Running();
+ }
+
+ double SpeedToAngleOffset(double speed) {
+ const frc971::constants::Values &values = frc971::constants::GetValues();
+ // scale speed to a [0.0-1.0] on something close to the max
+ // TODO(austin): Change the scale factor for different shots.
+ return (speed / values.drivetrain_max_speed) * velocity_compensation_;
+ }
+
+ private:
+ void StartAuto() {
+ LOG(INFO, "Starting auto mode\n");
+ ::frc971::autonomous::autonomous.MakeWithBuilder().run_auto(true).Send();
+ }
+
+ void StopAuto() {
+ LOG(INFO, "Stopping auto mode\n");
+ ::frc971::autonomous::autonomous.MakeWithBuilder().run_auto(false).Send();
+ }
+
+ bool is_high_gear_;
+ double shot_power_;
+ double goal_angle_;
+ double separation_angle_, shot_separation_angle_;
+ double velocity_compensation_;
+ double intake_power_;
+ bool was_running_;
+ bool moving_for_shot_ = false;
+
+ bool auto_running_ = false;
+
+ ::aos::common::actions::ActionQueue action_queue_;
+
+ ::aos::util::SimpleLogInterval no_drivetrain_status_ =
+ ::aos::util::SimpleLogInterval(::aos::time::Time::InSeconds(0.2), WARNING,
+ "no drivetrain status");
+};
+
+} // namespace joysticks
+} // namespace input
+} // namespace frc971
+
+int main() {
+ ::aos::Init();
+ ::frc971::input::joysticks::Reader reader;
+ reader.Run();
+ ::aos::Cleanup();
+}
diff --git a/y2014/prime/build.sh b/y2014/prime/build.sh
new file mode 100755
index 0000000..f83d9f4
--- /dev/null
+++ b/y2014/prime/build.sh
@@ -0,0 +1,5 @@
+#!/bin/bash
+
+cd $(dirname $0)
+
+exec ../../aos/build/build.py $0 prime y2014 prime.gyp "$@"
diff --git a/y2014/prime/compile_loop.sh b/y2014/prime/compile_loop.sh
new file mode 100755
index 0000000..de6b0d3
--- /dev/null
+++ b/y2014/prime/compile_loop.sh
@@ -0,0 +1,16 @@
+#!/bin/bash
+
+# Runs `build.sh all` and then waits for a file to be modified in a loop.
+# Useful for making changes to the code while continuously making sure they
+# compile.
+# Requires the util-linux and inotify-tools packages.
+
+chrt -i -p 0 $$
+ionice -c 3 -p $$
+
+while true; do
+ $(dirname $0)/build.sh all
+ echo 'compile_loop.sh: Waiting for a file modification...' 1>&2
+ inotifywait -e close_write -r aos frc971 bbb_cape
+ echo 'compile_loop.sh: Done waiting for a file modification' 1>&2
+done
diff --git a/y2014/prime/hot_goal_reader.cc b/y2014/prime/hot_goal_reader.cc
new file mode 100644
index 0000000..ffdb3a3
--- /dev/null
+++ b/y2014/prime/hot_goal_reader.cc
@@ -0,0 +1,108 @@
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <errno.h>
+#include <string.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include "aos/common/time.h"
+#include "aos/common/logging/queue_logging.h"
+#include "aos/common/logging/logging.h"
+#include "aos/linux_code/init.h"
+#include "aos/common/byteorder.h"
+
+#include "y2014/queues/hot_goal.q.h"
+
+int main() {
+ ::aos::InitNRT();
+
+ uint64_t left_count, right_count;
+ ::frc971::hot_goal.FetchLatest();
+ if (::frc971::hot_goal.get()) {
+ LOG_STRUCT(DEBUG, "starting with", *::frc971::hot_goal);
+ left_count = ::frc971::hot_goal->left_count;
+ right_count = ::frc971::hot_goal->left_count;
+ } else {
+ LOG(DEBUG, "no starting message\n");
+ left_count = right_count = 0;
+ }
+
+ int my_socket = -1;
+ while (true) {
+ if (my_socket == -1) {
+ my_socket = socket(AF_INET, SOCK_STREAM, 0);
+ if (my_socket == -1) {
+ PLOG(WARNING, "socket(AF_INET, SOCK_STREAM, 0) failed");
+ continue;
+ } else {
+ LOG(INFO, "opened socket (is %d)\n", my_socket);
+ sockaddr_in address, *sockaddr_pointer;
+ memset(&address, 0, sizeof(address));
+ address.sin_family = AF_INET;
+ address.sin_port = ::aos::hton<uint16_t>(1180);
+ sockaddr *address_pointer;
+ sockaddr_pointer = &address;
+ memcpy(&address_pointer, &sockaddr_pointer, sizeof(void *));
+ if (bind(my_socket, address_pointer, sizeof(address)) == -1) {
+ PLOG(WARNING, "bind(%d, %p, %zu) failed",
+ my_socket, &address, sizeof(address));
+ close(my_socket);
+ my_socket = -1;
+ continue;
+ }
+
+ if (listen(my_socket, 1) == -1) {
+ PLOG(WARNING, "listen(%d, 1) failed", my_socket);
+ close(my_socket);
+ my_socket = -1;
+ continue;
+ }
+ }
+ }
+
+ int connection = accept4(my_socket, nullptr, nullptr, SOCK_NONBLOCK);
+ if (connection == -1) {
+ PLOG(WARNING, "accept(%d, nullptr, nullptr) failed", my_socket);
+ continue;
+ }
+ LOG(INFO, "accepted (is %d)\n", connection);
+
+ while (connection != -1) {
+ fd_set fds;
+ FD_ZERO(&fds);
+ FD_SET(connection, &fds);
+ struct timeval timeout_timeval =
+ ::aos::time::Time::InSeconds(1).ToTimeval();
+ switch (
+ select(connection + 1, &fds, nullptr, nullptr, &timeout_timeval)) {
+ case 1: {
+ uint8_t data;
+ ssize_t read_bytes = read(connection, &data, sizeof(data));
+ if (read_bytes != sizeof(data)) {
+ LOG(WARNING, "read %zd bytes instead of %zd\n", read_bytes,
+ sizeof(data));
+ break;
+ }
+ if (data & 0x01) ++right_count;
+ if (data & 0x02) ++left_count;
+ auto message = ::frc971::hot_goal.MakeMessage();
+ message->left_count = left_count;
+ message->right_count = right_count;
+ LOG_STRUCT(DEBUG, "sending", *message);
+ message.Send();
+ } break;
+ case 0:
+ LOG(WARNING, "read on %d timed out\n", connection);
+ close(connection);
+ connection = -1;
+ break;
+ default:
+ PLOG(FATAL,
+ "select(%d, %p, nullptr, nullptr, %p) failed",
+ connection + 1, &fds, &timeout_timeval);
+ }
+ }
+ }
+
+ LOG(FATAL, "finished???\n");
+}
diff --git a/y2014/prime/prime.gyp b/y2014/prime/prime.gyp
new file mode 100644
index 0000000..a64fc1c
--- /dev/null
+++ b/y2014/prime/prime.gyp
@@ -0,0 +1,55 @@
+{
+ 'targets': [
+ {
+ 'target_name': 'All',
+ 'type': 'none',
+ 'dependencies': [
+ '../../frc971/frc971.gyp:All',
+
+ '../control_loops/drivetrain/drivetrain.gyp:drivetrain',
+ '../control_loops/drivetrain/drivetrain.gyp:drivetrain_lib_test',
+ '../control_loops/drivetrain/drivetrain.gyp:replay_drivetrain',
+ '../control_loops/claw/claw.gyp:claw',
+ '../control_loops/claw/claw.gyp:claw_calibration',
+ '../control_loops/claw/claw.gyp:claw_lib_test',
+ '../control_loops/claw/claw.gyp:replay_claw',
+ '../control_loops/shooter/shooter.gyp:shooter',
+ '../control_loops/shooter/shooter.gyp:shooter_lib_test',
+ '../control_loops/shooter/shooter.gyp:replay_shooter',
+ '../autonomous/autonomous.gyp:auto',
+ '../y2014.gyp:joystick_reader',
+ '../actors/actors.gyp:binaries',
+ 'hot_goal_reader',
+ ],
+ 'copies': [
+ {
+ 'destination': '<(rsync_dir)',
+ 'files': [
+ 'start_list.txt',
+ ],
+ },
+ ],
+ 'conditions': [
+ ['ARCHITECTURE=="arm_frc"', {
+ 'dependencies': [
+ '../wpilib/wpilib.gyp:wpilib_interface',
+ ],
+ }],
+ ],
+ },
+ {
+ 'target_name': 'hot_goal_reader',
+ 'type': 'executable',
+ 'sources': [
+ 'hot_goal_reader.cc',
+ ],
+ 'dependencies': [
+ '<(AOS)/common/common.gyp:time',
+ '<(AOS)/build/aos.gyp:logging',
+ '<(AOS)/common/logging/logging.gyp:queue_logging',
+ '<(AOS)/linux_code/linux_code.gyp:init',
+ '<(DEPTH)/y2014/queues/queues.gyp:hot_goal'
+ ],
+ },
+ ],
+}
diff --git a/y2014/prime/start_list.txt b/y2014/prime/start_list.txt
new file mode 100644
index 0000000..276aca8
--- /dev/null
+++ b/y2014/prime/start_list.txt
@@ -0,0 +1,13 @@
+binary_log_writer
+motor_writer
+joystick_reader
+drivetrain
+auto
+sensor_receiver
+joystick_proxy
+claw
+shooter
+shoot_action
+drivetrain_action
+catch_action
+hot_goal_reader
diff --git a/y2014/queues/auto_mode.q b/y2014/queues/auto_mode.q
new file mode 100644
index 0000000..7922dbf
--- /dev/null
+++ b/y2014/queues/auto_mode.q
@@ -0,0 +1,6 @@
+package frc971.sensors;
+
+message AutoMode {
+ double voltage;
+};
+queue AutoMode auto_mode;
diff --git a/y2014/queues/hot_goal.q b/y2014/queues/hot_goal.q
new file mode 100644
index 0000000..3444553
--- /dev/null
+++ b/y2014/queues/hot_goal.q
@@ -0,0 +1,7 @@
+package frc971;
+
+message HotGoal {
+ uint64_t left_count;
+ uint64_t right_count;
+};
+queue HotGoal hot_goal;
diff --git a/y2014/queues/profile_params.q b/y2014/queues/profile_params.q
new file mode 100644
index 0000000..e7ffe9a
--- /dev/null
+++ b/y2014/queues/profile_params.q
@@ -0,0 +1,6 @@
+package frc971;
+
+struct ProfileParams {
+ double velocity;
+ double acceleration;
+};
diff --git a/y2014/queues/queues.gyp b/y2014/queues/queues.gyp
new file mode 100644
index 0000000..1325384
--- /dev/null
+++ b/y2014/queues/queues.gyp
@@ -0,0 +1,37 @@
+{
+ 'targets': [
+ {
+ 'target_name': 'profile_params',
+ 'type': 'static_library',
+ 'sources': [
+ 'profile_params.q',
+ ],
+ 'variables': {
+ 'header_path': 'y2014/queues',
+ },
+ 'includes': ['../../aos/build/queues.gypi'],
+ },
+ {
+ 'target_name': 'hot_goal',
+ 'type': 'static_library',
+ 'sources': [
+ 'hot_goal.q',
+ ],
+ 'variables': {
+ 'header_path': 'y2014/queues',
+ },
+ 'includes': ['../../aos/build/queues.gypi'],
+ },
+ {
+ 'target_name': 'auto_mode',
+ 'type': 'static_library',
+ 'sources': [
+ 'auto_mode.q',
+ ],
+ 'variables': {
+ 'header_path': 'y2014/queues',
+ },
+ 'includes': ['../../aos/build/queues.gypi'],
+ },
+ ],
+}
diff --git a/y2014/wpilib/wpilib.gyp b/y2014/wpilib/wpilib.gyp
new file mode 100644
index 0000000..798ff76
--- /dev/null
+++ b/y2014/wpilib/wpilib.gyp
@@ -0,0 +1,39 @@
+{
+ 'targets': [
+ {
+ 'target_name': 'wpilib_interface',
+ 'type': 'executable',
+ 'sources': [
+ 'wpilib_interface.cc'
+ ],
+ 'dependencies': [
+ '<(AOS)/linux_code/linux_code.gyp:init',
+ '<(AOS)/common/common.gyp:stl_mutex',
+ '<(AOS)/build/aos.gyp:logging',
+ '<(EXTERNALS):WPILib',
+ '<(DEPTH)/y2014/y2014.gyp:constants',
+ '<(DEPTH)/y2014/queues/queues.gyp:auto_mode',
+ '<(DEPTH)/y2014/control_loops/drivetrain/drivetrain.gyp:drivetrain_queue',
+ '<(DEPTH)/y2014/control_loops/shooter/shooter.gyp:shooter_queue',
+ '<(DEPTH)/y2014/control_loops/claw/claw.gyp:claw_queue',
+ '<(AOS)/common/controls/controls.gyp:control_loop',
+ '<(AOS)/common/util/util.gyp:log_interval',
+ '<(AOS)/common/common.gyp:time',
+ '<(AOS)/common/logging/logging.gyp:queue_logging',
+ '<(AOS)/common/messages/messages.gyp:robot_state',
+ '<(AOS)/common/util/util.gyp:phased_loop',
+ '<(AOS)/common/messages/messages.gyp:robot_state',
+ '<(DEPTH)/frc971/wpilib/wpilib.gyp:hall_effect',
+ '<(DEPTH)/frc971/wpilib/wpilib.gyp:joystick_sender',
+ '<(DEPTH)/frc971/wpilib/wpilib.gyp:loop_output_handler',
+ '<(DEPTH)/frc971/wpilib/wpilib.gyp:buffered_pcm',
+ '<(DEPTH)/frc971/wpilib/wpilib.gyp:gyro_sender',
+ '<(DEPTH)/frc971/wpilib/wpilib.gyp:dma_edge_counting',
+ '<(DEPTH)/frc971/wpilib/wpilib.gyp:interrupt_edge_counting',
+ '<(DEPTH)/frc971/wpilib/wpilib.gyp:encoder_and_potentiometer',
+ '<(DEPTH)/frc971/control_loops/control_loops.gyp:queues',
+ '<(DEPTH)/frc971/wpilib/wpilib.gyp:logging_queue',
+ ],
+ },
+ ],
+}
diff --git a/y2014/wpilib/wpilib_interface.cc b/y2014/wpilib/wpilib_interface.cc
new file mode 100644
index 0000000..776b681
--- /dev/null
+++ b/y2014/wpilib/wpilib_interface.cc
@@ -0,0 +1,762 @@
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <inttypes.h>
+
+#include <thread>
+#include <mutex>
+#include <functional>
+
+#include "aos/common/logging/logging.h"
+#include "aos/common/logging/queue_logging.h"
+#include "aos/common/time.h"
+#include "aos/common/util/log_interval.h"
+#include "aos/common/util/phased_loop.h"
+#include "aos/common/util/wrapping_counter.h"
+#include "aos/common/stl_mutex.h"
+#include "aos/linux_code/init.h"
+#include "aos/common/messages/robot_state.q.h"
+
+#include "frc971/shifter_hall_effect.h"
+#include "y2014/control_loops/drivetrain/drivetrain.q.h"
+#include "y2014/control_loops/claw/claw.q.h"
+#include "y2014/control_loops/shooter/shooter.q.h"
+#include "y2014/constants.h"
+#include "y2014/queues/auto_mode.q.h"
+
+#include "frc971/wpilib/hall_effect.h"
+#include "frc971/wpilib/joystick_sender.h"
+#include "frc971/wpilib/loop_output_handler.h"
+#include "frc971/wpilib/buffered_solenoid.h"
+#include "frc971/wpilib/buffered_pcm.h"
+#include "frc971/wpilib/gyro_sender.h"
+#include "frc971/wpilib/dma_edge_counting.h"
+#include "frc971/wpilib/interrupt_edge_counting.h"
+#include "frc971/wpilib/encoder_and_potentiometer.h"
+#include "frc971/wpilib/logging.q.h"
+
+#include "Encoder.h"
+#include "Talon.h"
+#include "DriverStation.h"
+#include "AnalogInput.h"
+#include "Compressor.h"
+#include "Relay.h"
+#include "RobotBase.h"
+#include "dma.h"
+#include "ControllerPower.h"
+
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif
+
+using ::frc971::control_loops::drivetrain_queue;
+using ::frc971::control_loops::claw_queue;
+using ::frc971::control_loops::shooter_queue;
+
+namespace frc971 {
+namespace wpilib {
+
+// TODO(brian): Replace this with ::std::make_unique once all our toolchains
+// have support.
+template <class T, class... U>
+std::unique_ptr<T> make_unique(U &&... u) {
+ return std::unique_ptr<T>(new T(std::forward<U>(u)...));
+}
+
+double drivetrain_translate(int32_t in) {
+ return static_cast<double>(in) /
+ (256.0 /*cpr*/ * 4.0 /*4x*/) *
+ constants::GetValues().drivetrain_encoder_ratio *
+ (3.5 /*wheel diameter*/ * 2.54 / 100.0 * M_PI);
+}
+
+float hall_translate(const constants::ShifterHallEffect &k, float in_low,
+ float in_high) {
+ const float low_ratio =
+ 0.5 * (in_low - static_cast<float>(k.low_gear_low)) /
+ static_cast<float>(k.low_gear_middle - k.low_gear_low);
+ const float high_ratio =
+ 0.5 + 0.5 * (in_high - static_cast<float>(k.high_gear_middle)) /
+ static_cast<float>(k.high_gear_high - k.high_gear_middle);
+
+ // Return low when we are below 1/2, and high when we are above 1/2.
+ if (low_ratio + high_ratio < 1.0) {
+ return low_ratio;
+ } else {
+ return high_ratio;
+ }
+}
+
+double claw_translate(int32_t in) {
+ return static_cast<double>(in) / (256.0 /*cpr*/ * 4.0 /*quad*/) /
+ (18.0 / 48.0 /*encoder gears*/) / (12.0 / 60.0 /*chain reduction*/) *
+ (M_PI / 180.0);
+}
+
+double shooter_translate(int32_t in) {
+ return static_cast<double>(in) / (256.0 /*cpr*/ * 4.0 /*quad*/) *
+ 16 /*sprocket teeth*/ * 0.375 /*chain pitch*/
+ * (2.54 / 100.0 /*in to m*/);
+}
+
+static const double kMaximumEncoderPulsesPerSecond =
+ 5600.0 /* free speed RPM */ * 14.0 / 48.0 /* bottom gear reduction */ *
+ 18.0 / 32.0 /* big belt reduction */ *
+ 18.0 / 66.0 /* top gear reduction */ * 48.0 / 18.0 /* encoder gears */ /
+ 60.0 /* seconds / minute */ * 256.0 /* CPR */;
+
+class SensorReader {
+ public:
+ SensorReader() {
+ // Set it to filter out anything shorter than 1/4 of the minimum pulse width
+ // we should ever see.
+ encoder_filter_.SetPeriodNanoSeconds(
+ static_cast<int>(1 / 4.0 / kMaximumEncoderPulsesPerSecond * 1e9 + 0.5));
+ hall_filter_.SetPeriodNanoSeconds(100000);
+ }
+
+ void set_auto_selector_analog(::std::unique_ptr<AnalogInput> analog) {
+ auto_selector_analog_ = ::std::move(analog);
+ }
+
+ void set_drivetrain_left_encoder(::std::unique_ptr<Encoder> encoder) {
+ drivetrain_left_encoder_ = ::std::move(encoder);
+ }
+
+ void set_drivetrain_right_encoder(::std::unique_ptr<Encoder> encoder) {
+ drivetrain_right_encoder_ = ::std::move(encoder);
+ }
+
+ void set_high_left_drive_hall(::std::unique_ptr<AnalogInput> analog) {
+ high_left_drive_hall_ = ::std::move(analog);
+ }
+
+ void set_low_right_drive_hall(::std::unique_ptr<AnalogInput> analog) {
+ low_right_drive_hall_ = ::std::move(analog);
+ }
+
+ void set_high_right_drive_hall(::std::unique_ptr<AnalogInput> analog) {
+ high_right_drive_hall_ = ::std::move(analog);
+ }
+
+ void set_low_left_drive_hall(::std::unique_ptr<AnalogInput> analog) {
+ low_left_drive_hall_ = ::std::move(analog);
+ }
+
+ void set_top_claw_encoder(::std::unique_ptr<Encoder> encoder) {
+ encoder_filter_.Add(encoder.get());
+ top_reader_.set_encoder(::std::move(encoder));
+ }
+
+ void set_top_claw_front_hall(::std::unique_ptr<DigitalSource> hall) {
+ hall_filter_.Add(hall.get());
+ top_reader_.set_front_hall(::std::move(hall));
+ }
+
+ void set_top_claw_calibration_hall(::std::unique_ptr<DigitalSource> hall) {
+ hall_filter_.Add(hall.get());
+ top_reader_.set_calibration_hall(::std::move(hall));
+ }
+
+ void set_top_claw_back_hall(::std::unique_ptr<DigitalSource> hall) {
+ hall_filter_.Add(hall.get());
+ top_reader_.set_back_hall(::std::move(hall));
+ }
+
+ void set_bottom_claw_encoder(::std::unique_ptr<Encoder> encoder) {
+ encoder_filter_.Add(encoder.get());
+ bottom_reader_.set_encoder(::std::move(encoder));
+ }
+
+ void set_bottom_claw_front_hall(::std::unique_ptr<DigitalSource> hall) {
+ hall_filter_.Add(hall.get());
+ bottom_reader_.set_front_hall(::std::move(hall));
+ }
+
+ void set_bottom_claw_calibration_hall(::std::unique_ptr<DigitalSource> hall) {
+ hall_filter_.Add(hall.get());
+ bottom_reader_.set_calibration_hall(::std::move(hall));
+ }
+
+ void set_bottom_claw_back_hall(::std::unique_ptr<DigitalSource> hall) {
+ hall_filter_.Add(hall.get());
+ bottom_reader_.set_back_hall(::std::move(hall));
+ }
+
+ void set_shooter_encoder(::std::unique_ptr<Encoder> encoder) {
+ encoder_filter_.Add(encoder.get());
+ shooter_encoder_ = ::std::move(encoder);
+ }
+
+ void set_shooter_proximal(::std::unique_ptr<DigitalSource> hall) {
+ hall_filter_.Add(hall.get());
+ shooter_proximal_ = ::std::move(hall);
+ }
+
+ void set_shooter_distal(::std::unique_ptr<DigitalSource> hall) {
+ hall_filter_.Add(hall.get());
+ shooter_distal_ = ::std::move(hall);
+ }
+
+ void set_shooter_plunger(::std::unique_ptr<DigitalSource> hall) {
+ hall_filter_.Add(hall.get());
+ shooter_plunger_ = ::std::move(hall);
+ shooter_plunger_reader_ =
+ make_unique<DMADigitalReader>(shooter_plunger_.get());
+ }
+
+ void set_shooter_latch(::std::unique_ptr<DigitalSource> hall) {
+ hall_filter_.Add(hall.get());
+ shooter_latch_ = ::std::move(hall);
+ shooter_latch_reader_ = make_unique<DMADigitalReader>(shooter_latch_.get());
+ }
+
+ // All of the DMA-related set_* calls must be made before this, and it doesn't
+ // hurt to do all of them.
+ void set_dma(::std::unique_ptr<DMA> dma) {
+ shooter_proximal_counter_ = make_unique<DMAEdgeCounter>(
+ shooter_encoder_.get(), shooter_proximal_.get());
+ shooter_distal_counter_ = make_unique<DMAEdgeCounter>(
+ shooter_encoder_.get(), shooter_distal_.get());
+
+ dma_synchronizer_.reset(new DMASynchronizer(::std::move(dma)));
+ dma_synchronizer_->Add(shooter_proximal_counter_.get());
+ dma_synchronizer_->Add(shooter_distal_counter_.get());
+ dma_synchronizer_->Add(shooter_plunger_reader_.get());
+ dma_synchronizer_->Add(shooter_latch_reader_.get());
+ }
+
+ void operator()() {
+ ::aos::SetCurrentThreadName("SensorReader");
+ LOG(INFO, "In sensor reader thread\n");
+
+ my_pid_ = getpid();
+ ds_ = DriverStation::GetInstance();
+
+ top_reader_.Start();
+ bottom_reader_.Start();
+ dma_synchronizer_->Start();
+ LOG(INFO, "Things are now started\n");
+
+ ::aos::SetCurrentThreadRealtimePriority(kPriority);
+ while (run_) {
+ ::aos::time::PhasedLoopXMS(10, 9000);
+ RunIteration();
+ }
+
+ top_reader_.Quit();
+ bottom_reader_.Quit();
+ }
+
+ void RunIteration() {
+ {
+ auto new_state = ::aos::robot_state.MakeMessage();
+
+ new_state->reader_pid = my_pid_;
+ new_state->outputs_enabled = ds_->IsSysActive();
+ new_state->browned_out = ds_->IsSysBrownedOut();
+
+ new_state->is_3v3_active = ControllerPower::GetEnabled3V3();
+ new_state->is_5v_active = ControllerPower::GetEnabled5V();
+ new_state->voltage_3v3 = ControllerPower::GetVoltage3V3();
+ new_state->voltage_5v = ControllerPower::GetVoltage5V();
+
+ new_state->voltage_roborio_in = ControllerPower::GetInputVoltage();
+ new_state->voltage_battery = ds_->GetBatteryVoltage();
+
+ LOG_STRUCT(DEBUG, "robot_state", *new_state);
+
+ new_state.Send();
+ }
+
+ const auto &values = constants::GetValues();
+
+ {
+ auto drivetrain_message = drivetrain_queue.position.MakeMessage();
+ drivetrain_message->right_encoder =
+ drivetrain_translate(drivetrain_right_encoder_->GetRaw());
+ drivetrain_message->left_encoder =
+ -drivetrain_translate(drivetrain_left_encoder_->GetRaw());
+ drivetrain_message->left_shifter_position =
+ hall_translate(values.left_drive, low_left_drive_hall_->GetVoltage(),
+ high_left_drive_hall_->GetVoltage());
+ drivetrain_message->right_shifter_position = hall_translate(
+ values.right_drive, low_right_drive_hall_->GetVoltage(),
+ high_right_drive_hall_->GetVoltage());
+
+ drivetrain_message.Send();
+ }
+
+ ::frc971::sensors::auto_mode.MakeWithBuilder()
+ .voltage(auto_selector_analog_->GetVoltage())
+ .Send();
+
+ dma_synchronizer_->RunIteration();
+
+ {
+ auto shooter_message = shooter_queue.position.MakeMessage();
+ shooter_message->position = shooter_translate(shooter_encoder_->GetRaw());
+ shooter_message->plunger = shooter_plunger_reader_->value();
+ shooter_message->latch = shooter_latch_reader_->value();
+ CopyShooterPosedgeCounts(shooter_proximal_counter_.get(),
+ &shooter_message->pusher_proximal);
+ CopyShooterPosedgeCounts(shooter_distal_counter_.get(),
+ &shooter_message->pusher_distal);
+
+ shooter_message.Send();
+ }
+
+ {
+ auto claw_message = claw_queue.position.MakeMessage();
+ top_reader_.RunIteration(&claw_message->top);
+ bottom_reader_.RunIteration(&claw_message->bottom);
+
+ claw_message.Send();
+ }
+ }
+
+ void Quit() { run_ = false; }
+
+ private:
+ class HalfClawReader {
+ public:
+ HalfClawReader(bool reversed) : reversed_(reversed) {}
+
+ void set_encoder(::std::unique_ptr<Encoder> encoder) {
+ encoder_ = ::std::move(encoder);
+ }
+
+ void set_front_hall(::std::unique_ptr<DigitalSource> front_hall) {
+ front_hall_ = ::std::move(front_hall);
+ }
+
+ void set_calibration_hall(
+ ::std::unique_ptr<DigitalSource> calibration_hall) {
+ calibration_hall_ = ::std::move(calibration_hall);
+ }
+
+ void set_back_hall(::std::unique_ptr<DigitalSource> back_hall) {
+ back_hall_ = ::std::move(back_hall);
+ }
+
+ void Start() {
+ front_counter_ =
+ make_unique<EdgeCounter>(encoder_.get(), front_hall_.get());
+ synchronizer_.Add(front_counter_.get());
+ calibration_counter_ =
+ make_unique<EdgeCounter>(encoder_.get(), calibration_hall_.get());
+ synchronizer_.Add(calibration_counter_.get());
+ back_counter_ =
+ make_unique<EdgeCounter>(encoder_.get(), back_hall_.get());
+ synchronizer_.Add(back_counter_.get());
+ synchronized_encoder_ =
+ make_unique<InterruptSynchronizedEncoder>(encoder_.get());
+ synchronizer_.Add(synchronized_encoder_.get());
+
+ synchronizer_.Start();
+ }
+
+ void Quit() { synchronizer_.Quit(); }
+
+ void RunIteration(control_loops::HalfClawPosition *half_claw_position) {
+ const double multiplier = reversed_ ? -1.0 : 1.0;
+
+ synchronizer_.RunIteration();
+
+ CopyPosition(front_counter_.get(), &half_claw_position->front);
+ CopyPosition(calibration_counter_.get(),
+ &half_claw_position->calibration);
+ CopyPosition(back_counter_.get(), &half_claw_position->back);
+ half_claw_position->position =
+ multiplier * claw_translate(synchronized_encoder_->get());
+ }
+
+ private:
+ void CopyPosition(const EdgeCounter *counter, HallEffectStruct *out) {
+ const double multiplier = reversed_ ? -1.0 : 1.0;
+
+ out->current = counter->polled_value();
+ out->posedge_count = counter->positive_interrupt_count();
+ out->negedge_count = counter->negative_interrupt_count();
+ out->posedge_value =
+ multiplier * claw_translate(counter->last_positive_encoder_value());
+ out->negedge_value =
+ multiplier * claw_translate(counter->last_negative_encoder_value());
+ }
+
+ InterruptSynchronizer synchronizer_{kInterruptPriority};
+
+ ::std::unique_ptr<EdgeCounter> front_counter_;
+ ::std::unique_ptr<EdgeCounter> calibration_counter_;
+ ::std::unique_ptr<EdgeCounter> back_counter_;
+ ::std::unique_ptr<InterruptSynchronizedEncoder> synchronized_encoder_;
+
+ ::std::unique_ptr<Encoder> encoder_;
+ ::std::unique_ptr<DigitalSource> front_hall_;
+ ::std::unique_ptr<DigitalSource> calibration_hall_;
+ ::std::unique_ptr<DigitalSource> back_hall_;
+
+ const bool reversed_;
+ };
+
+ static const int kPriority = 30;
+ static const int kInterruptPriority = 55;
+
+ void CopyShooterPosedgeCounts(const DMAEdgeCounter *counter,
+ PosedgeOnlyCountedHallEffectStruct *output) {
+ output->current = counter->polled_value();
+ output->posedge_count = counter->positive_count();
+ output->negedge_count = counter->negative_count();
+ output->posedge_value =
+ shooter_translate(counter->last_positive_encoder_value());
+ }
+
+ int32_t my_pid_;
+ DriverStation *ds_;
+
+ ::std::unique_ptr<DMASynchronizer> dma_synchronizer_;
+
+ ::std::unique_ptr<AnalogInput> auto_selector_analog_;
+
+ ::std::unique_ptr<Encoder> drivetrain_left_encoder_;
+ ::std::unique_ptr<Encoder> drivetrain_right_encoder_;
+ ::std::unique_ptr<AnalogInput> low_left_drive_hall_;
+ ::std::unique_ptr<AnalogInput> high_left_drive_hall_;
+ ::std::unique_ptr<AnalogInput> low_right_drive_hall_;
+ ::std::unique_ptr<AnalogInput> high_right_drive_hall_;
+
+ HalfClawReader top_reader_{false}, bottom_reader_{true};
+
+ ::std::unique_ptr<Encoder> shooter_encoder_;
+ ::std::unique_ptr<DigitalSource> shooter_proximal_, shooter_distal_;
+ ::std::unique_ptr<DigitalSource> shooter_plunger_, shooter_latch_;
+ ::std::unique_ptr<DMAEdgeCounter> shooter_proximal_counter_,
+ shooter_distal_counter_;
+ ::std::unique_ptr<DMADigitalReader> shooter_plunger_reader_,
+ shooter_latch_reader_;
+
+ ::std::atomic<bool> run_{true};
+ DigitalGlitchFilter encoder_filter_, hall_filter_;
+};
+
+class SolenoidWriter {
+ public:
+ SolenoidWriter(const ::std::unique_ptr<BufferedPcm> &pcm)
+ : pcm_(pcm),
+ shooter_(".frc971.control_loops.shooter_queue.output"),
+ drivetrain_(".frc971.control_loops.drivetrain_queue.output") {}
+
+ void set_pressure_switch(::std::unique_ptr<DigitalSource> pressure_switch) {
+ pressure_switch_ = ::std::move(pressure_switch);
+ }
+
+ void set_compressor_relay(::std::unique_ptr<Relay> compressor_relay) {
+ compressor_relay_ = ::std::move(compressor_relay);
+ }
+
+ void set_drivetrain_left(::std::unique_ptr<BufferedSolenoid> s) {
+ drivetrain_left_ = ::std::move(s);
+ }
+
+ void set_drivetrain_right(::std::unique_ptr<BufferedSolenoid> s) {
+ drivetrain_right_ = ::std::move(s);
+ }
+
+ void set_shooter_latch(::std::unique_ptr<BufferedSolenoid> s) {
+ shooter_latch_ = ::std::move(s);
+ }
+
+ void set_shooter_brake(::std::unique_ptr<BufferedSolenoid> s) {
+ shooter_brake_ = ::std::move(s);
+ }
+
+ void operator()() {
+ ::aos::SetCurrentThreadName("Solenoids");
+ ::aos::SetCurrentThreadRealtimePriority(30);
+
+ while (run_) {
+ ::aos::time::PhasedLoopXMS(20, 1000);
+
+ {
+ shooter_.FetchLatest();
+ if (shooter_.get()) {
+ LOG_STRUCT(DEBUG, "solenoids", *shooter_);
+ shooter_latch_->Set(!shooter_->latch_piston);
+ shooter_brake_->Set(!shooter_->brake_piston);
+ }
+ }
+
+ {
+ drivetrain_.FetchLatest();
+ if (drivetrain_.get()) {
+ LOG_STRUCT(DEBUG, "solenoids", *drivetrain_);
+ drivetrain_left_->Set(drivetrain_->left_high);
+ drivetrain_right_->Set(drivetrain_->right_high);
+ }
+ }
+
+ {
+ PneumaticsToLog to_log;
+ {
+ const bool compressor_on = !pressure_switch_->Get();
+ to_log.compressor_on = compressor_on;
+ if (compressor_on) {
+ compressor_relay_->Set(Relay::kForward);
+ } else {
+ compressor_relay_->Set(Relay::kOff);
+ }
+ }
+
+ pcm_->Flush();
+ to_log.read_solenoids = pcm_->GetAll();
+ LOG_STRUCT(DEBUG, "pneumatics info", to_log);
+ }
+ }
+ }
+
+ void Quit() { run_ = false; }
+
+ private:
+ const ::std::unique_ptr<BufferedPcm> &pcm_;
+
+ ::std::unique_ptr<BufferedSolenoid> drivetrain_left_;
+ ::std::unique_ptr<BufferedSolenoid> drivetrain_right_;
+ ::std::unique_ptr<BufferedSolenoid> shooter_latch_;
+ ::std::unique_ptr<BufferedSolenoid> shooter_brake_;
+
+ ::std::unique_ptr<DigitalSource> pressure_switch_;
+ ::std::unique_ptr<Relay> compressor_relay_;
+
+ ::aos::Queue<::frc971::control_loops::ShooterQueue::Output> shooter_;
+ ::aos::Queue<::frc971::control_loops::DrivetrainQueue::Output> drivetrain_;
+
+ ::std::atomic<bool> run_{true};
+};
+
+class DrivetrainWriter : public LoopOutputHandler {
+ public:
+ void set_left_drivetrain_talon(::std::unique_ptr<Talon> t) {
+ left_drivetrain_talon_ = ::std::move(t);
+ }
+
+ void set_right_drivetrain_talon(::std::unique_ptr<Talon> t) {
+ right_drivetrain_talon_ = ::std::move(t);
+ }
+
+ private:
+ virtual void Read() override {
+ ::frc971::control_loops::drivetrain_queue.output.FetchAnother();
+ }
+
+ virtual void Write() override {
+ auto &queue = ::frc971::control_loops::drivetrain_queue.output;
+ LOG_STRUCT(DEBUG, "will output", *queue);
+ left_drivetrain_talon_->Set(queue->left_voltage / 12.0);
+ right_drivetrain_talon_->Set(-queue->right_voltage / 12.0);
+ }
+
+ virtual void Stop() override {
+ LOG(WARNING, "drivetrain output too old\n");
+ left_drivetrain_talon_->Disable();
+ right_drivetrain_talon_->Disable();
+ }
+
+ ::std::unique_ptr<Talon> left_drivetrain_talon_;
+ ::std::unique_ptr<Talon> right_drivetrain_talon_;
+};
+
+class ShooterWriter : public LoopOutputHandler {
+ public:
+ void set_shooter_talon(::std::unique_ptr<Talon> t) {
+ shooter_talon_ = ::std::move(t);
+ }
+
+ private:
+ virtual void Read() override {
+ ::frc971::control_loops::shooter_queue.output.FetchAnother();
+ }
+
+ virtual void Write() override {
+ auto &queue = ::frc971::control_loops::shooter_queue.output;
+ LOG_STRUCT(DEBUG, "will output", *queue);
+ shooter_talon_->Set(queue->voltage / 12.0);
+ }
+
+ virtual void Stop() override {
+ LOG(WARNING, "shooter output too old\n");
+ shooter_talon_->Disable();
+ }
+
+ ::std::unique_ptr<Talon> shooter_talon_;
+};
+
+class ClawWriter : public LoopOutputHandler {
+ public:
+ void set_top_claw_talon(::std::unique_ptr<Talon> t) {
+ top_claw_talon_ = ::std::move(t);
+ }
+
+ void set_bottom_claw_talon(::std::unique_ptr<Talon> t) {
+ bottom_claw_talon_ = ::std::move(t);
+ }
+
+ void set_left_tusk_talon(::std::unique_ptr<Talon> t) {
+ left_tusk_talon_ = ::std::move(t);
+ }
+
+ void set_right_tusk_talon(::std::unique_ptr<Talon> t) {
+ right_tusk_talon_ = ::std::move(t);
+ }
+
+ void set_intake1_talon(::std::unique_ptr<Talon> t) {
+ intake1_talon_ = ::std::move(t);
+ }
+
+ void set_intake2_talon(::std::unique_ptr<Talon> t) {
+ intake2_talon_ = ::std::move(t);
+ }
+
+ private:
+ virtual void Read() override {
+ ::frc971::control_loops::claw_queue.output.FetchAnother();
+ }
+
+ virtual void Write() override {
+ auto &queue = ::frc971::control_loops::claw_queue.output;
+ LOG_STRUCT(DEBUG, "will output", *queue);
+ intake1_talon_->Set(queue->intake_voltage / 12.0);
+ intake2_talon_->Set(queue->intake_voltage / 12.0);
+ bottom_claw_talon_->Set(-queue->bottom_claw_voltage / 12.0);
+ top_claw_talon_->Set(queue->top_claw_voltage / 12.0);
+ left_tusk_talon_->Set(queue->tusk_voltage / 12.0);
+ right_tusk_talon_->Set(-queue->tusk_voltage / 12.0);
+ }
+
+ virtual void Stop() override {
+ LOG(WARNING, "claw output too old\n");
+ intake1_talon_->Disable();
+ intake2_talon_->Disable();
+ bottom_claw_talon_->Disable();
+ top_claw_talon_->Disable();
+ left_tusk_talon_->Disable();
+ right_tusk_talon_->Disable();
+ }
+
+ ::std::unique_ptr<Talon> top_claw_talon_;
+ ::std::unique_ptr<Talon> bottom_claw_talon_;
+ ::std::unique_ptr<Talon> left_tusk_talon_;
+ ::std::unique_ptr<Talon> right_tusk_talon_;
+ ::std::unique_ptr<Talon> intake1_talon_;
+ ::std::unique_ptr<Talon> intake2_talon_;
+};
+
+class WPILibRobot : public RobotBase {
+ public:
+ ::std::unique_ptr<Encoder> make_encoder(int index) {
+ return make_unique<Encoder>(10 + index * 2, 11 + index * 2, false,
+ Encoder::k4X);
+ }
+
+ virtual void StartCompetition() {
+ ::aos::InitNRT();
+ ::aos::SetCurrentThreadName("StartCompetition");
+
+ JoystickSender joystick_sender;
+ ::std::thread joystick_thread(::std::ref(joystick_sender));
+
+ SensorReader reader;
+ LOG(INFO, "Creating the reader\n");
+
+ reader.set_auto_selector_analog(make_unique<AnalogInput>(4));
+
+ reader.set_drivetrain_left_encoder(make_encoder(0));
+ reader.set_drivetrain_right_encoder(make_encoder(1));
+ reader.set_high_left_drive_hall(make_unique<AnalogInput>(1));
+ reader.set_low_left_drive_hall(make_unique<AnalogInput>(0));
+ reader.set_high_right_drive_hall(make_unique<AnalogInput>(2));
+ reader.set_low_right_drive_hall(make_unique<AnalogInput>(3));
+
+ reader.set_top_claw_encoder(make_encoder(3));
+ reader.set_top_claw_front_hall(make_unique<HallEffect>(4)); // R2
+ reader.set_top_claw_calibration_hall(make_unique<HallEffect>(3)); // R3
+ reader.set_top_claw_back_hall(make_unique<HallEffect>(5)); // R1
+
+ reader.set_bottom_claw_encoder(make_encoder(5));
+ reader.set_bottom_claw_front_hall(make_unique<HallEffect>(1)); // L2
+ reader.set_bottom_claw_calibration_hall(make_unique<HallEffect>(0)); // L3
+ reader.set_bottom_claw_back_hall(make_unique<HallEffect>(2)); // L1
+
+ reader.set_shooter_encoder(make_encoder(2));
+ reader.set_shooter_proximal(make_unique<HallEffect>(6)); // S1
+ reader.set_shooter_distal(make_unique<HallEffect>(7)); // S2
+ reader.set_shooter_plunger(make_unique<HallEffect>(8)); // S3
+ reader.set_shooter_latch(make_unique<HallEffect>(9)); // S4
+
+ reader.set_dma(make_unique<DMA>());
+ ::std::thread reader_thread(::std::ref(reader));
+
+ GyroSender gyro_sender;
+ ::std::thread gyro_thread(::std::ref(gyro_sender));
+
+ DrivetrainWriter drivetrain_writer;
+ drivetrain_writer.set_left_drivetrain_talon(
+ ::std::unique_ptr<Talon>(new Talon(5)));
+ drivetrain_writer.set_right_drivetrain_talon(
+ ::std::unique_ptr<Talon>(new Talon(2)));
+ ::std::thread drivetrain_writer_thread(::std::ref(drivetrain_writer));
+
+ ::frc971::wpilib::ClawWriter claw_writer;
+ claw_writer.set_top_claw_talon(::std::unique_ptr<Talon>(new Talon(1)));
+ claw_writer.set_bottom_claw_talon(::std::unique_ptr<Talon>(new Talon(0)));
+ claw_writer.set_left_tusk_talon(::std::unique_ptr<Talon>(new Talon(4)));
+ claw_writer.set_right_tusk_talon(::std::unique_ptr<Talon>(new Talon(3)));
+ claw_writer.set_intake1_talon(::std::unique_ptr<Talon>(new Talon(7)));
+ claw_writer.set_intake2_talon(::std::unique_ptr<Talon>(new Talon(8)));
+ ::std::thread claw_writer_thread(::std::ref(claw_writer));
+
+ ::frc971::wpilib::ShooterWriter shooter_writer;
+ shooter_writer.set_shooter_talon(::std::unique_ptr<Talon>(new Talon(6)));
+ ::std::thread shooter_writer_thread(::std::ref(shooter_writer));
+
+ ::std::unique_ptr<::frc971::wpilib::BufferedPcm> pcm(
+ new ::frc971::wpilib::BufferedPcm());
+ SolenoidWriter solenoid_writer(pcm);
+ solenoid_writer.set_drivetrain_left(pcm->MakeSolenoid(6));
+ solenoid_writer.set_drivetrain_right(pcm->MakeSolenoid(7));
+ solenoid_writer.set_shooter_latch(pcm->MakeSolenoid(5));
+ solenoid_writer.set_shooter_brake(pcm->MakeSolenoid(4));
+
+ solenoid_writer.set_pressure_switch(make_unique<DigitalInput>(9));
+ solenoid_writer.set_compressor_relay(make_unique<Relay>(0));
+ ::std::thread solenoid_thread(::std::ref(solenoid_writer));
+
+ // Wait forever. Not much else to do...
+ PCHECK(select(0, nullptr, nullptr, nullptr, nullptr));
+
+ LOG(ERROR, "Exiting WPILibRobot\n");
+
+ joystick_sender.Quit();
+ joystick_thread.join();
+ reader.Quit();
+ reader_thread.join();
+ gyro_sender.Quit();
+ gyro_thread.join();
+
+ drivetrain_writer.Quit();
+ drivetrain_writer_thread.join();
+ shooter_writer.Quit();
+ shooter_writer_thread.join();
+ claw_writer.Quit();
+ claw_writer_thread.join();
+ solenoid_writer.Quit();
+ solenoid_thread.join();
+
+ ::aos::Cleanup();
+ }
+};
+
+} // namespace wpilib
+} // namespace frc971
+
+
+START_ROBOT_CLASS(::frc971::wpilib::WPILibRobot);
diff --git a/y2014/y2014.gyp b/y2014/y2014.gyp
new file mode 100644
index 0000000..8787a9a
--- /dev/null
+++ b/y2014/y2014.gyp
@@ -0,0 +1,44 @@
+{
+ 'targets': [
+ {
+ 'target_name': 'constants',
+ 'type': 'static_library',
+ 'sources': [
+ 'constants.cc',
+ ],
+ 'dependencies': [
+ '<(AOS)/build/aos.gyp:logging',
+ '<(AOS)/common/common.gyp:once',
+ '<(AOS)/common/network/network.gyp:team_number',
+ '<(DEPTH)/frc971/control_loops/control_loops.gyp:state_feedback_loop',
+ '<(DEPTH)/y2014/control_loops/drivetrain/drivetrain.gyp:polydrivetrain_plants',
+ ],
+ 'export_dependent_settings': [
+ '<(DEPTH)/frc971/control_loops/control_loops.gyp:state_feedback_loop',
+ ],
+ },
+ {
+ 'target_name': 'joystick_reader',
+ 'type': 'executable',
+ 'sources': [
+ 'joystick_reader.cc',
+ ],
+ 'dependencies': [
+ '<(AOS)/prime/input/input.gyp:joystick_input',
+ '<(AOS)/linux_code/linux_code.gyp:init',
+ '<(AOS)/build/aos.gyp:logging',
+ '<(AOS)/common/common.gyp:time',
+ '<(AOS)/common/util/util.gyp:log_interval',
+ '<(AOS)/common/actions/actions.gyp:action_lib',
+
+ '<(DEPTH)/y2014/control_loops/drivetrain/drivetrain.gyp:drivetrain_queue',
+ '<(DEPTH)/y2014/y2014.gyp:constants',
+ '<(DEPTH)/frc971/queues/queues.gyp:gyro',
+ '<(DEPTH)/frc971/autonomous/autonomous.gyp:auto_queue',
+ '<(DEPTH)/y2014/control_loops/claw/claw.gyp:claw_queue',
+ '<(DEPTH)/y2014/control_loops/shooter/shooter.gyp:shooter_queue',
+ '<(DEPTH)/y2014/actors/actors.gyp:shoot_action_lib',
+ ],
+ },
+ ],
+}
diff --git a/y2015/actors/actors.gyp b/y2015/actors/actors.gyp
new file mode 100644
index 0000000..f4989f0
--- /dev/null
+++ b/y2015/actors/actors.gyp
@@ -0,0 +1,630 @@
+{
+ 'targets': [
+ {
+ 'target_name': 'binaries',
+ 'type': 'none',
+ 'dependencies': [
+ 'drivetrain_action',
+ 'score_action',
+ 'score_action_test',
+ 'pickup_action',
+ 'stack_action',
+ 'stack_and_lift_action',
+ 'stack_and_hold_action',
+ 'held_to_lift_action',
+ 'can_pickup_action',
+ 'horizontal_can_pickup_action',
+ 'lift_action',
+ 'stack_action_test',
+ ],
+ },
+ {
+ 'target_name': 'drivetrain_action_queue',
+ 'type': 'static_library',
+ 'sources': ['drivetrain_action.q'],
+ 'variables': {
+ 'header_path': 'y2015/actors',
+ },
+ 'dependencies': [
+ '<(AOS)/common/actions/actions.gyp:action_queue',
+ ],
+ 'export_dependent_settings': [
+ '<(AOS)/common/actions/actions.gyp:action_queue',
+ ],
+ 'includes': ['../../aos/build/queues.gypi'],
+ },
+ {
+ 'target_name': 'drivetrain_action_lib',
+ 'type': 'static_library',
+ 'sources': [
+ 'drivetrain_actor.cc',
+ ],
+ 'dependencies': [
+ 'drivetrain_action_queue',
+ '<(DEPTH)/y2015/y2015.gyp:constants',
+ '<(AOS)/common/common.gyp:time',
+ '<(AOS)/common/util/util.gyp:phased_loop',
+ '<(AOS)/build/aos.gyp:logging',
+ '<(AOS)/common/actions/actions.gyp:action_lib',
+ '<(AOS)/common/logging/logging.gyp:queue_logging',
+ '<(EXTERNALS):eigen',
+ '<(AOS)/common/util/util.gyp:trapezoid_profile',
+ '<(DEPTH)/y2015/control_loops/drivetrain/drivetrain.gyp:drivetrain_queue',
+ ],
+ 'export_dependent_settings': [
+ '<(AOS)/common/actions/actions.gyp:action_lib',
+ 'drivetrain_action_queue',
+ ],
+ },
+ {
+ 'target_name': 'drivetrain_action',
+ 'type': 'executable',
+ 'sources': [
+ 'drivetrain_actor_main.cc',
+ ],
+ 'dependencies': [
+ '<(AOS)/linux_code/linux_code.gyp:init',
+ '<(AOS)/common/actions/actions.gyp:action_lib',
+ 'drivetrain_action_queue',
+ 'drivetrain_action_lib',
+ ],
+ },
+ {
+ 'target_name' : 'fridge_profile_lib',
+ 'type' : 'static_library',
+ 'sources' : [
+ 'fridge_profile_lib.cc',
+ ],
+ 'dependencies' : [
+ '<(AOS)/build/aos.gyp:logging_interface',
+ '<(AOS)/common/actions/actions.gyp:action_lib',
+ '<(DEPTH)/y2015/control_loops/fridge/fridge.gyp:fridge_queue',
+ ],
+ 'export_dependent_settings' : [
+ '<(AOS)/build/aos.gyp:logging_interface',
+ '<(AOS)/common/actions/actions.gyp:action_lib',
+ '<(DEPTH)/y2015/control_loops/fridge/fridge.gyp:fridge_queue',
+ ],
+ },
+ {
+ 'target_name': 'score_action_queue',
+ 'type': 'static_library',
+ 'sources': ['score_action.q'],
+ 'variables': {
+ 'header_path': 'y2015/actors',
+ },
+ 'dependencies': [
+ '<(AOS)/common/actions/actions.gyp:action_queue',
+ ],
+ 'export_dependent_settings': [
+ '<(AOS)/common/actions/actions.gyp:action_queue',
+ ],
+ 'includes': ['../../aos/build/queues.gypi'],
+ },
+ {
+ 'target_name': 'score_action_lib',
+ 'type': 'static_library',
+ 'sources': [
+ 'score_actor.cc',
+ ],
+ 'dependencies': [
+ 'score_action_queue',
+ '<(AOS)/build/aos.gyp:logging',
+ '<(AOS)/common/actions/actions.gyp:action_lib',
+ '<(AOS)/common/controls/controls.gyp:control_loop',
+ '<(DEPTH)/y2015/y2015.gyp:constants',
+ '<(DEPTH)/y2015/control_loops/fridge/fridge.gyp:fridge_queue',
+ '<(EXTERNALS):eigen',
+ ],
+ 'export_dependent_settings': [
+ '<(AOS)/common/actions/actions.gyp:action_lib',
+ 'score_action_queue',
+ '<(EXTERNALS):eigen',
+ ],
+ },
+ {
+ 'target_name': 'score_action',
+ 'type': 'executable',
+ 'sources': [
+ 'score_actor_main.cc',
+ ],
+ 'dependencies': [
+ '<(AOS)/linux_code/linux_code.gyp:init',
+ '<(AOS)/common/actions/actions.gyp:action_lib',
+ 'score_action_queue',
+ 'score_action_lib',
+ ],
+ },
+ {
+ 'target_name': 'score_action_test',
+ 'type': 'executable',
+ 'sources': [
+ 'score_actor_test.cc',
+ ],
+ 'dependencies': [
+ '<(EXTERNALS):gtest',
+ '<(AOS)/common/common.gyp:queue_testutils',
+ '<(AOS)/common/logging/logging.gyp:queue_logging',
+ '<(AOS)/common/common.gyp:queues',
+ '<(AOS)/common/common.gyp:time',
+ '<(AOS)/linux_code/linux_code.gyp:init',
+ '<(AOS)/common/actions/actions.gyp:action_lib',
+ '<(DEPTH)/y2015/control_loops/fridge/fridge.gyp:fridge_queue',
+ '<(DEPTH)/frc971/control_loops/control_loops.gyp:team_number_test_environment',
+ 'score_action_queue',
+ 'score_action_lib',
+ ],
+ },
+ {
+ 'target_name': 'pickup_action_queue',
+ 'type': 'static_library',
+ 'sources': ['pickup_action.q'],
+ 'variables': {
+ 'header_path': 'y2015/actors',
+ },
+ 'dependencies': [
+ '<(AOS)/common/actions/actions.gyp:action_queue',
+ ],
+ 'export_dependent_settings': [
+ '<(AOS)/common/actions/actions.gyp:action_queue',
+ ],
+ 'includes': ['../../aos/build/queues.gypi'],
+ },
+ {
+ 'target_name': 'pickup_action_lib',
+ 'type': 'static_library',
+ 'sources': [
+ 'pickup_actor.cc',
+ ],
+ 'dependencies': [
+ 'pickup_action_queue',
+ '<(AOS)/build/aos.gyp:logging',
+ '<(AOS)/common/actions/actions.gyp:action_lib',
+ '<(AOS)/common/controls/controls.gyp:control_loop',
+ '<(DEPTH)/y2015/control_loops/claw/claw.gyp:claw_queue',
+ ],
+ 'export_dependent_settings': [
+ '<(AOS)/common/actions/actions.gyp:action_lib',
+ 'pickup_action_queue',
+ ],
+ },
+ {
+ 'target_name': 'pickup_action',
+ 'type': 'executable',
+ 'sources': [
+ 'pickup_actor_main.cc',
+ ],
+ 'dependencies': [
+ '<(AOS)/linux_code/linux_code.gyp:init',
+ '<(AOS)/common/actions/actions.gyp:action_lib',
+ 'pickup_action_queue',
+ 'pickup_action_lib',
+ ],
+ },
+ {
+ 'target_name': 'can_pickup_action_queue',
+ 'type': 'static_library',
+ 'sources': ['can_pickup_action.q'],
+ 'variables': {
+ 'header_path': 'y2015/actors',
+ },
+ 'dependencies': [
+ '<(AOS)/common/actions/actions.gyp:action_queue',
+ ],
+ 'export_dependent_settings': [
+ '<(AOS)/common/actions/actions.gyp:action_queue',
+ ],
+ 'includes': ['../../aos/build/queues.gypi'],
+ },
+ {
+ 'target_name': 'can_pickup_action_lib',
+ 'type': 'static_library',
+ 'sources': [
+ 'can_pickup_actor.cc',
+ ],
+ 'dependencies': [
+ 'fridge_profile_lib',
+ 'can_pickup_action_queue',
+ '<(AOS)/build/aos.gyp:logging',
+ '<(AOS)/common/util/util.gyp:phased_loop',
+ '<(AOS)/common/actions/actions.gyp:action_lib',
+ '<(DEPTH)/y2015/y2015.gyp:constants',
+ '<(DEPTH)/y2015/control_loops/claw/claw.gyp:claw_queue',
+ '<(AOS)/common/controls/controls.gyp:control_loop',
+ ],
+ 'export_dependent_settings': [
+ 'fridge_profile_lib',
+ '<(AOS)/common/actions/actions.gyp:action_lib',
+ 'can_pickup_action_queue',
+ ],
+ },
+ {
+ 'target_name': 'can_pickup_action',
+ 'type': 'executable',
+ 'sources': [
+ 'can_pickup_actor_main.cc',
+ ],
+ 'dependencies': [
+ '<(AOS)/linux_code/linux_code.gyp:init',
+ '<(AOS)/common/actions/actions.gyp:action_lib',
+ 'can_pickup_action_queue',
+ 'can_pickup_action_lib',
+ ],
+ },
+ {
+ 'target_name': 'horizontal_can_pickup_action_queue',
+ 'type': 'static_library',
+ 'sources': ['horizontal_can_pickup_action.q'],
+ 'variables': {
+ 'header_path': 'y2015/actors',
+ },
+ 'dependencies': [
+ '<(AOS)/common/actions/actions.gyp:action_queue',
+ ],
+ 'export_dependent_settings': [
+ '<(AOS)/common/actions/actions.gyp:action_queue',
+ ],
+ 'includes': ['../../aos/build/queues.gypi'],
+ },
+ {
+ 'target_name': 'horizontal_can_pickup_action_lib',
+ 'type': 'static_library',
+ 'sources': [
+ 'horizontal_can_pickup_actor.cc',
+ ],
+ 'dependencies': [
+ 'fridge_profile_lib',
+ 'horizontal_can_pickup_action_queue',
+ '<(AOS)/build/aos.gyp:logging',
+ '<(AOS)/common/util/util.gyp:phased_loop',
+ '<(AOS)/common/actions/actions.gyp:action_lib',
+ '<(DEPTH)/y2015/y2015.gyp:constants',
+ '<(DEPTH)/y2015/control_loops/claw/claw.gyp:claw_queue',
+ '<(AOS)/common/controls/controls.gyp:control_loop',
+ ],
+ 'export_dependent_settings': [
+ 'fridge_profile_lib',
+ '<(AOS)/common/actions/actions.gyp:action_lib',
+ 'horizontal_can_pickup_action_queue',
+ ],
+ },
+ {
+ 'target_name': 'horizontal_can_pickup_action',
+ 'type': 'executable',
+ 'sources': [
+ 'horizontal_can_pickup_actor_main.cc',
+ ],
+ 'dependencies': [
+ '<(AOS)/linux_code/linux_code.gyp:init',
+ '<(AOS)/common/actions/actions.gyp:action_lib',
+ 'horizontal_can_pickup_action_queue',
+ 'horizontal_can_pickup_action_lib',
+ ],
+ },
+ {
+ 'target_name': 'held_to_lift_action_queue',
+ 'type': 'static_library',
+ 'sources': ['held_to_lift_action.q'],
+ 'variables': {
+ 'header_path': 'y2015/actors',
+ },
+ 'dependencies': [
+ '<(AOS)/common/actions/actions.gyp:action_queue',
+ 'lift_action_params',
+ ],
+ 'export_dependent_settings': [
+ '<(AOS)/common/actions/actions.gyp:action_queue',
+ 'lift_action_params',
+ ],
+ 'includes': ['../../aos/build/queues.gypi'],
+ },
+ {
+ 'target_name': 'held_to_lift_action_lib',
+ 'type': 'static_library',
+ 'sources': [
+ 'held_to_lift_actor.cc',
+ ],
+ 'dependencies': [
+ 'fridge_profile_lib',
+ 'held_to_lift_action_queue',
+ 'lift_action_lib',
+ '<(AOS)/build/aos.gyp:logging',
+ '<(AOS)/common/util/util.gyp:phased_loop',
+ '<(AOS)/common/actions/actions.gyp:action_lib',
+ '<(DEPTH)/y2015/y2015.gyp:constants',
+ '<(DEPTH)/y2015/control_loops/claw/claw.gyp:claw_queue',
+ '<(AOS)/common/controls/controls.gyp:control_loop',
+ ],
+ 'export_dependent_settings': [
+ '<(AOS)/common/actions/actions.gyp:action_lib',
+ 'held_to_lift_action_queue',
+ 'fridge_profile_lib',
+ ],
+ },
+ {
+ 'target_name': 'held_to_lift_action',
+ 'type': 'executable',
+ 'sources': [
+ 'held_to_lift_actor_main.cc',
+ ],
+ 'dependencies': [
+ '<(AOS)/linux_code/linux_code.gyp:init',
+ '<(AOS)/common/actions/actions.gyp:action_lib',
+ 'held_to_lift_action_queue',
+ 'held_to_lift_action_lib',
+ ],
+ },
+ {
+ 'target_name': 'stack_and_hold_action_queue',
+ 'type': 'static_library',
+ 'sources': ['stack_and_hold_action.q'],
+ 'variables': {
+ 'header_path': 'y2015/actors',
+ },
+ 'dependencies': [
+ '<(AOS)/common/actions/actions.gyp:action_queue',
+ 'stack_action_params',
+ ],
+ 'export_dependent_settings': [
+ '<(AOS)/common/actions/actions.gyp:action_queue',
+ 'stack_action_params',
+ ],
+ 'includes': ['../../aos/build/queues.gypi'],
+ },
+ {
+ 'target_name': 'stack_and_hold_action_lib',
+ 'type': 'static_library',
+ 'sources': [
+ 'stack_and_hold_actor.cc',
+ ],
+ 'dependencies': [
+ 'fridge_profile_lib',
+ 'stack_and_hold_action_queue',
+ 'stack_action_lib',
+ '<(AOS)/build/aos.gyp:logging',
+ '<(AOS)/common/util/util.gyp:phased_loop',
+ '<(AOS)/common/actions/actions.gyp:action_lib',
+ '<(DEPTH)/y2015/y2015.gyp:constants',
+ '<(DEPTH)/y2015/control_loops/claw/claw.gyp:claw_queue',
+ '<(AOS)/common/controls/controls.gyp:control_loop',
+ ],
+ 'export_dependent_settings': [
+ '<(AOS)/common/actions/actions.gyp:action_lib',
+ 'stack_and_hold_action_queue',
+ 'fridge_profile_lib',
+ ],
+ },
+ {
+ 'target_name': 'stack_and_hold_action',
+ 'type': 'executable',
+ 'sources': [
+ 'stack_and_hold_actor_main.cc',
+ ],
+ 'dependencies': [
+ '<(AOS)/linux_code/linux_code.gyp:init',
+ '<(AOS)/common/actions/actions.gyp:action_lib',
+ 'stack_and_hold_action_queue',
+ 'stack_and_hold_action_lib',
+ ],
+ },
+ {
+ 'target_name': 'stack_and_lift_action_queue',
+ 'type': 'static_library',
+ 'sources': ['stack_and_lift_action.q'],
+ 'variables': {
+ 'header_path': 'y2015/actors',
+ },
+ 'dependencies': [
+ '<(AOS)/common/actions/actions.gyp:action_queue',
+ 'stack_action_params',
+ 'lift_action_params',
+ ],
+ 'export_dependent_settings': [
+ '<(AOS)/common/actions/actions.gyp:action_queue',
+ 'stack_action_params',
+ 'lift_action_params',
+ ],
+ 'includes': ['../../aos/build/queues.gypi'],
+ },
+ {
+ 'target_name': 'stack_and_lift_action_lib',
+ 'type': 'static_library',
+ 'sources': [
+ 'stack_and_lift_actor.cc',
+ ],
+ 'dependencies': [
+ 'fridge_profile_lib',
+ 'stack_and_lift_action_queue',
+ 'stack_action_lib',
+ 'lift_action_lib',
+ '<(AOS)/build/aos.gyp:logging',
+ '<(AOS)/common/util/util.gyp:phased_loop',
+ '<(AOS)/common/actions/actions.gyp:action_lib',
+ '<(DEPTH)/y2015/y2015.gyp:constants',
+ '<(DEPTH)/y2015/control_loops/claw/claw.gyp:claw_queue',
+ '<(AOS)/common/controls/controls.gyp:control_loop',
+ ],
+ 'export_dependent_settings': [
+ '<(AOS)/common/actions/actions.gyp:action_lib',
+ 'stack_and_lift_action_queue',
+ 'fridge_profile_lib',
+ ],
+ },
+ {
+ 'target_name': 'stack_and_lift_action',
+ 'type': 'executable',
+ 'sources': [
+ 'stack_and_lift_actor_main.cc',
+ ],
+ 'dependencies': [
+ '<(AOS)/linux_code/linux_code.gyp:init',
+ '<(AOS)/common/actions/actions.gyp:action_lib',
+ 'stack_and_lift_action_queue',
+ 'stack_and_lift_action_lib',
+ ],
+ },
+ {
+ # This is a wrapper target to avoid adding the directory with a stale
+ # stack_action_params.q.h to the compiler's include path.
+ 'target_name': 'stack_action_queue',
+ 'type': 'none',
+ 'dependencies': ['stack_action_queue_real'],
+ 'export_dependent_settings': ['stack_action_queue_real'],
+ },
+ {
+ 'target_name': 'stack_action_queue_real',
+ 'type': 'static_library',
+ 'sources': [
+ 'stack_action.q',
+ ],
+ 'variables': {
+ 'header_path': 'y2015/actors',
+ },
+ 'dependencies': [
+ '<(AOS)/common/actions/actions.gyp:action_queue',
+ 'stack_action_params',
+ ],
+ 'export_dependent_settings': [
+ '<(AOS)/common/actions/actions.gyp:action_queue',
+ 'stack_action_params',
+ ],
+ 'includes': ['../../aos/build/queues.gypi'],
+ },
+ {
+ 'target_name': 'stack_action_params',
+ 'type': 'static_library',
+ 'sources': [
+ 'stack_action_params.q',
+ ],
+ 'variables': {
+ 'header_path': 'y2015/actors',
+ },
+ 'includes': ['../../aos/build/queues.gypi'],
+ },
+ {
+ 'target_name': 'stack_action_test',
+ 'type': 'executable',
+ 'sources': [
+ 'stack_actor_test.cc',
+ ],
+ 'dependencies': [
+ '<(EXTERNALS):gtest',
+ '<(AOS)/common/common.gyp:queue_testutils',
+ '<(AOS)/common/logging/logging.gyp:queue_logging',
+ '<(AOS)/common/common.gyp:queues',
+ '<(AOS)/common/common.gyp:time',
+ '<(AOS)/linux_code/linux_code.gyp:init',
+ '<(AOS)/common/actions/actions.gyp:action_lib',
+ '<(DEPTH)/y2015/control_loops/fridge/fridge.gyp:fridge_queue',
+ '<(DEPTH)/frc971/control_loops/control_loops.gyp:team_number_test_environment',
+ 'stack_action_queue',
+ 'stack_action_lib',
+ ],
+ },
+ {
+ 'target_name': 'stack_action_lib',
+ 'type': 'static_library',
+ 'sources': [
+ 'stack_actor.cc',
+ ],
+ 'dependencies': [
+ 'fridge_profile_lib',
+ 'stack_action_queue',
+ '<(AOS)/build/aos.gyp:logging',
+ '<(AOS)/common/util/util.gyp:phased_loop',
+ '<(AOS)/common/actions/actions.gyp:action_lib',
+ '<(DEPTH)/y2015/y2015.gyp:constants',
+ '<(DEPTH)/y2015/control_loops/claw/claw.gyp:claw_queue',
+ ],
+ 'export_dependent_settings': [
+ 'fridge_profile_lib',
+ '<(AOS)/common/actions/actions.gyp:action_lib',
+ 'stack_action_queue',
+ ],
+ },
+ {
+ 'target_name': 'stack_action',
+ 'type': 'executable',
+ 'sources': [
+ 'stack_actor_main.cc',
+ ],
+ 'dependencies': [
+ '<(AOS)/linux_code/linux_code.gyp:init',
+ '<(AOS)/common/actions/actions.gyp:action_lib',
+ 'stack_action_queue',
+ 'stack_action_lib',
+ ],
+ },
+ {
+ # This is a wrapper target to avoid adding the directory with a stale
+ # lift_action_params.q.h to the compiler's include path.
+ 'target_name': 'lift_action_queue',
+ 'type': 'none',
+ 'dependencies': ['lift_action_queue_real'],
+ 'export_dependent_settings': ['lift_action_queue_real'],
+ },
+ {
+ 'target_name': 'lift_action_queue_real',
+ 'type': 'static_library',
+ 'sources': [
+ 'lift_action.q',
+ ],
+ 'variables': {
+ 'header_path': 'y2015/actors',
+ },
+ 'dependencies': [
+ '<(AOS)/common/actions/actions.gyp:action_queue',
+ 'lift_action_params',
+ ],
+ 'export_dependent_settings': [
+ '<(AOS)/common/actions/actions.gyp:action_queue',
+ 'lift_action_params',
+ ],
+ 'includes': ['../../aos/build/queues.gypi'],
+ },
+ {
+ 'target_name': 'lift_action_params',
+ 'type': 'static_library',
+ 'sources': [
+ 'lift_action_params.q',
+ ],
+ 'variables': {
+ 'header_path': 'y2015/actors',
+ },
+ 'includes': ['../../aos/build/queues.gypi'],
+ },
+ {
+ 'target_name': 'lift_action_lib',
+ 'type': 'static_library',
+ 'sources': [
+ 'lift_actor.cc',
+ ],
+ 'dependencies': [
+ 'fridge_profile_lib',
+ 'lift_action_queue',
+ '<(AOS)/build/aos.gyp:logging',
+ '<(AOS)/common/actions/actions.gyp:action_lib',
+ '<(DEPTH)/y2015/y2015.gyp:constants',
+ '<(DEPTH)/y2015/control_loops/claw/claw.gyp:claw_queue',
+ ],
+ 'export_dependent_settings': [
+ 'fridge_profile_lib',
+ '<(AOS)/common/actions/actions.gyp:action_lib',
+ 'lift_action_queue',
+ ],
+ },
+ {
+ 'target_name': 'lift_action',
+ 'type': 'executable',
+ 'sources': [
+ 'lift_actor_main.cc',
+ ],
+ 'dependencies': [
+ '<(AOS)/linux_code/linux_code.gyp:init',
+ '<(AOS)/common/actions/actions.gyp:action_lib',
+ 'lift_action_queue',
+ 'lift_action_lib',
+ ],
+ },
+ ],
+}
diff --git a/y2015/actors/can_pickup_action.q b/y2015/actors/can_pickup_action.q
new file mode 100644
index 0000000..4846722
--- /dev/null
+++ b/y2015/actors/can_pickup_action.q
@@ -0,0 +1,40 @@
+package frc971.actors;
+
+import "aos/common/actions/actions.q";
+
+// Parameters to send with start.
+// This action picks a right side up can from the claw.
+// It starts by lifting up, then moving the arm out, then lifting the can out of
+// the claw, and then ends with the can inside the bot.
+struct CanPickupParams {
+ // X position for the fridge when picking up.
+ double pickup_x;
+ // Y position for the fridge when picking up.
+ double pickup_y;
+ // Height to move the elevator to to lift the can out of the claw.
+ double lift_height;
+ // Height to use when lifting before moving it back.
+ double pickup_goal_before_move_height;
+ // The height at which to start pulling back.
+ double before_place_height;
+
+ // X at which to start lowering the can.
+ double start_lowering_x;
+ // End position with the can.
+ double end_height;
+ double end_angle;
+};
+
+queue_group CanPickupActionQueueGroup {
+ implements aos.common.actions.ActionQueueGroup;
+
+ message Goal {
+ uint32_t run;
+ CanPickupParams params;
+ };
+
+ queue Goal goal;
+ queue aos.common.actions.Status status;
+};
+
+queue_group CanPickupActionQueueGroup can_pickup_action;
diff --git a/y2015/actors/can_pickup_actor.cc b/y2015/actors/can_pickup_actor.cc
new file mode 100644
index 0000000..d2a191f
--- /dev/null
+++ b/y2015/actors/can_pickup_actor.cc
@@ -0,0 +1,122 @@
+#include <math.h>
+
+#include "aos/common/time.h"
+#include "aos/common/util/phased_loop.h"
+
+#include "y2015/actors/can_pickup_actor.h"
+#include "y2015/constants.h"
+#include "y2015/control_loops/claw/claw.q.h"
+
+using ::frc971::control_loops::fridge_queue;
+
+namespace frc971 {
+namespace actors {
+namespace {
+constexpr ProfileParams kHorizontalMove{1.1, 1.8};
+constexpr ProfileParams kVerticalMove{0.3, 2.0};
+constexpr ProfileParams kFastHorizontalMove{1.25, 5.0};
+constexpr ProfileParams kFastVerticalMove{0.40, 2.0};
+constexpr ProfileParams kPureVerticalMove{1.20, 5.0};
+} // namespace
+
+CanPickupActor::CanPickupActor(CanPickupActionQueueGroup *queues)
+ : FridgeActorBase<CanPickupActionQueueGroup>(queues) {}
+
+double CanPickupActor::CurrentGoalX() {
+ fridge_queue.status.FetchLatest();
+ if (!fridge_queue.status.get()) {
+ LOG(ERROR, "Reading from fridge status queue failed.\n");
+ return 0.0;
+ }
+
+ return fridge_queue.status->goal_x;
+}
+
+double CanPickupActor::CurrentGoalHeight() {
+ fridge_queue.status.FetchLatest();
+ if (!fridge_queue.status.get()) {
+ LOG(ERROR, "Reading from fridge status queue failed.\n");
+ return 0.0;
+ }
+
+ return fridge_queue.status->goal_y;
+}
+
+bool CanPickupActor::RunAction(const CanPickupParams ¶ms) {
+ // Make sure the claw is down.
+ {
+ auto message = control_loops::claw_queue.goal.MakeMessage();
+ message->angle = 0.0;
+ message->angular_velocity = 0.0;
+ message->intake = 0.0;
+ message->rollers_closed = true;
+ message->max_velocity = 6.0;
+ message->max_acceleration = 10.0;
+
+ LOG_STRUCT(DEBUG, "Sending claw down goal", *message);
+ message.Send();
+ }
+
+ // Go around the can.
+ DoFridgeXYProfile(params.pickup_x, params.pickup_y, kFastHorizontalMove,
+ kFastVerticalMove, true, false, false);
+ if (ShouldCancel()) return true;
+
+ if (!StartFridgeXYProfile(
+ params.pickup_x, params.pickup_goal_before_move_height,
+ kHorizontalMove, kPureVerticalMove, true, true, true)) {
+ return false;
+ }
+ {
+ auto message = control_loops::claw_queue.goal.MakeMessage();
+ message->angle = 0.0;
+ message->angular_velocity = 0.0;
+ message->intake = 0.0;
+ message->rollers_closed = false;
+ message->max_velocity = 6.0;
+ message->max_acceleration = 10.0;
+
+ LOG_STRUCT(DEBUG, "Sending claw down goal", *message);
+ message.Send();
+ }
+
+ bool above_claw = false;
+ while (true) {
+ if (CurrentGoalHeight() > params.lift_height && !above_claw) {
+ if (!StartFridgeXYProfile(0.0, params.before_place_height,
+ kHorizontalMove, kVerticalMove, true, true,
+ true)) {
+ return false;
+ }
+ above_claw = true;
+ }
+ if (CurrentGoalX() < params.start_lowering_x) {
+ // Getting close, start lowering.
+ LOG(DEBUG, "Starting to lower the can onto the tray.\n");
+ break;
+ }
+ ProfileStatus status =
+ IterateXYProfile(0.0, params.before_place_height, kHorizontalMove,
+ kVerticalMove, true, true, true);
+ if (status == DONE || status == CANCELED) {
+ break;
+ }
+ }
+ if (ShouldCancel()) return true;
+
+ // Lower it.
+ DoFridgeXYProfile(0.0, params.end_height, kHorizontalMove, kPureVerticalMove,
+ true);
+ if (ShouldCancel()) return true;
+
+ return true;
+}
+
+::std::unique_ptr<CanPickupAction> MakeCanPickupAction(
+ const CanPickupParams ¶ms) {
+ return ::std::unique_ptr<CanPickupAction>(
+ new CanPickupAction(&::frc971::actors::can_pickup_action, params));
+}
+
+} // namespace actors
+} // namespace frc971
diff --git a/y2015/actors/can_pickup_actor.h b/y2015/actors/can_pickup_actor.h
new file mode 100644
index 0000000..f0b1ec9
--- /dev/null
+++ b/y2015/actors/can_pickup_actor.h
@@ -0,0 +1,37 @@
+#ifndef Y2015_ACTORS_CAN_PICKUP_ACTOR_H_
+#define Y2015_ACTORS_CAN_PICKUP_ACTOR_H_
+
+#include <stdint.h>
+
+#include <memory>
+
+#include "aos/common/actions/actions.h"
+#include "aos/common/actions/actor.h"
+#include "y2015/actors/can_pickup_action.q.h"
+#include "y2015/actors/fridge_profile_lib.h"
+
+namespace frc971 {
+namespace actors {
+
+class CanPickupActor : public FridgeActorBase<CanPickupActionQueueGroup> {
+ public:
+ explicit CanPickupActor(CanPickupActionQueueGroup *queues);
+
+ bool RunAction(const CanPickupParams ¶ms) override;
+
+ private:
+ double CurrentGoalHeight();
+ double CurrentGoalX();
+};
+
+typedef aos::common::actions::TypedAction<CanPickupActionQueueGroup>
+ CanPickupAction;
+
+// Makes a new CanPickupActor action.
+::std::unique_ptr<CanPickupAction> MakeCanPickupAction(
+ const CanPickupParams ¶ms);
+
+} // namespace actors
+} // namespace frc971
+
+#endif
diff --git a/y2015/actors/can_pickup_actor_main.cc b/y2015/actors/can_pickup_actor_main.cc
new file mode 100644
index 0000000..bed9412
--- /dev/null
+++ b/y2015/actors/can_pickup_actor_main.cc
@@ -0,0 +1,15 @@
+#include <stdio.h>
+
+#include "aos/linux_code/init.h"
+#include "y2015/actors/can_pickup_action.q.h"
+#include "y2015/actors/can_pickup_actor.h"
+
+int main(int /*argc*/, char* /*argv*/ []) {
+ ::aos::Init();
+
+ ::frc971::actors::CanPickupActor can_pickup(&::frc971::actors::can_pickup_action);
+ can_pickup.Run();
+
+ ::aos::Cleanup();
+ return 0;
+}
diff --git a/y2015/actors/drivetrain_action.q b/y2015/actors/drivetrain_action.q
new file mode 100644
index 0000000..9ad55d3
--- /dev/null
+++ b/y2015/actors/drivetrain_action.q
@@ -0,0 +1,29 @@
+package frc971.actors;
+
+import "aos/common/actions/actions.q";
+
+// Parameters to send with start.
+struct DrivetrainActionParams {
+ double left_initial_position;
+ double right_initial_position;
+ double y_offset;
+ double theta_offset;
+ double maximum_velocity;
+ double maximum_acceleration;
+ double maximum_turn_velocity;
+ double maximum_turn_acceleration;
+};
+
+queue_group DrivetrainActionQueueGroup {
+ implements aos.common.actions.ActionQueueGroup;
+
+ message Goal {
+ uint32_t run;
+ DrivetrainActionParams params;
+ };
+
+ queue Goal goal;
+ queue aos.common.actions.Status status;
+};
+
+queue_group DrivetrainActionQueueGroup drivetrain_action;
diff --git a/y2015/actors/drivetrain_actor.cc b/y2015/actors/drivetrain_actor.cc
new file mode 100644
index 0000000..9ef81c5
--- /dev/null
+++ b/y2015/actors/drivetrain_actor.cc
@@ -0,0 +1,168 @@
+#include "y2015/actors/drivetrain_actor.h"
+
+#include <functional>
+#include <numeric>
+
+#include <Eigen/Dense>
+
+#include "aos/common/util/phased_loop.h"
+#include "aos/common/logging/logging.h"
+#include "aos/common/util/trapezoid_profile.h"
+#include "aos/common/commonmath.h"
+#include "aos/common/time.h"
+
+#include "y2015/actors/drivetrain_actor.h"
+#include "y2015/constants.h"
+#include "y2015/control_loops/drivetrain/drivetrain.q.h"
+
+namespace frc971 {
+namespace actors {
+
+DrivetrainActor::DrivetrainActor(actors::DrivetrainActionQueueGroup* s)
+ : aos::common::actions::ActorBase<actors::DrivetrainActionQueueGroup>(s) {}
+
+bool DrivetrainActor::RunAction(const actors::DrivetrainActionParams ¶ms) {
+ static const auto K = constants::GetValues().make_drivetrain_loop().K();
+
+ const double yoffset = params.y_offset;
+ const double turn_offset =
+ params.theta_offset * constants::GetValues().turn_width / 2.0;
+ LOG(INFO, "Going to move %f and turn %f\n", yoffset, turn_offset);
+
+ // Measured conversion to get the distance right.
+ ::aos::util::TrapezoidProfile profile(::aos::time::Time::InMS(5));
+ ::aos::util::TrapezoidProfile turn_profile(::aos::time::Time::InMS(5));
+ const double goal_velocity = 0.0;
+ const double epsilon = 0.01;
+ ::Eigen::Matrix<double, 2, 1> left_goal_state, right_goal_state;
+
+ profile.set_maximum_acceleration(params.maximum_acceleration);
+ profile.set_maximum_velocity(params.maximum_velocity);
+ turn_profile.set_maximum_acceleration(params.maximum_turn_acceleration *
+ constants::GetValues().turn_width /
+ 2.0);
+ turn_profile.set_maximum_velocity(params.maximum_turn_velocity *
+ constants::GetValues().turn_width / 2.0);
+
+ while (true) {
+ ::aos::time::PhasedLoopXMS(5, 2500);
+
+ control_loops::drivetrain_queue.status.FetchLatest();
+ if (control_loops::drivetrain_queue.status.get()) {
+ const auto& status = *control_loops::drivetrain_queue.status;
+ if (::std::abs(status.uncapped_left_voltage -
+ status.uncapped_right_voltage) > 24) {
+ LOG(DEBUG, "spinning in place\n");
+ // They're more than 24V apart, so stop moving forwards and let it deal
+ // with spinning first.
+ profile.SetGoal(
+ (status.filtered_left_position + status.filtered_right_position -
+ params.left_initial_position - params.right_initial_position) /
+ 2.0);
+ } else {
+ static const double divisor = K(0, 0) + K(0, 2);
+ double dx_left, dx_right;
+ if (status.uncapped_left_voltage > 12.0) {
+ dx_left = (status.uncapped_left_voltage - 12.0) / divisor;
+ } else if (status.uncapped_left_voltage < -12.0) {
+ dx_left = (status.uncapped_left_voltage + 12.0) / divisor;
+ } else {
+ dx_left = 0;
+ }
+ if (status.uncapped_right_voltage > 12.0) {
+ dx_right = (status.uncapped_right_voltage - 12.0) / divisor;
+ } else if (status.uncapped_right_voltage < -12.0) {
+ dx_right = (status.uncapped_right_voltage + 12.0) / divisor;
+ } else {
+ dx_right = 0;
+ }
+ double dx;
+ if (dx_left == 0 && dx_right == 0) {
+ dx = 0;
+ } else if (dx_left != 0 && dx_right != 0 &&
+ ::aos::sign(dx_left) != ::aos::sign(dx_right)) {
+ // Both saturating in opposite directions. Don't do anything.
+ LOG(DEBUG, "Saturating opposite ways, not adjusting\n");
+ dx = 0;
+ } else if (::std::abs(dx_left) > ::std::abs(dx_right)) {
+ dx = dx_left;
+ } else {
+ dx = dx_right;
+ }
+ if (dx != 0) {
+ LOG(DEBUG, "adjusting goal by %f\n", dx);
+ profile.MoveGoal(-dx);
+ }
+ }
+ } else {
+ // If we ever get here, that's bad and we should just give up
+ LOG(ERROR, "no drivetrain status!\n");
+ return false;
+ }
+
+ const auto drive_profile_goal_state =
+ profile.Update(yoffset, goal_velocity);
+ const auto turn_profile_goal_state = turn_profile.Update(turn_offset, 0.0);
+ left_goal_state = drive_profile_goal_state - turn_profile_goal_state;
+ right_goal_state = drive_profile_goal_state + turn_profile_goal_state;
+
+ if (::std::abs(drive_profile_goal_state(0, 0) - yoffset) < epsilon &&
+ ::std::abs(turn_profile_goal_state(0, 0) - turn_offset) < epsilon) {
+ break;
+ }
+
+ if (ShouldCancel()) return true;
+
+ LOG(DEBUG, "Driving left to %f, right to %f\n",
+ left_goal_state(0, 0) + params.left_initial_position,
+ right_goal_state(0, 0) + params.right_initial_position);
+ control_loops::drivetrain_queue.goal.MakeWithBuilder()
+ .control_loop_driving(true)
+ //.highgear(false)
+ .left_goal(left_goal_state(0, 0) + params.left_initial_position)
+ .right_goal(right_goal_state(0, 0) + params.right_initial_position)
+ .left_velocity_goal(left_goal_state(1, 0))
+ .right_velocity_goal(right_goal_state(1, 0))
+ .Send();
+ }
+ if (ShouldCancel()) return true;
+ control_loops::drivetrain_queue.status.FetchLatest();
+ while (!control_loops::drivetrain_queue.status.get()) {
+ LOG(WARNING,
+ "No previous drivetrain status packet, trying to fetch again\n");
+ control_loops::drivetrain_queue.status.FetchNextBlocking();
+ if (ShouldCancel()) return true;
+ }
+ while (true) {
+ if (ShouldCancel()) return true;
+ const double kPositionThreshold = 0.05;
+
+ const double left_error = ::std::abs(
+ control_loops::drivetrain_queue.status->filtered_left_position -
+ (left_goal_state(0, 0) + params.left_initial_position));
+ const double right_error = ::std::abs(
+ control_loops::drivetrain_queue.status->filtered_right_position -
+ (right_goal_state(0, 0) + params.right_initial_position));
+ const double velocity_error =
+ ::std::abs(control_loops::drivetrain_queue.status->robot_speed);
+ if (left_error < kPositionThreshold && right_error < kPositionThreshold &&
+ velocity_error < 0.2) {
+ break;
+ } else {
+ LOG(DEBUG, "Drivetrain error is %f, %f, %f\n", left_error, right_error,
+ velocity_error);
+ }
+ control_loops::drivetrain_queue.status.FetchNextBlocking();
+ }
+ LOG(INFO, "Done moving\n");
+ return true;
+}
+
+::std::unique_ptr<DrivetrainAction> MakeDrivetrainAction(
+ const ::frc971::actors::DrivetrainActionParams& params) {
+ return ::std::unique_ptr<DrivetrainAction>(
+ new DrivetrainAction(&::frc971::actors::drivetrain_action, params));
+}
+
+} // namespace actors
+} // namespace frc971
diff --git a/y2015/actors/drivetrain_actor.h b/y2015/actors/drivetrain_actor.h
new file mode 100644
index 0000000..369c6ed
--- /dev/null
+++ b/y2015/actors/drivetrain_actor.h
@@ -0,0 +1,31 @@
+#ifndef Y2015_ACTIONS_DRIVETRAIN_ACTION_H_
+#define Y2015_ACTIONS_DRIVETRAIN_ACTION_H_
+
+#include <memory>
+
+#include "y2015/actors/drivetrain_action.q.h"
+#include "aos/common/actions/actor.h"
+#include "aos/common/actions/actions.h"
+
+namespace frc971 {
+namespace actors {
+
+class DrivetrainActor
+ : public aos::common::actions::ActorBase<DrivetrainActionQueueGroup> {
+ public:
+ explicit DrivetrainActor(DrivetrainActionQueueGroup* s);
+
+ bool RunAction(const actors::DrivetrainActionParams ¶ms) override;
+};
+
+typedef aos::common::actions::TypedAction<DrivetrainActionQueueGroup>
+ DrivetrainAction;
+
+// Makes a new DrivetrainActor action.
+::std::unique_ptr<DrivetrainAction> MakeDrivetrainAction(
+ const ::frc971::actors::DrivetrainActionParams& params);
+
+} // namespace actors
+} // namespace frc971
+
+#endif
diff --git a/y2015/actors/drivetrain_actor_main.cc b/y2015/actors/drivetrain_actor_main.cc
new file mode 100644
index 0000000..7e461ff
--- /dev/null
+++ b/y2015/actors/drivetrain_actor_main.cc
@@ -0,0 +1,18 @@
+#include <stdio.h>
+
+#include "aos/linux_code/init.h"
+#include "y2015/actors/drivetrain_action.q.h"
+#include "y2015/actors/drivetrain_actor.h"
+
+using ::aos::time::Time;
+
+int main(int /*argc*/, char * /*argv*/[]) {
+ ::aos::Init();
+
+ frc971::actors::DrivetrainActor drivetrain(
+ &::frc971::actors::drivetrain_action);
+ drivetrain.Run();
+
+ ::aos::Cleanup();
+ return 0;
+}
diff --git a/y2015/actors/fridge_profile_lib.cc b/y2015/actors/fridge_profile_lib.cc
new file mode 100644
index 0000000..1a1866f
--- /dev/null
+++ b/y2015/actors/fridge_profile_lib.cc
@@ -0,0 +1 @@
+#include "y2015/actors/fridge_profile_lib.h"
diff --git a/y2015/actors/fridge_profile_lib.h b/y2015/actors/fridge_profile_lib.h
new file mode 100644
index 0000000..ce75f75
--- /dev/null
+++ b/y2015/actors/fridge_profile_lib.h
@@ -0,0 +1,285 @@
+#ifndef Y2015_ACTORS_FRIDGE_PROFILE_LIB_H_
+#define Y2015_ACTORS_FRIDGE_PROFILE_LIB_H_
+
+#include <cmath>
+
+#include "aos/common/actions/actor.h"
+#include "aos/common/util/phased_loop.h"
+#include "y2015/control_loops/fridge/fridge.q.h"
+
+namespace frc971 {
+namespace actors {
+
+struct ProfileParams {
+ double velocity;
+ double acceleration;
+};
+
+// Base class to provide helper utilities to all Actors who want to control the
+// fridge.
+template <typename T>
+class FridgeActorBase : public aos::common::actions::ActorBase<T> {
+ public:
+ FridgeActorBase(T *queues) : aos::common::actions::ActorBase<T>(queues) {}
+
+ protected:
+ void DoFridgeProfile(double height, double angle,
+ ProfileParams elevator_parameters,
+ ProfileParams arm_parameters, bool grabbers) {
+ DoFridgeProfile(height, angle, elevator_parameters, arm_parameters,
+ grabbers, grabbers, grabbers);
+ }
+
+ bool StartFridgeProfile(double height, double angle,
+ ProfileParams elevator_parameters,
+ ProfileParams arm_parameters, bool top_grabbers,
+ bool front_grabbers, bool back_grabbers) {
+ auto new_fridge_goal = control_loops::fridge_queue.goal.MakeMessage();
+ new_fridge_goal->profiling_type = 0;
+ new_fridge_goal->max_velocity = elevator_parameters.velocity;
+ new_fridge_goal->max_acceleration = elevator_parameters.acceleration;
+ new_fridge_goal->height = height;
+ new_fridge_goal->velocity = 0.0;
+ new_fridge_goal->max_angular_velocity = arm_parameters.velocity;
+ new_fridge_goal->max_angular_acceleration = arm_parameters.acceleration;
+ new_fridge_goal->angle = angle;
+ new_fridge_goal->angular_velocity = 0.0;
+ new_fridge_goal->grabbers.top_front = top_grabbers;
+ new_fridge_goal->grabbers.top_back = top_grabbers;
+ new_fridge_goal->grabbers.bottom_front = front_grabbers;
+ new_fridge_goal->grabbers.bottom_back = back_grabbers;
+ LOG(INFO, "Starting profile to %f, %f\n", height, angle);
+
+ if (!new_fridge_goal.Send()) {
+ LOG(ERROR, "Failed to send fridge goal\n");
+ return false;
+ }
+ return true;
+ }
+
+ enum ProfileStatus { RUNNING, DONE, CANCELED };
+
+ ProfileStatus IterateProfile(double height, double angle,
+ ProfileParams elevator_parameters,
+ ProfileParams arm_parameters, bool top_grabbers,
+ bool front_grabbers, bool back_grabbers) {
+ if (this->ShouldCancel()) {
+ LOG(INFO, "Canceling fridge movement\n");
+ if (!control_loops::fridge_queue.status.get()) {
+ LOG(WARNING, "no fridge status so can't really cancel\n");
+ return CANCELED;
+ }
+
+ auto new_fridge_goal = control_loops::fridge_queue.goal.MakeMessage();
+ new_fridge_goal->profiling_type = 0;
+ new_fridge_goal->max_velocity = elevator_parameters.velocity;
+ new_fridge_goal->max_acceleration = elevator_parameters.acceleration;
+ new_fridge_goal->height =
+ control_loops::fridge_queue.status->height +
+ (control_loops::fridge_queue.status->goal_velocity *
+ ::std::abs(control_loops::fridge_queue.status->goal_velocity)) /
+ (2.0 * new_fridge_goal->max_acceleration);
+ height = new_fridge_goal->height;
+ new_fridge_goal->velocity = 0.0;
+ new_fridge_goal->max_angular_velocity = arm_parameters.velocity;
+ new_fridge_goal->max_angular_acceleration = arm_parameters.acceleration;
+ new_fridge_goal->angle =
+ control_loops::fridge_queue.status->angle +
+ (control_loops::fridge_queue.status->goal_angular_velocity *
+ ::std::abs(
+ control_loops::fridge_queue.status->goal_angular_velocity)) /
+ (2.0 * new_fridge_goal->max_angular_acceleration);
+ angle = new_fridge_goal->angle;
+ new_fridge_goal->angular_velocity = 0.0;
+ new_fridge_goal->grabbers.top_front = top_grabbers;
+ new_fridge_goal->grabbers.top_back = top_grabbers;
+ new_fridge_goal->grabbers.bottom_front = front_grabbers;
+ new_fridge_goal->grabbers.bottom_back = back_grabbers;
+
+ if (!new_fridge_goal.Send()) {
+ LOG(ERROR, "Failed to send fridge goal\n");
+ }
+ return CANCELED;
+ }
+ control_loops::fridge_queue.status.FetchAnother();
+
+ constexpr double kProfileError = 1e-5;
+ constexpr double kAngleEpsilon = 0.02, kHeightEpsilon = 0.015;
+
+ if (control_loops::fridge_queue.status->state != 4) {
+ LOG(ERROR, "Fridge no longer running, aborting action\n");
+ return CANCELED;
+ }
+
+ if (::std::abs(control_loops::fridge_queue.status->goal_angle - angle) <
+ kProfileError &&
+ ::std::abs(control_loops::fridge_queue.status->goal_height - height) <
+ kProfileError &&
+ ::std::abs(control_loops::fridge_queue.status->goal_angular_velocity) <
+ kProfileError &&
+ ::std::abs(control_loops::fridge_queue.status->goal_velocity) <
+ kProfileError) {
+ LOG(INFO, "Profile done.\n");
+ if (::std::abs(control_loops::fridge_queue.status->angle - angle) <
+ kAngleEpsilon &&
+ ::std::abs(control_loops::fridge_queue.status->height -
+ height) < kHeightEpsilon) {
+ LOG(INFO, "Near goal, done.\n");
+ return DONE;
+ }
+ }
+
+ return RUNNING;
+ }
+
+ void DoFridgeProfile(double height, double angle,
+ ProfileParams elevator_parameters,
+ ProfileParams arm_parameters, bool top_grabbers,
+ bool front_grabbers, bool back_grabbers) {
+ if (!StartFridgeProfile(height, angle, elevator_parameters, arm_parameters,
+ top_grabbers, front_grabbers, back_grabbers)) {
+ return;
+ }
+
+ while (true) {
+ ProfileStatus status =
+ IterateProfile(height, angle, elevator_parameters, arm_parameters,
+ top_grabbers, front_grabbers, back_grabbers);
+ if (status == DONE || status == CANCELED) {
+ return;
+ }
+ }
+ }
+
+ void DoFridgeXYProfile(double x, double y, ProfileParams x_parameters,
+ ProfileParams y_parameters, bool grabbers) {
+ DoFridgeXYProfile(x, y, x_parameters, y_parameters, grabbers, grabbers,
+ grabbers);
+ }
+
+ void DoFridgeXYProfile(double x, double y, ProfileParams x_parameters,
+ ProfileParams y_parameters, bool top_grabbers,
+ bool front_grabbers, bool back_grabbers) {
+ if (!StartFridgeXYProfile(x, y, x_parameters, y_parameters, top_grabbers,
+ front_grabbers, back_grabbers)) {
+ return;
+ }
+
+ while (true) {
+ ProfileStatus status =
+ IterateXYProfile(x, y, x_parameters, y_parameters, top_grabbers,
+ front_grabbers, back_grabbers);
+ if (status == DONE || status == CANCELED) {
+ return;
+ }
+ }
+ }
+
+ void CancelXYMotion(ProfileParams x_parameters, ProfileParams y_parameters,
+ bool top_grabbers, bool front_grabbers,
+ bool back_grabbers) {
+ LOG(INFO, "Canceling fridge movement\n");
+ if (!control_loops::fridge_queue.status.get()) {
+ LOG(WARNING, "no fridge status so can't really cancel\n");
+ return;
+ }
+
+ auto new_fridge_goal = control_loops::fridge_queue.goal.MakeMessage();
+ new_fridge_goal->profiling_type = 1;
+ new_fridge_goal->max_x_velocity = x_parameters.velocity;
+ new_fridge_goal->max_x_acceleration = x_parameters.acceleration;
+ new_fridge_goal->x =
+ control_loops::fridge_queue.status->x +
+ (control_loops::fridge_queue.status->goal_x_velocity *
+ ::std::abs(control_loops::fridge_queue.status->goal_x_velocity)) /
+ (2.0 * new_fridge_goal->max_x_acceleration);
+ new_fridge_goal->x_velocity = 0.0;
+
+ new_fridge_goal->max_y_velocity = y_parameters.velocity;
+ new_fridge_goal->max_y_acceleration = y_parameters.acceleration;
+ new_fridge_goal->y =
+ control_loops::fridge_queue.status->y +
+ (control_loops::fridge_queue.status->goal_y_velocity *
+ ::std::abs(control_loops::fridge_queue.status->goal_y_velocity)) /
+ (2.0 * new_fridge_goal->max_y_acceleration);
+ new_fridge_goal->y_velocity = 0.0;
+
+ new_fridge_goal->grabbers.top_front = top_grabbers;
+ new_fridge_goal->grabbers.top_back = top_grabbers;
+ new_fridge_goal->grabbers.bottom_front = front_grabbers;
+ new_fridge_goal->grabbers.bottom_back = back_grabbers;
+
+ if (!new_fridge_goal.Send()) {
+ LOG(ERROR, "Failed to send fridge goal\n");
+ }
+ }
+
+ ProfileStatus IterateXYProfile(double x, double y, ProfileParams x_parameters,
+ ProfileParams y_parameters, bool top_grabbers,
+ bool front_grabbers, bool back_grabbers) {
+ if (this->ShouldCancel()) {
+ CancelXYMotion(x_parameters, y_parameters, top_grabbers, front_grabbers,
+ back_grabbers);
+ return CANCELED;
+ }
+ control_loops::fridge_queue.status.FetchAnother();
+
+ constexpr double kProfileError = 1e-5;
+ constexpr double kXEpsilon = 0.02, kYEpsilon = 0.02;
+
+ if (control_loops::fridge_queue.status->state != 4) {
+ LOG(ERROR, "Fridge no longer running, aborting action\n");
+ return CANCELED;
+ }
+
+ if (::std::abs(control_loops::fridge_queue.status->goal_x - x) <
+ kProfileError &&
+ ::std::abs(control_loops::fridge_queue.status->goal_y - y) <
+ kProfileError &&
+ ::std::abs(control_loops::fridge_queue.status->goal_x_velocity) <
+ kProfileError &&
+ ::std::abs(control_loops::fridge_queue.status->goal_y_velocity) <
+ kProfileError) {
+ LOG(INFO, "Profile done.\n");
+ if (::std::abs(control_loops::fridge_queue.status->x - x) < kXEpsilon &&
+ ::std::abs(control_loops::fridge_queue.status->y - y) < kYEpsilon) {
+ LOG(INFO, "Near goal, done.\n");
+ return DONE;
+ }
+ }
+
+ return RUNNING;
+ }
+
+ bool StartFridgeXYProfile(double x, double y, ProfileParams x_parameters,
+ ProfileParams y_parameters, bool top_grabbers,
+ bool front_grabbers, bool back_grabbers) {
+ auto new_fridge_goal = control_loops::fridge_queue.goal.MakeMessage();
+ new_fridge_goal->profiling_type = 1;
+ new_fridge_goal->max_x_velocity = x_parameters.velocity;
+ new_fridge_goal->max_x_acceleration = x_parameters.acceleration;
+ new_fridge_goal->x = x;
+ new_fridge_goal->x_velocity = 0.0;
+
+ new_fridge_goal->max_y_velocity = y_parameters.velocity;
+ new_fridge_goal->max_y_acceleration = y_parameters.acceleration;
+ new_fridge_goal->y = y;
+ new_fridge_goal->y_velocity = 0.0;
+ new_fridge_goal->grabbers.top_front = top_grabbers;
+ new_fridge_goal->grabbers.top_back = top_grabbers;
+ new_fridge_goal->grabbers.bottom_front = front_grabbers;
+ new_fridge_goal->grabbers.bottom_back = back_grabbers;
+ LOG(INFO, "Starting xy profile to %f, %f\n", x, y);
+
+ if (!new_fridge_goal.Send()) {
+ LOG(ERROR, "Failed to send fridge goal\n");
+ return false;
+ }
+ return true;
+ }
+};
+
+} // namespace actors
+} // namespace frc971
+
+#endif // Y2015_ACTORS_FRIDGE_PROFILE_LIB_H_
diff --git a/y2015/actors/held_to_lift_action.q b/y2015/actors/held_to_lift_action.q
new file mode 100644
index 0000000..c2dd689
--- /dev/null
+++ b/y2015/actors/held_to_lift_action.q
@@ -0,0 +1,36 @@
+package frc971.actors;
+
+import "aos/common/actions/actions.q";
+import "y2015/actors/lift_action_params.q";
+
+// Parameters to send with start.
+struct HeldToLiftParams {
+ // The maximum claw value to avoid collisions.
+ double claw_out_angle;
+
+ // The value to move the arm forwards to clear the stack when lowering.
+ double arm_clearance;
+ // End height.
+ double bottom_height;
+ // Amount to wait for the elevator to settle before lifting.
+ double before_lift_settle_time;
+ // Amount to wait to clamp.
+ double clamp_pause_time;
+
+ // Lift parameters
+ LiftParams lift_params;
+};
+
+queue_group HeldToLiftActionQueueGroup {
+ implements aos.common.actions.ActionQueueGroup;
+
+ message Goal {
+ uint32_t run;
+ HeldToLiftParams params;
+ };
+
+ queue Goal goal;
+ queue aos.common.actions.Status status;
+};
+
+queue_group HeldToLiftActionQueueGroup held_to_lift_action;
diff --git a/y2015/actors/held_to_lift_actor.cc b/y2015/actors/held_to_lift_actor.cc
new file mode 100644
index 0000000..2336364
--- /dev/null
+++ b/y2015/actors/held_to_lift_actor.cc
@@ -0,0 +1,114 @@
+#include "y2015/actors/held_to_lift_actor.h"
+
+#include <math.h>
+
+#include "aos/common/time.h"
+#include "y2015/constants.h"
+#include "y2015/actors/fridge_profile_lib.h"
+#include "y2015/actors/lift_actor.h"
+#include "y2015/control_loops/claw/claw.q.h"
+
+namespace frc971 {
+namespace actors {
+namespace {
+constexpr ProfileParams kArmMove{0.6, 2.0};
+constexpr ProfileParams kFastArmMove{1.2, 4.0};
+constexpr ProfileParams kElevatorMove{0.9, 3.0};
+constexpr ProfileParams kFastElevatorMove{1.2, 5.0};
+} // namespace
+
+HeldToLiftActor::HeldToLiftActor(HeldToLiftActionQueueGroup *queues)
+ : FridgeActorBase<HeldToLiftActionQueueGroup>(queues) {}
+
+bool HeldToLiftActor::RunAction(const HeldToLiftParams ¶ms) {
+ control_loops::fridge_queue.status.FetchLatest();
+ if (!control_loops::fridge_queue.status.get()) {
+ return false;
+ }
+
+ // Move claw out of the way.
+ {
+ bool send_goal = true;
+ double claw_goal = params.claw_out_angle;
+ control_loops::claw_queue.status.FetchLatest();
+ if (control_loops::claw_queue.status.get()) {
+ if (control_loops::claw_queue.status->goal_angle < claw_goal) {
+ send_goal = false;
+ }
+ }
+ if (send_goal) {
+ auto message = control_loops::claw_queue.goal.MakeMessage();
+ message->angle = params.claw_out_angle;
+ message->angular_velocity = 0.0;
+ message->intake = 0.0;
+ message->max_velocity = 6.0;
+ message->max_acceleration = 10.0;
+ message->rollers_closed = true;
+
+ LOG_STRUCT(DEBUG, "Sending claw goal", *message);
+ message.Send();
+ }
+ }
+
+ control_loops::fridge_queue.status.FetchLatest();
+ if (!control_loops::fridge_queue.status.get()) {
+ return false;
+ }
+
+ if (control_loops::fridge_queue.status->goal_height != params.bottom_height ||
+ control_loops::fridge_queue.status->goal_angle != 0.0) {
+ // Lower with the fridge clamps open and move it forwards slightly to clear.
+ DoFridgeProfile(control_loops::fridge_queue.status->goal_height,
+ params.arm_clearance, kFastElevatorMove, kFastArmMove,
+ false);
+ if (ShouldCancel()) return true;
+
+ DoFridgeProfile(params.bottom_height, params.arm_clearance,
+ kFastElevatorMove, kFastArmMove, false);
+ if (ShouldCancel()) return true;
+
+ // Move it back to the storage location.
+ DoFridgeProfile(params.bottom_height, 0.0, kElevatorMove, kArmMove, false);
+ if (ShouldCancel()) return true;
+
+ if (!WaitOrCancel(
+ aos::time::Time::InSeconds(params.before_lift_settle_time))) {
+ return true;
+ }
+
+ // Clamp
+ DoFridgeProfile(params.bottom_height, 0.0, kElevatorMove, kArmMove, true);
+ if (ShouldCancel()) return true;
+
+ if (!WaitOrCancel(aos::time::Time::InSeconds(params.clamp_pause_time))) {
+ return true;
+ }
+ }
+
+ {
+ ::std::unique_ptr<LiftAction> lift_action =
+ MakeLiftAction(params.lift_params);
+ lift_action->Start();
+ while (lift_action->Running()) {
+ ::aos::time::PhasedLoopXMS(::aos::controls::kLoopFrequency.ToMSec(),
+ 2500);
+
+ if (ShouldCancel()) {
+ lift_action->Cancel();
+ LOG(WARNING, "Cancelling fridge and claw.\n");
+ return true;
+ }
+ }
+ }
+
+ return true;
+}
+
+::std::unique_ptr<HeldToLiftAction> MakeHeldToLiftAction(
+ const HeldToLiftParams ¶ms) {
+ return ::std::unique_ptr<HeldToLiftAction>(
+ new HeldToLiftAction(&::frc971::actors::held_to_lift_action, params));
+}
+
+} // namespace actors
+} // namespace frc971
diff --git a/y2015/actors/held_to_lift_actor.h b/y2015/actors/held_to_lift_actor.h
new file mode 100644
index 0000000..369fea4
--- /dev/null
+++ b/y2015/actors/held_to_lift_actor.h
@@ -0,0 +1,33 @@
+#ifndef Y2015_ACTORS_HELD_TO_LIFT_ACTOR_H_
+#define Y2015_ACTORS_HELD_TO_LIFT_ACTOR_H_
+
+#include <stdint.h>
+
+#include <memory>
+
+#include "aos/common/actions/actions.h"
+#include "aos/common/actions/actor.h"
+#include "y2015/actors/held_to_lift_action.q.h"
+#include "y2015/actors/fridge_profile_lib.h"
+
+namespace frc971 {
+namespace actors {
+
+class HeldToLiftActor : public FridgeActorBase<HeldToLiftActionQueueGroup> {
+ public:
+ explicit HeldToLiftActor(HeldToLiftActionQueueGroup *queues);
+
+ bool RunAction(const HeldToLiftParams ¶ms) override;
+};
+
+typedef aos::common::actions::TypedAction<HeldToLiftActionQueueGroup>
+ HeldToLiftAction;
+
+// Makes a new HeldToLiftActor action.
+::std::unique_ptr<HeldToLiftAction> MakeHeldToLiftAction(
+ const HeldToLiftParams ¶ms);
+
+} // namespace actors
+} // namespace frc971
+
+#endif // Y2015_ACTORS_HELD_TO_LIFT_ACTOR_H_
diff --git a/y2015/actors/held_to_lift_actor_main.cc b/y2015/actors/held_to_lift_actor_main.cc
new file mode 100644
index 0000000..a5d78e1
--- /dev/null
+++ b/y2015/actors/held_to_lift_actor_main.cc
@@ -0,0 +1,16 @@
+#include <stdio.h>
+
+#include "aos/linux_code/init.h"
+#include "y2015/actors/held_to_lift_action.q.h"
+#include "y2015/actors/held_to_lift_actor.h"
+
+int main(int /*argc*/, char* /*argv*/ []) {
+ ::aos::Init();
+
+ ::frc971::actors::HeldToLiftActor lift(
+ &::frc971::actors::held_to_lift_action);
+ lift.Run();
+
+ ::aos::Cleanup();
+ return 0;
+}
diff --git a/y2015/actors/horizontal_can_pickup_action.q b/y2015/actors/horizontal_can_pickup_action.q
new file mode 100644
index 0000000..c1a1c55
--- /dev/null
+++ b/y2015/actors/horizontal_can_pickup_action.q
@@ -0,0 +1,48 @@
+package frc971.actors;
+
+import "aos/common/actions/actions.q";
+
+// Parameters to send with start.
+// This action picks a horizontal can from the claw.
+struct HorizontalCanPickupParams {
+ // Elevator catch height.
+ double elevator_height;
+ // Angle to move the claw to when placing the base of the can on the robot.
+ double pickup_angle;
+
+ // Time and power to spit the can out before lifting.
+ double spit_time;
+ double spit_power;
+
+ // Time and power to pull the can in when lifted.
+ double suck_time;
+ double suck_power;
+
+ // Time to push down and suck in to slide the claw down on the can.
+ double claw_settle_time;
+ double claw_settle_power;
+
+ // Angle to lift the claw to to lift the can.
+ double claw_full_lift_angle;
+
+ // Angle to move the claw back down to.
+ double claw_end_angle;
+
+ // The end arm and elevator position once we are done lifting.
+ double elevator_end_height;
+ double arm_end_angle;
+};
+
+queue_group HorizontalCanPickupActionQueueGroup {
+ implements aos.common.actions.ActionQueueGroup;
+
+ message Goal {
+ uint32_t run;
+ HorizontalCanPickupParams params;
+ };
+
+ queue Goal goal;
+ queue aos.common.actions.Status status;
+};
+
+queue_group HorizontalCanPickupActionQueueGroup horizontal_can_pickup_action;
diff --git a/y2015/actors/horizontal_can_pickup_actor.cc b/y2015/actors/horizontal_can_pickup_actor.cc
new file mode 100644
index 0000000..fb6e542
--- /dev/null
+++ b/y2015/actors/horizontal_can_pickup_actor.cc
@@ -0,0 +1,161 @@
+#include <math.h>
+
+#include "aos/common/controls/control_loop.h"
+#include "aos/common/time.h"
+#include "aos/common/util/phased_loop.h"
+
+#include "y2015/actors/horizontal_can_pickup_actor.h"
+#include "y2015/actors/fridge_profile_lib.h"
+#include "y2015/constants.h"
+#include "y2015/control_loops/claw/claw.q.h"
+
+namespace frc971 {
+namespace actors {
+namespace {
+constexpr ProfileParams kClawPickup{3.0, 2.0};
+constexpr ProfileParams kClawBackDown{7.0, 10.0};
+constexpr ProfileParams kClawInitialLift{7.0, 8.0};
+
+constexpr ProfileParams kArmMove{1.0, 1.6};
+constexpr ProfileParams kElevatorMove{0.6, 2.2};
+
+constexpr ProfileParams kFastArmMove{2.0, 3.0};
+constexpr ProfileParams kFastElevatorMove{1.0, 3.0};
+
+constexpr double kAngleEpsilon = 0.10;
+constexpr double kGoalAngleEpsilon = 0.01;
+
+} // namespace
+
+HorizontalCanPickupActor::HorizontalCanPickupActor(
+ HorizontalCanPickupActionQueueGroup *queues)
+ : FridgeActorBase<HorizontalCanPickupActionQueueGroup>(queues) {}
+
+bool HorizontalCanPickupActor::WaitUntilGoalNear(double angle) {
+ while (true) {
+ control_loops::claw_queue.status.FetchAnother();
+ if (ShouldCancel()) return false;
+ const double goal_angle = control_loops::claw_queue.status->goal_angle;
+ LOG_STRUCT(DEBUG, "Got claw status", *control_loops::claw_queue.status);
+
+ if (::std::abs(goal_angle - angle) < kGoalAngleEpsilon) {
+ return true;
+ }
+ }
+}
+
+bool HorizontalCanPickupActor::WaitUntilNear(double angle) {
+ while (true) {
+ control_loops::claw_queue.status.FetchAnother();
+ if (ShouldCancel()) return false;
+ const double current_angle = control_loops::claw_queue.status->angle;
+ LOG_STRUCT(DEBUG, "Got claw status", *control_loops::claw_queue.status);
+
+ if (::std::abs(current_angle - angle) < kAngleEpsilon) {
+ return true;
+ }
+ }
+}
+void HorizontalCanPickupActor::MoveArm(double angle, double intake_power) {
+ MoveArm(angle, intake_power, kClawPickup);
+}
+
+void HorizontalCanPickupActor::MoveArm(double angle, double intake_power,
+ const ProfileParams profile_params) {
+ auto message = control_loops::claw_queue.goal.MakeMessage();
+ message->angle = angle;
+ message->max_velocity = profile_params.velocity;
+ message->max_acceleration = profile_params.acceleration;
+ message->angular_velocity = 0.0;
+ message->intake = intake_power;
+ message->rollers_closed = true;
+
+ LOG_STRUCT(DEBUG, "Sending claw goal", *message);
+ message.Send();
+}
+
+bool HorizontalCanPickupActor::RunAction(
+ const HorizontalCanPickupParams ¶ms) {
+ // Go around the can.
+ if (!StartFridgeProfile(params.elevator_height, 0.0, kFastElevatorMove,
+ kFastArmMove, false, false, true)) {
+ return true;
+ }
+
+ control_loops::claw_queue.status.FetchAnother();
+
+ MoveArm(control_loops::claw_queue.status->angle, params.spit_power);
+
+ if (!WaitOrCancel(aos::time::Time::InSeconds(params.spit_time))) {
+ return true;
+ }
+
+ MoveArm(params.pickup_angle, 0.0, kClawInitialLift);
+
+ if (!WaitUntilNear(params.pickup_angle)) {
+ return true;
+ }
+
+ MoveArm(params.pickup_angle, params.suck_power);
+
+ if (!WaitOrCancel(aos::time::Time::InSeconds(params.suck_time))) {
+ return true;
+ }
+
+ MoveArm(0.0, 0.0, kClawBackDown);
+
+ if (!WaitUntilGoalNear(0.0)) {
+ return true;
+ }
+
+ MoveArm(0.0, params.claw_settle_power);
+
+ if (!WaitOrCancel(aos::time::Time::InSeconds(params.claw_settle_time))) {
+ return true;
+ }
+
+ while (true) {
+ ProfileStatus status =
+ IterateProfile(params.elevator_height, 0.0, kFastElevatorMove,
+ kFastArmMove, false, false, true);
+ if (status == DONE) {
+ break;
+ } else if (status == CANCELED) {
+ return true;
+ }
+ }
+
+ MoveArm(params.claw_full_lift_angle, 0.0);
+
+ if (!WaitUntilNear(params.claw_full_lift_angle)) {
+ return true;
+ }
+
+ DoFridgeProfile(params.elevator_height, 0.0, kElevatorMove, kArmMove, false,
+ true, true);
+ if (ShouldCancel()) return true;
+
+ MoveArm(params.claw_end_angle, 7.0);
+
+ if (!WaitUntilNear(params.claw_end_angle)) {
+ return true;
+ }
+ MoveArm(params.claw_end_angle, 0.0);
+
+ if (ShouldCancel()) return true;
+
+ DoFridgeProfile(params.elevator_end_height, params.arm_end_angle,
+ kElevatorMove, kArmMove, false, true, true);
+
+ return true;
+}
+
+::std::unique_ptr<HorizontalCanPickupAction> MakeHorizontalCanPickupAction(
+ const HorizontalCanPickupParams ¶ms) {
+ return ::std::unique_ptr<HorizontalCanPickupAction>(
+ new HorizontalCanPickupAction(
+ &::frc971::actors::horizontal_can_pickup_action, params));
+}
+
+} // namespace actors
+} // namespace frc971
diff --git a/y2015/actors/horizontal_can_pickup_actor.h b/y2015/actors/horizontal_can_pickup_actor.h
new file mode 100644
index 0000000..5510178
--- /dev/null
+++ b/y2015/actors/horizontal_can_pickup_actor.h
@@ -0,0 +1,45 @@
+#ifndef Y2015_ACTORS_HORIZONTAL_CAN_PICKUP_ACTOR_H_
+#define Y2015_ACTORS_HORIZONTAL_CAN_PICKUP_ACTOR_H_
+
+#include <stdint.h>
+
+#include <memory>
+
+#include "aos/common/actions/actions.h"
+#include "aos/common/actions/actor.h"
+#include "y2015/actors/horizontal_can_pickup_action.q.h"
+#include "y2015/actors/fridge_profile_lib.h"
+
+namespace frc971 {
+namespace actors {
+
+class HorizontalCanPickupActor
+ : public FridgeActorBase<HorizontalCanPickupActionQueueGroup> {
+ public:
+ explicit HorizontalCanPickupActor(
+ HorizontalCanPickupActionQueueGroup *queues);
+
+ bool RunAction(const HorizontalCanPickupParams ¶ms) override;
+
+ private:
+ // Waits until we are near the angle.
+ // Returns false if we should cancel.
+ bool WaitUntilNear(double angle);
+ bool WaitUntilGoalNear(double angle);
+
+ void MoveArm(double angle, double intake_power);
+ void MoveArm(double angle, double intake_power,
+ const ProfileParams profile_params);
+};
+
+typedef aos::common::actions::TypedAction<HorizontalCanPickupActionQueueGroup>
+ HorizontalCanPickupAction;
+
+// Makes a new HorizontalCanPickupActor action.
+::std::unique_ptr<HorizontalCanPickupAction> MakeHorizontalCanPickupAction(
+ const HorizontalCanPickupParams ¶ms);
+
+} // namespace actors
+} // namespace frc971
+
+#endif // Y2015_ACTORS_HORIZONTAL_CAN_PICKUP_ACTOR_H_
diff --git a/y2015/actors/horizontal_can_pickup_actor_main.cc b/y2015/actors/horizontal_can_pickup_actor_main.cc
new file mode 100644
index 0000000..67f986c
--- /dev/null
+++ b/y2015/actors/horizontal_can_pickup_actor_main.cc
@@ -0,0 +1,16 @@
+#include <stdio.h>
+
+#include "aos/linux_code/init.h"
+#include "y2015/actors/horizontal_can_pickup_action.q.h"
+#include "y2015/actors/horizontal_can_pickup_actor.h"
+
+int main(int /*argc*/, char* /*argv*/ []) {
+ ::aos::Init();
+
+ ::frc971::actors::HorizontalCanPickupActor horizontal_can_pickup(
+ &::frc971::actors::horizontal_can_pickup_action);
+ horizontal_can_pickup.Run();
+
+ ::aos::Cleanup();
+ return 0;
+}
diff --git a/y2015/actors/lift_action.q b/y2015/actors/lift_action.q
new file mode 100644
index 0000000..37fba8d
--- /dev/null
+++ b/y2015/actors/lift_action.q
@@ -0,0 +1,18 @@
+package frc971.actors;
+
+import "aos/common/actions/actions.q";
+import "y2015/actors/lift_action_params.q";
+
+queue_group LiftActionQueueGroup {
+ implements aos.common.actions.ActionQueueGroup;
+
+ message Goal {
+ uint32_t run;
+ LiftParams params;
+ };
+
+ queue Goal goal;
+ queue aos.common.actions.Status status;
+};
+
+queue_group LiftActionQueueGroup lift_action;
diff --git a/y2015/actors/lift_action_params.q b/y2015/actors/lift_action_params.q
new file mode 100644
index 0000000..b31982b
--- /dev/null
+++ b/y2015/actors/lift_action_params.q
@@ -0,0 +1,19 @@
+package frc971.actors;
+
+// Parameters to send with start.
+struct LiftParams {
+ // Lift height
+ double lift_height;
+ // Arm goal.
+ double lift_arm;
+
+ // If true, do the second lift.
+ bool second_lift;
+ // Arm goal.
+ double intermediate_lift_height;
+
+ // True to move the claw in the middle of the lift.
+ bool pack_claw;
+ // Iff pack_claw is true, the angle to move the claw to.
+ double pack_claw_angle;
+};
diff --git a/y2015/actors/lift_actor.cc b/y2015/actors/lift_actor.cc
new file mode 100644
index 0000000..c057eef
--- /dev/null
+++ b/y2015/actors/lift_actor.cc
@@ -0,0 +1,92 @@
+#include <math.h>
+
+#include "aos/common/time.h"
+#include "y2015/actors/lift_actor.h"
+#include "y2015/constants.h"
+#include "y2015/actors/fridge_profile_lib.h"
+#include "y2015/control_loops/claw/claw.q.h"
+
+namespace frc971 {
+namespace actors {
+namespace {
+constexpr ProfileParams kArmMove{0.6, 1.0};
+constexpr ProfileParams kElevatorMove{0.9, 3.0};
+constexpr ProfileParams kElevatorFixMove{0.9, 2.0};
+} // namespace
+
+LiftActor::LiftActor(LiftActionQueueGroup *queues)
+ : FridgeActorBase<LiftActionQueueGroup>(queues) {}
+
+bool LiftActor::RunAction(const LiftParams ¶ms) {
+ control_loops::fridge_queue.status.FetchLatest();
+ if (!control_loops::fridge_queue.status.get()) {
+ return false;
+ }
+
+ double goal_height = params.lift_height;
+ double goal_angle = 0.0;
+
+ if (params.second_lift) {
+ DoFridgeProfile(params.intermediate_lift_height, 0.0, kElevatorFixMove,
+ kArmMove,
+ control_loops::fridge_queue.status->grabbers.top_front,
+ control_loops::fridge_queue.status->grabbers.bottom_front,
+ control_loops::fridge_queue.status->grabbers.bottom_back);
+ if (ShouldCancel()) return true;
+ }
+
+ if (!StartFridgeProfile(
+ params.lift_height, 0.0, kElevatorMove, kArmMove,
+ control_loops::fridge_queue.status->grabbers.top_front,
+ control_loops::fridge_queue.status->grabbers.bottom_front,
+ control_loops::fridge_queue.status->grabbers.bottom_back)) {
+ return true;
+ }
+
+ bool has_started_back = false;
+ while (true) {
+ if (control_loops::fridge_queue.status->goal_height > 0.1) {
+ if (!has_started_back) {
+ if (!StartFridgeProfile(
+ params.lift_height, params.lift_arm, kElevatorMove, kArmMove,
+ control_loops::fridge_queue.status->grabbers.top_front,
+ control_loops::fridge_queue.status->grabbers.bottom_front,
+ control_loops::fridge_queue.status->grabbers.bottom_back)) {
+ return true;
+ }
+ goal_angle = params.lift_arm;
+ has_started_back = true;
+ if (params.pack_claw) {
+ auto message = control_loops::claw_queue.goal.MakeMessage();
+ message->angle = params.pack_claw_angle;
+ message->angular_velocity = 0.0;
+ message->intake = 0.0;
+ message->rollers_closed = true;
+ message->max_velocity = 6.0;
+ message->max_acceleration = 10.0;
+
+ LOG_STRUCT(DEBUG, "Sending claw goal", *message);
+ message.Send();
+ }
+ }
+ }
+
+ ProfileStatus status = IterateProfile(
+ goal_height, goal_angle, kElevatorMove, kArmMove,
+ control_loops::fridge_queue.status->grabbers.top_front,
+ control_loops::fridge_queue.status->grabbers.bottom_front,
+ control_loops::fridge_queue.status->grabbers.bottom_back);
+ if (status == DONE || status == CANCELED) {
+ return true;
+ }
+ }
+ return true;
+}
+
+::std::unique_ptr<LiftAction> MakeLiftAction(const LiftParams ¶ms) {
+ return ::std::unique_ptr<LiftAction>(
+ new LiftAction(&::frc971::actors::lift_action, params));
+}
+
+} // namespace actors
+} // namespace frc971
diff --git a/y2015/actors/lift_actor.h b/y2015/actors/lift_actor.h
new file mode 100644
index 0000000..cbd79ec
--- /dev/null
+++ b/y2015/actors/lift_actor.h
@@ -0,0 +1,31 @@
+#ifndef Y2015_ACTORS_LIFT_ACTOR_H_
+#define Y2015_ACTORS_LIFT_ACTOR_H_
+
+#include <stdint.h>
+
+#include <memory>
+
+#include "aos/common/actions/actions.h"
+#include "aos/common/actions/actor.h"
+#include "y2015/actors/lift_action.q.h"
+#include "y2015/actors/fridge_profile_lib.h"
+
+namespace frc971 {
+namespace actors {
+
+class LiftActor : public FridgeActorBase<LiftActionQueueGroup> {
+ public:
+ explicit LiftActor(LiftActionQueueGroup *queues);
+
+ bool RunAction(const LiftParams ¶ms) override;
+};
+
+typedef aos::common::actions::TypedAction<LiftActionQueueGroup> LiftAction;
+
+// Makes a new LiftActor action.
+::std::unique_ptr<LiftAction> MakeLiftAction(const LiftParams ¶ms);
+
+} // namespace actors
+} // namespace frc971
+
+#endif // Y2015_ACTORS_LIFT_ACTOR_H_
diff --git a/y2015/actors/lift_actor_main.cc b/y2015/actors/lift_actor_main.cc
new file mode 100644
index 0000000..ed809ef
--- /dev/null
+++ b/y2015/actors/lift_actor_main.cc
@@ -0,0 +1,15 @@
+#include <stdio.h>
+
+#include "aos/linux_code/init.h"
+#include "y2015/actors/lift_action.q.h"
+#include "y2015/actors/lift_actor.h"
+
+int main(int /*argc*/, char* /*argv*/ []) {
+ ::aos::Init();
+
+ ::frc971::actors::LiftActor lift(&::frc971::actors::lift_action);
+ lift.Run();
+
+ ::aos::Cleanup();
+ return 0;
+}
diff --git a/y2015/actors/pickup_action.q b/y2015/actors/pickup_action.q
new file mode 100644
index 0000000..237337a
--- /dev/null
+++ b/y2015/actors/pickup_action.q
@@ -0,0 +1,33 @@
+package frc971.actors;
+
+import "aos/common/actions/actions.q";
+
+// Parameters to send with start.
+struct PickupParams {
+ // Angle to pull in at the top.
+ double pickup_angle;
+ // Angle to start sucking in above.
+ double suck_angle;
+ // Angle to finish sucking at.
+ double suck_angle_finish;
+ // Angle to go to once we get to the top to finish pulling in.
+ double pickup_finish_angle;
+ // Power to pull the bin in at the top.
+ double intake_voltage;
+ // Time to pull the bin in for.
+ double intake_time;
+};
+
+queue_group PickupActionQueueGroup {
+ implements aos.common.actions.ActionQueueGroup;
+
+ message Goal {
+ uint32_t run;
+ PickupParams params;
+ };
+
+ queue Goal goal;
+ queue aos.common.actions.Status status;
+};
+
+queue_group PickupActionQueueGroup pickup_action;
diff --git a/y2015/actors/pickup_actor.cc b/y2015/actors/pickup_actor.cc
new file mode 100644
index 0000000..0863ba6
--- /dev/null
+++ b/y2015/actors/pickup_actor.cc
@@ -0,0 +1,149 @@
+#include "y2015/actors/pickup_actor.h"
+
+#include <cmath>
+
+#include "aos/common/logging/logging.h"
+#include "aos/common/controls/control_loop.h"
+#include "aos/common/util/phased_loop.h"
+#include "aos/common/time.h"
+#include "y2015/control_loops/claw/claw.q.h"
+
+namespace frc971 {
+namespace actors {
+namespace {
+constexpr double kClawPickupVelocity = 3.00;
+constexpr double kClawPickupAcceleration = 3.5;
+constexpr double kClawMoveDownVelocity = 7.00;
+constexpr double kClawMoveDownAcceleration = 15.0;
+constexpr double kClawMoveUpVelocity = 8.0;
+constexpr double kClawMoveUpAcceleration = 25.0;
+} // namespace
+
+PickupActor::PickupActor(PickupActionQueueGroup* queues)
+ : aos::common::actions::ActorBase<PickupActionQueueGroup>(queues) {}
+
+bool PickupActor::RunAction(const PickupParams& params) {
+ constexpr double kAngleEpsilon = 0.10;
+ // Start lifting the tote.
+ {
+ auto message = control_loops::claw_queue.goal.MakeMessage();
+ message->angle = params.pickup_angle;
+ message->max_velocity = kClawPickupVelocity;
+ message->max_acceleration = kClawPickupAcceleration;
+ message->angular_velocity = 0.0;
+ message->intake = 0.0;
+ message->rollers_closed = true;
+
+ LOG_STRUCT(DEBUG, "Sending claw goal", *message);
+ message.Send();
+ }
+ while (true) {
+ control_loops::claw_queue.status.FetchAnother();
+ if (ShouldCancel()) return true;
+ const double current_angle = control_loops::claw_queue.status->angle;
+ LOG_STRUCT(DEBUG, "Got claw status", *control_loops::claw_queue.status);
+
+ if (current_angle > params.suck_angle ||
+ ::std::abs(current_angle - params.pickup_angle) < kAngleEpsilon) {
+ break;
+ }
+ }
+
+ // Once above params.suck_angle, start sucking while lifting.
+ {
+ auto message = control_loops::claw_queue.goal.MakeMessage();
+ message->angle = params.pickup_angle;
+ message->max_velocity = kClawPickupVelocity;
+ message->max_acceleration = kClawPickupAcceleration;
+ message->angular_velocity = 0.0;
+ message->intake = params.intake_voltage;
+ message->rollers_closed = true;
+
+ LOG_STRUCT(DEBUG, "Sending claw goal", *message);
+ message.Send();
+ }
+
+ while (true) {
+ control_loops::claw_queue.status.FetchAnother();
+ if (ShouldCancel()) return true;
+ const double current_angle = control_loops::claw_queue.status->angle;
+ LOG_STRUCT(DEBUG, "Got claw status", *control_loops::claw_queue.status);
+
+ if (::std::abs(current_angle - params.pickup_angle) < kAngleEpsilon) {
+ break;
+ }
+ }
+
+ // Now that we have reached the upper height, come back down while intaking.
+ {
+ auto message = control_loops::claw_queue.goal.MakeMessage();
+ message->angle = params.suck_angle_finish;
+ message->max_velocity = kClawMoveDownVelocity;
+ message->max_acceleration = kClawMoveDownAcceleration;
+ message->angular_velocity = 0.0;
+ message->intake = params.intake_voltage;
+ message->rollers_closed = true;
+
+ LOG_STRUCT(DEBUG, "Sending claw goal", *message);
+ message.Send();
+ }
+
+ // Pull in for params.intake_time.
+ ::aos::time::Time end_time =
+ ::aos::time::Time::Now() + aos::time::Time::InSeconds(params.intake_time);
+ while ( ::aos::time::Time::Now() <= end_time) {
+ ::aos::time::PhasedLoopXMS(::aos::controls::kLoopFrequency.ToMSec(), 2500);
+ if (ShouldCancel()) return true;
+ }
+
+ // Lift the claw back up to pack the box back in.
+ {
+ auto message = control_loops::claw_queue.goal.MakeMessage();
+ message->angle = params.pickup_finish_angle;
+ message->max_velocity = kClawMoveUpVelocity;
+ message->max_acceleration = kClawMoveUpAcceleration;
+ message->angular_velocity = 0.0;
+ message->intake = params.intake_voltage;
+ message->rollers_closed = true;
+
+ LOG_STRUCT(DEBUG, "Sending claw goal", *message);
+ message.Send();
+ }
+
+ while (true) {
+ control_loops::claw_queue.status.FetchAnother();
+ if (ShouldCancel()) return true;
+ const double current_angle = control_loops::claw_queue.status->angle;
+ LOG_STRUCT(DEBUG, "Got claw status", *control_loops::claw_queue.status);
+
+ if (::std::abs(current_angle - params.pickup_finish_angle) <
+ kAngleEpsilon) {
+ break;
+ }
+ }
+
+ // Stop the motors...
+ {
+ auto message = control_loops::claw_queue.goal.MakeMessage();
+ message->angle = params.pickup_finish_angle;
+ message->max_velocity = kClawMoveUpVelocity;
+ message->max_acceleration = kClawMoveUpAcceleration;
+ message->angular_velocity = 0.0;
+ message->intake = 0.0;
+ message->rollers_closed = true;
+
+ LOG_STRUCT(DEBUG, "Sending claw goal", *message);
+ message.Send();
+ }
+
+
+ return true;
+}
+
+::std::unique_ptr<PickupAction> MakePickupAction(const PickupParams& params) {
+ return ::std::unique_ptr<PickupAction>(
+ new PickupAction(&::frc971::actors::pickup_action, params));
+}
+
+} // namespace actors
+} // namespace frc971
diff --git a/y2015/actors/pickup_actor.h b/y2015/actors/pickup_actor.h
new file mode 100644
index 0000000..1f09718
--- /dev/null
+++ b/y2015/actors/pickup_actor.h
@@ -0,0 +1,27 @@
+#ifndef Y2015_ACTORS_PICKUP_ACTOR_H_
+#define Y2015_ACTORS_PICKUP_ACTOR_H_
+
+#include "aos/common/actions/actions.h"
+#include "aos/common/actions/actor.h"
+#include "y2015/actors/pickup_action.q.h"
+
+namespace frc971 {
+namespace actors {
+
+class PickupActor
+ : public aos::common::actions::ActorBase<PickupActionQueueGroup> {
+ public:
+ explicit PickupActor(PickupActionQueueGroup *queues);
+
+ bool RunAction(const PickupParams ¶ms) override;
+};
+
+typedef aos::common::actions::TypedAction<PickupActionQueueGroup> PickupAction;
+
+// Makes a new PickupActor action.
+::std::unique_ptr<PickupAction> MakePickupAction(const PickupParams ¶ms);
+
+} // namespace actors
+} // namespace frc971
+
+#endif // Y2015_ACTORS_PICKUP_ACTOR_H_
diff --git a/y2015/actors/pickup_actor_main.cc b/y2015/actors/pickup_actor_main.cc
new file mode 100644
index 0000000..9c2c488
--- /dev/null
+++ b/y2015/actors/pickup_actor_main.cc
@@ -0,0 +1,15 @@
+#include <stdio.h>
+
+#include "aos/linux_code/init.h"
+#include "y2015/actors/pickup_action.q.h"
+#include "y2015/actors/pickup_actor.h"
+
+int main(int /*argc*/, char* /*argv*/ []) {
+ ::aos::Init();
+
+ frc971::actors::PickupActor pickup(&::frc971::actors::pickup_action);
+ pickup.Run();
+
+ ::aos::Cleanup();
+ return 0;
+}
diff --git a/y2015/actors/score_action.q b/y2015/actors/score_action.q
new file mode 100644
index 0000000..5b14210
--- /dev/null
+++ b/y2015/actors/score_action.q
@@ -0,0 +1,34 @@
+package frc971.actors;
+
+import "aos/common/actions/actions.q";
+
+// Parameters to send with start.
+struct ScoreParams {
+ // If true, move the stack first.
+ bool move_the_stack;
+ // If true, place the stack (possibly after moving it).
+ bool place_the_stack;
+
+ // TODO(Brian): Comments (by somebody who knows what these all mean).
+ double upper_move_height;
+ double begin_horizontal_move_height;
+ double horizontal_move_target;
+ double horizontal_start_lowering;
+ double place_height;
+ double home_lift_horizontal_start_position;
+ double home_return_height;
+};
+
+queue_group ScoreActionQueueGroup {
+ implements aos.common.actions.ActionQueueGroup;
+
+ message Goal {
+ uint32_t run;
+ ScoreParams params;
+ };
+
+ queue Goal goal;
+ queue aos.common.actions.Status status;
+};
+
+queue_group ScoreActionQueueGroup score_action;
diff --git a/y2015/actors/score_actor.cc b/y2015/actors/score_actor.cc
new file mode 100644
index 0000000..c0a398f
--- /dev/null
+++ b/y2015/actors/score_actor.cc
@@ -0,0 +1,296 @@
+#include "y2015/actors/score_actor.h"
+
+#include <cmath>
+
+#include "aos/common/controls/control_loop.h"
+#include "aos/common/logging/logging.h"
+#include "aos/common/util/phased_loop.h"
+#include "aos/common/logging/queue_logging.h"
+
+#include "y2015/constants.h"
+#include "y2015/control_loops/fridge/fridge.q.h"
+
+using ::frc971::control_loops::fridge_queue;
+
+namespace frc971 {
+namespace actors {
+namespace {
+
+const double kSlowMaxXVelocity = 0.80;
+const double kSlowMaxYVelocity = 0.40;
+const double kFastMaxXVelocity = 0.80;
+const double kFastMaxYVelocity = 0.30;
+const double kReallyFastMaxXVelocity = 1.0;
+const double kReallyFastMaxYVelocity = 0.6;
+
+const double kMaxXAcceleration = 0.5;
+const double kMaxYAcceleration = 0.7;
+const double kLiftYAcceleration = 2.0;
+const double kLowerYAcceleration = 2.0;
+const double kFastMaxXAcceleration = 1.5;
+const double kFastMaxYAcceleration = 1.5;
+const double kSlowMaxXAcceleration = 0.3;
+const double kSlowMaxYAcceleration = 0.5;
+
+} // namespace
+
+ScoreActor::ScoreActor(ScoreActionQueueGroup* queues)
+ : aos::common::actions::ActorBase<ScoreActionQueueGroup>(queues),
+ kinematics_(constants::GetValues().fridge.arm_length,
+ constants::GetValues().fridge.elevator.upper_limit,
+ constants::GetValues().fridge.elevator.lower_limit,
+ constants::GetValues().fridge.arm.upper_limit,
+ constants::GetValues().fridge.arm.lower_limit) {}
+
+bool ScoreActor::RunAction(const ScoreParams& params) {
+ if (params.move_the_stack) {
+ LOG(INFO, "moving stack\n");
+ if (!MoveStackIntoPosition(params)) return false;
+ LOG(INFO, "done moving stack\n");
+ if (ShouldCancel()) return true;
+ }
+ if (params.place_the_stack) {
+ LOG(INFO, "placing stack\n");
+ if (!PlaceTheStack(params)) return false;
+ LOG(INFO, "done placing stack\n");
+ if (ShouldCancel()) return true;
+ }
+ return true;
+}
+
+bool ScoreActor::MoveStackIntoPosition(const ScoreParams& params) {
+ // Move the fridge up a little bit.
+ if (!SendGoal(0.0, params.upper_move_height, true, kSlowMaxXVelocity,
+ kSlowMaxYVelocity, kMaxXAcceleration, kLiftYAcceleration)) {
+ LOG(ERROR, "Sending fridge message failed.\n");
+ return false;
+ }
+
+ while (true) {
+ ::aos::time::PhasedLoopXMS(::aos::controls::kLoopFrequency.ToMSec(), 2500);
+ if (ShouldCancel()) {
+ return true;
+ }
+
+ // Move on when it is clear of the tote knobs.
+ if (CurrentGoalHeight() > params.begin_horizontal_move_height) {
+ LOG(INFO, "moving on to horizontal move\n");
+ break;
+ }
+ }
+
+ // Move the fridge out.
+ if (!SendGoal(params.horizontal_move_target,
+ params.begin_horizontal_move_height, true, kSlowMaxXVelocity,
+ kFastMaxYVelocity, kSlowMaxXAcceleration,
+ kSlowMaxYAcceleration)) {
+ LOG(ERROR, "Sending fridge message failed.\n");
+ return false;
+ }
+
+ bool started_lowering = false;
+
+ while (true) {
+ ::aos::time::PhasedLoopXMS(::aos::controls::kLoopFrequency.ToMSec(), 2500);
+ if (ShouldCancel()) {
+ return true;
+ }
+ // Round the moving out corner and start setting down.
+ if (params.place_the_stack && !started_lowering) {
+ if (CurrentGoalX() < params.horizontal_start_lowering) {
+ if (!SendGoal(params.horizontal_move_target, params.place_height, true,
+ kSlowMaxXVelocity, kFastMaxYVelocity,
+ kSlowMaxXAcceleration, kMaxYAcceleration)) {
+ LOG(ERROR, "Sending fridge message failed.\n");
+ return false;
+ }
+ started_lowering = true;
+ }
+ }
+
+ if (NearHorizontalGoal(params.horizontal_move_target)) {
+ LOG(INFO, "reached goal\n");
+ break;
+ }
+ }
+
+ if (ShouldCancel()) return true;
+
+ return true;
+}
+
+bool ScoreActor::PlaceTheStack(const ScoreParams& params) {
+ // Once the fridge is way out, put it on the ground.
+ if (!SendGoal(params.horizontal_move_target, params.place_height, true,
+ kFastMaxXVelocity, kFastMaxYVelocity, kMaxXAcceleration,
+ kLowerYAcceleration)) {
+ LOG(ERROR, "Sending fridge message failed.\n");
+ return false;
+ }
+
+ while (true) {
+ ::aos::time::PhasedLoopXMS(::aos::controls::kLoopFrequency.ToMSec(), 2500);
+ if (ShouldCancel()) {
+ return true;
+ }
+
+ if (NearGoal(params.horizontal_move_target, params.place_height)) {
+ break;
+ }
+ }
+
+ // Release and give the grabers a chance to get out of the way.
+ if (!SendGoal(params.horizontal_move_target, params.place_height, false,
+ kFastMaxXVelocity, kFastMaxYVelocity, kMaxXAcceleration,
+ kLowerYAcceleration)) {
+ LOG(ERROR, "Sending fridge message failed.\n");
+ return false;
+ }
+ if (!WaitOrCancel(::aos::time::Time::InSeconds(0.1))) return true;
+
+ // Go back to the home position.
+ if (!SendGoal(0.0, params.place_height, false, kReallyFastMaxXVelocity,
+ kReallyFastMaxYVelocity, kFastMaxXAcceleration,
+ kFastMaxYAcceleration)) {
+ LOG(ERROR, "Sending fridge message failed.\n");
+ return false;
+ }
+
+ bool has_lifted = false;
+ while (true) {
+ ::aos::time::PhasedLoopXMS(::aos::controls::kLoopFrequency.ToMSec(), 2500);
+ if (ShouldCancel()) {
+ return true;
+ }
+
+ if (!has_lifted &&
+ CurrentGoalX() > params.home_lift_horizontal_start_position) {
+ if (!SendGoal(0.0, params.home_return_height, false,
+ kReallyFastMaxXVelocity, kReallyFastMaxYVelocity,
+ kFastMaxXAcceleration, kFastMaxYAcceleration)) {
+ LOG(ERROR, "Sending fridge message failed.\n");
+ return false;
+ }
+ has_lifted = true;
+ }
+
+ if (NearGoal(0.0, params.home_return_height)) {
+ break;
+ }
+ }
+
+ return true;
+}
+
+double ScoreActor::CurrentHeight() {
+ fridge_queue.status.FetchLatest();
+ if (!fridge_queue.status.get()) {
+ LOG(ERROR, "Reading from fridge status queue failed.\n");
+ return false;
+ }
+
+ ::aos::util::ElevatorArmKinematics::KinematicResult results;
+ kinematics_.ForwardKinematic(fridge_queue.status->height,
+ fridge_queue.status->angle, 0.0, 0.0, &results);
+
+ return results.fridge_h;
+}
+
+double ScoreActor::CurrentGoalHeight() {
+ fridge_queue.status.FetchLatest();
+ if (!fridge_queue.status.get()) {
+ LOG(ERROR, "Reading from fridge status queue failed.\n");
+ return 0.0;
+ }
+
+ ::aos::util::ElevatorArmKinematics::KinematicResult results;
+ kinematics_.ForwardKinematic(fridge_queue.status->goal_height,
+ fridge_queue.status->goal_angle, 0.0, 0.0,
+ &results);
+
+ return results.fridge_h;
+}
+
+double ScoreActor::CurrentX() {
+ fridge_queue.status.FetchLatest();
+ if (!fridge_queue.status.get()) {
+ LOG(ERROR, "Reading from fridge status queue failed.\n");
+ return false;
+ }
+
+ return fridge_queue.status->x;
+}
+
+double ScoreActor::CurrentGoalX() {
+ fridge_queue.status.FetchLatest();
+ if (!fridge_queue.status.get()) {
+ LOG(ERROR, "Reading from fridge status queue failed.\n");
+ return 0.0;
+ }
+
+ return fridge_queue.status->goal_x;
+}
+
+bool ScoreActor::SendGoal(double x, double y, bool grabbers_enabled,
+ double max_x_velocity, double max_y_velocity,
+ double max_x_acceleration,
+ double max_y_acceleration) {
+ auto new_fridge_goal = control_loops::fridge_queue.goal.MakeMessage();
+ new_fridge_goal->x = x;
+ new_fridge_goal->y = y;
+ new_fridge_goal->profiling_type = 1;
+ new_fridge_goal->angular_velocity = 0.0;
+ new_fridge_goal->velocity = 0.0;
+ new_fridge_goal->grabbers.top_front = grabbers_enabled;
+ new_fridge_goal->grabbers.top_back = grabbers_enabled;
+ new_fridge_goal->grabbers.bottom_front = grabbers_enabled;
+ new_fridge_goal->grabbers.bottom_back = grabbers_enabled;
+ new_fridge_goal->max_x_velocity = max_x_velocity;
+ new_fridge_goal->max_y_velocity = max_y_velocity;
+ new_fridge_goal->max_x_acceleration = max_x_acceleration;
+ new_fridge_goal->max_y_acceleration = max_y_acceleration;
+ LOG_STRUCT(INFO, "sending fridge goal", *new_fridge_goal);
+
+ return new_fridge_goal.Send();
+}
+
+bool ScoreActor::NearHorizontalGoal(double x) {
+ fridge_queue.status.FetchLatest();
+ if (!fridge_queue.status.get()) {
+ LOG(ERROR, "Reading from fridge status queue failed.\n");
+ return false;
+ }
+
+ ::aos::util::ElevatorArmKinematics::KinematicResult results;
+ ::aos::util::ElevatorArmKinematics::KinematicResult goal_results;
+ kinematics_.ForwardKinematic(fridge_queue.status->height,
+ fridge_queue.status->angle, 0.0, 0.0, &results);
+ kinematics_.ForwardKinematic(fridge_queue.status->goal_height,
+ fridge_queue.status->goal_angle, 0.0, 0.0,
+ &goal_results);
+
+ return (::std::abs(results.fridge_x - x) < 0.020 &&
+ ::std::abs(goal_results.fridge_x - x) < 0.0001);
+}
+
+bool ScoreActor::NearGoal(double x, double y) {
+ fridge_queue.status.FetchLatest();
+ if (!fridge_queue.status.get()) {
+ LOG(ERROR, "Reading from fridge status queue failed.\n");
+ return false;
+ }
+
+ const auto &status = *fridge_queue.status;
+ return (::std::abs(status.x - x) < 0.020 &&
+ ::std::abs(status.y - y) < 0.020 &&
+ ::std::abs(status.goal_x - x) < 0.0001 &&
+ ::std::abs(status.goal_y - y) < 0.0001);
+}
+
+::std::unique_ptr<ScoreAction> MakeScoreAction(const ScoreParams& params) {
+ return ::std::unique_ptr<ScoreAction>(
+ new ScoreAction(&::frc971::actors::score_action, params));
+}
+
+} // namespace actors
+} // namespace frc971
diff --git a/y2015/actors/score_actor.h b/y2015/actors/score_actor.h
new file mode 100644
index 0000000..c9ed71f
--- /dev/null
+++ b/y2015/actors/score_actor.h
@@ -0,0 +1,43 @@
+#ifndef Y2015_ACTORS_SCORE_ACTOR_H_
+#define Y2015_ACTORS_SCORE_ACTOR_H_
+
+#include "aos/common/actions/actions.h"
+#include "aos/common/actions/actor.h"
+#include "y2015/util/kinematics.h"
+#include "y2015/actors/score_action.q.h"
+
+namespace frc971 {
+namespace actors {
+
+class ScoreActor
+ : public ::aos::common::actions::ActorBase<ScoreActionQueueGroup> {
+ public:
+ explicit ScoreActor(ScoreActionQueueGroup *queues);
+
+ bool RunAction(const ScoreParams ¶ms) override;
+
+ private:
+
+ ::aos::util::ElevatorArmKinematics kinematics_;
+ bool NearGoal(double x, double y);
+ bool NearHorizontalGoal(double x);
+ bool PlaceTheStack(const ScoreParams ¶ms);
+ bool MoveStackIntoPosition(const ScoreParams ¶ms);
+ bool SendGoal(double x, double y, bool grabbers_enabled,
+ double max_x_velocity, double max_y_velocity,
+ double max_x_acceleration, double max_y_acceleration);
+ double CurrentHeight();
+ double CurrentGoalHeight();
+ double CurrentX();
+ double CurrentGoalX();
+};
+
+typedef aos::common::actions::TypedAction<ScoreActionQueueGroup> ScoreAction;
+
+// Makes a new ScoreActor action.
+::std::unique_ptr<ScoreAction> MakeScoreAction(const ScoreParams ¶ms);
+
+} // namespace actors
+} // namespace frc971
+
+#endif
diff --git a/y2015/actors/score_actor_main.cc b/y2015/actors/score_actor_main.cc
new file mode 100644
index 0000000..cc149e6
--- /dev/null
+++ b/y2015/actors/score_actor_main.cc
@@ -0,0 +1,15 @@
+#include <stdio.h>
+
+#include "aos/linux_code/init.h"
+#include "y2015/actors/score_action.q.h"
+#include "y2015/actors/score_actor.h"
+
+int main(int /*argc*/, char* /*argv*/ []) {
+ ::aos::Init();
+
+ frc971::actors::ScoreActor score(&::frc971::actors::score_action);
+ score.Run();
+
+ ::aos::Cleanup();
+ return 0;
+}
diff --git a/y2015/actors/score_actor_test.cc b/y2015/actors/score_actor_test.cc
new file mode 100644
index 0000000..4791f09
--- /dev/null
+++ b/y2015/actors/score_actor_test.cc
@@ -0,0 +1,98 @@
+#include <unistd.h>
+
+#include <memory>
+
+#include "gtest/gtest.h"
+#include "aos/common/queue.h"
+#include "aos/common/queue_testutils.h"
+#include "aos/common/actions/actor.h"
+#include "y2015/actors/score_action.q.h"
+#include "y2015/actors/score_actor.h"
+#include "y2015/control_loops/fridge/fridge.q.h"
+#include "frc971/control_loops/team_number_test_environment.h"
+
+using ::aos::time::Time;
+
+namespace frc971 {
+namespace actors {
+namespace testing {
+
+class ScoreActionTest : public ::testing::Test {
+ protected:
+ ScoreActionTest() {
+ frc971::actors::score_action.goal.Clear();
+ frc971::actors::score_action.status.Clear();
+ control_loops::fridge_queue.status.Clear();
+ control_loops::fridge_queue.goal.Clear();
+ }
+
+ virtual ~ScoreActionTest() {
+ frc971::actors::score_action.goal.Clear();
+ frc971::actors::score_action.status.Clear();
+ control_loops::fridge_queue.status.Clear();
+ control_loops::fridge_queue.goal.Clear();
+ }
+
+ // Bring up and down Core.
+ ::aos::common::testing::GlobalCoreInstance my_core;
+};
+
+// Tests that cancel stops not only the score action, but also the underlying
+// profile action.
+TEST_F(ScoreActionTest, PlaceTheStackCancel) {
+ ScoreActor score(&frc971::actors::score_action);
+
+ frc971::actors::score_action.goal.MakeWithBuilder().run(true).Send();
+
+ // Tell it the fridge is zeroed.
+ ASSERT_TRUE(control_loops::fridge_queue.status.MakeWithBuilder()
+ .zeroed(true)
+ .angle(0.0)
+ .height(0.0)
+ .Send());
+
+ // do the action and it will post to the goal queue
+ score.WaitForActionRequest();
+
+ // the action has started, so now cancel it and it should cancel
+ // the underlying profile
+ frc971::actors::score_action.goal.MakeWithBuilder().run(false).Send();
+
+ // let the action start running, if we return from this call it has worked.
+ const ScoreParams params = {true, true, 0.14, 0.13, -0.7, -0.7, -0.10, -0.5, 0.1};
+ score.RunAction(params);
+
+ SUCCEED();
+}
+
+// Tests that cancel stops not only the score action, but also the underlying
+// profile action.
+TEST_F(ScoreActionTest, MoveStackIntoPositionCancel) {
+ ScoreActor score(&frc971::actors::score_action);
+
+ frc971::actors::score_action.goal.MakeWithBuilder().run(true).Send();
+
+ // Tell it the fridge is zeroed.
+ ASSERT_TRUE(control_loops::fridge_queue.status.MakeWithBuilder()
+ .zeroed(true)
+ .angle(0.0)
+ .height(0.0)
+ .Send());
+
+ // do the action and it will post to the goal queue
+ score.WaitForActionRequest();
+
+ // the action has started, so now cancel it and it should cancel
+ // the underlying profile
+ frc971::actors::score_action.goal.MakeWithBuilder().run(false).Send();
+
+ // let the action start running, if we return from this call it has worked.
+ const ScoreParams params = {false, true, 0.14, 0.13, -0.7, -0.7, -0.10, -0.5, 0.1};
+ score.RunAction(params);
+
+ SUCCEED();
+}
+
+} // namespace testing
+} // namespace actors
+} // namespace frc971
diff --git a/y2015/actors/stack_action.q b/y2015/actors/stack_action.q
new file mode 100644
index 0000000..b4389b8
--- /dev/null
+++ b/y2015/actors/stack_action.q
@@ -0,0 +1,18 @@
+package frc971.actors;
+
+import "aos/common/actions/actions.q";
+import "y2015/actors/stack_action_params.q";
+
+queue_group StackActionQueueGroup {
+ implements aos.common.actions.ActionQueueGroup;
+
+ message Goal {
+ uint32_t run;
+ StackParams params;
+ };
+
+ queue Goal goal;
+ queue aos.common.actions.Status status;
+};
+
+queue_group StackActionQueueGroup stack_action;
diff --git a/y2015/actors/stack_action_params.q b/y2015/actors/stack_action_params.q
new file mode 100644
index 0000000..3958f7d
--- /dev/null
+++ b/y2015/actors/stack_action_params.q
@@ -0,0 +1,17 @@
+package frc971.actors;
+
+// Parameters to send with start.
+struct StackParams {
+ // If true, don't grab the lower tote after lowering.
+ bool only_place;
+
+ // The angle to move the arm to while lowering it.
+ double arm_clearance;
+
+ double claw_out_angle;
+ // The height just above the box to move before lowering.
+ double over_box_before_place_height;
+
+ // Bottom position.
+ double bottom;
+};
diff --git a/y2015/actors/stack_actor.cc b/y2015/actors/stack_actor.cc
new file mode 100644
index 0000000..539018f
--- /dev/null
+++ b/y2015/actors/stack_actor.cc
@@ -0,0 +1,96 @@
+#include "y2015/actors/stack_actor.h"
+
+#include <math.h>
+
+#include "aos/common/time.h"
+#include "aos/common/util/phased_loop.h"
+
+#include "y2015/constants.h"
+#include "y2015/control_loops/claw/claw.q.h"
+
+namespace frc971 {
+namespace actors {
+namespace {
+constexpr ProfileParams kArmWithStackMove{1.75, 4.20};
+constexpr ProfileParams kSlowArmMove{1.3, 1.4};
+constexpr ProfileParams kSlowElevatorMove{1.0, 3.0};
+
+constexpr ProfileParams kFastArmMove{0.8, 4.0};
+constexpr ProfileParams kFastElevatorMove{1.2, 5.0};
+constexpr ProfileParams kReallyFastElevatorMove{1.2, 6.0};
+} // namespace
+
+StackActor::StackActor(StackActionQueueGroup *queues)
+ : FridgeActorBase<StackActionQueueGroup>(queues) {}
+
+bool StackActor::RunAction(const StackParams ¶ms) {
+ const auto &values = constants::GetValues();
+
+ control_loops::fridge_queue.status.FetchLatest();
+ if (!control_loops::fridge_queue.status.get()) {
+ LOG(ERROR, "Got no fridge status packet.\n");
+ return false;
+ }
+
+ // If we are really high, probably have a can. Move over before down.
+ if (control_loops::fridge_queue.status->goal_height >
+ params.over_box_before_place_height + 0.1) {
+ // Set the current stack down on top of the bottom box.
+ DoFridgeProfile(control_loops::fridge_queue.status->goal_height, 0.0,
+ kSlowElevatorMove, kArmWithStackMove, true);
+ if (ShouldCancel()) return true;
+ }
+
+ // Set the current stack down on top of the bottom box.
+ DoFridgeProfile(params.bottom + values.tote_height, 0.0, kSlowElevatorMove,
+ kSlowArmMove, true);
+ if (ShouldCancel()) return true;
+
+ // Clamp.
+ if (!params.only_place) {
+ // Move the claw out of the way only if we are supposed to pick up.
+ bool send_goal = true;
+ control_loops::claw_queue.status.FetchLatest();
+ if (control_loops::claw_queue.status.get()) {
+ if (control_loops::claw_queue.status->goal_angle <
+ params.claw_out_angle) {
+ send_goal = false;
+ }
+ }
+ if (send_goal) {
+ auto message = control_loops::claw_queue.goal.MakeMessage();
+ message->angle = params.claw_out_angle;
+ message->angular_velocity = 0.0;
+ message->intake = 0.0;
+ message->rollers_closed = true;
+ message->max_velocity = 6.0;
+ message->max_acceleration = 10.0;
+
+ LOG_STRUCT(DEBUG, "Sending claw goal", *message);
+ message.Send();
+ }
+ }
+
+ if (params.only_place) {
+ // open grabber for place only
+ DoFridgeProfile(params.bottom + values.tote_height, 0.0, kFastElevatorMove,
+ kFastArmMove, false);
+ // Finish early if we aren't supposed to grab.
+ return true;
+ }
+
+ if (ShouldCancel()) return true;
+ // grab can (if in fang mode the grabber stays closed)
+ DoFridgeProfile(params.bottom, 0.0, kReallyFastElevatorMove, kFastArmMove, true,
+ true, true);
+
+ return true;
+}
+
+::std::unique_ptr<StackAction> MakeStackAction(const StackParams ¶ms) {
+ return ::std::unique_ptr<StackAction>(
+ new StackAction(&::frc971::actors::stack_action, params));
+}
+
+} // namespace actors
+} // namespace frc971
diff --git a/y2015/actors/stack_actor.h b/y2015/actors/stack_actor.h
new file mode 100644
index 0000000..5f2f7fb
--- /dev/null
+++ b/y2015/actors/stack_actor.h
@@ -0,0 +1,31 @@
+#ifndef Y2015_ACTORS_STACK_ACTOR_H_
+#define Y2015_ACTORS_STACK_ACTOR_H_
+
+#include <stdint.h>
+
+#include <memory>
+
+#include "aos/common/actions/actions.h"
+#include "aos/common/actions/actor.h"
+#include "y2015/actors/stack_action.q.h"
+#include "y2015/actors/fridge_profile_lib.h"
+
+namespace frc971 {
+namespace actors {
+
+class StackActor : public FridgeActorBase<StackActionQueueGroup> {
+ public:
+ explicit StackActor(StackActionQueueGroup *queues);
+
+ bool RunAction(const StackParams ¶ms) override;
+};
+
+typedef aos::common::actions::TypedAction<StackActionQueueGroup> StackAction;
+
+// Makes a new stackActor action.
+::std::unique_ptr<StackAction> MakeStackAction(const StackParams ¶ms);
+
+} // namespace actors
+} // namespace frc971
+
+#endif
diff --git a/y2015/actors/stack_actor_main.cc b/y2015/actors/stack_actor_main.cc
new file mode 100644
index 0000000..f65733c
--- /dev/null
+++ b/y2015/actors/stack_actor_main.cc
@@ -0,0 +1,15 @@
+#include <stdio.h>
+
+#include "aos/linux_code/init.h"
+#include "y2015/actors/stack_action.q.h"
+#include "y2015/actors/stack_actor.h"
+
+int main(int /*argc*/, char* /*argv*/ []) {
+ ::aos::Init();
+
+ ::frc971::actors::StackActor stack(&::frc971::actors::stack_action);
+ stack.Run();
+
+ ::aos::Cleanup();
+ return 0;
+}
diff --git a/y2015/actors/stack_actor_test.cc b/y2015/actors/stack_actor_test.cc
new file mode 100644
index 0000000..e1b1856
--- /dev/null
+++ b/y2015/actors/stack_actor_test.cc
@@ -0,0 +1,72 @@
+#include <unistd.h>
+
+#include <memory>
+
+#include "gtest/gtest.h"
+#include "aos/common/queue.h"
+#include "aos/common/queue_testutils.h"
+#include "aos/common/actions/actor.h"
+#include "y2015/actors/stack_action.q.h"
+#include "y2015/actors/stack_actor.h"
+#include "y2015/control_loops/fridge/fridge.q.h"
+
+#include "aos/common/controls/control_loop_test.h"
+#include "frc971/control_loops/team_number_test_environment.h"
+
+using ::aos::time::Time;
+
+namespace frc971 {
+namespace actors {
+namespace testing {
+
+class StackActionTest : public ::testing::Test {
+ protected:
+ StackActionTest() {
+ frc971::actors::stack_action.goal.Clear();
+ frc971::actors::stack_action.status.Clear();
+ control_loops::fridge_queue.status.Clear();
+ control_loops::fridge_queue.goal.Clear();
+ }
+
+ virtual ~StackActionTest() {
+ frc971::actors::stack_action.goal.Clear();
+ frc971::actors::stack_action.status.Clear();
+ control_loops::fridge_queue.status.Clear();
+ control_loops::fridge_queue.goal.Clear();
+ }
+
+ // Bring up and down Core.
+ ::aos::common::testing::GlobalCoreInstance my_core;
+};
+
+// Tests that cancel stops not only the stack action, but the underlying profile
+// action.
+TEST_F(StackActionTest, StackCancel) {
+ StackActor stack(&frc971::actors::stack_action);
+
+ frc971::actors::stack_action.goal.MakeWithBuilder().run(true).Send();
+
+ // tell it the fridge is zeroed
+ control_loops::fridge_queue.status.MakeWithBuilder()
+ .zeroed(true)
+ .angle(0.0)
+ .height(0.0)
+ .Send();
+
+ // do the action and it will post to the goal queue
+ stack.WaitForActionRequest();
+
+ // the action has started, so now cancel it and it should cancel
+ // the underlying profile
+ frc971::actors::stack_action.goal.MakeWithBuilder().run(false).Send();
+
+ // let the action start running, if we return from this call it has worked.
+ StackParams s;
+ stack.RunAction(s);
+
+ SUCCEED();
+}
+
+} // namespace testing
+} // namespace actors
+} // namespace frc971
diff --git a/y2015/actors/stack_and_hold_action.q b/y2015/actors/stack_and_hold_action.q
new file mode 100644
index 0000000..42e98ed
--- /dev/null
+++ b/y2015/actors/stack_and_hold_action.q
@@ -0,0 +1,42 @@
+package frc971.actors;
+
+import "aos/common/actions/actions.q";
+import "y2015/actors/stack_action_params.q";
+
+// Parameters to send with start.
+struct StackAndHoldParams {
+ // If true, there is no tote on the tray, and we should place instead.
+ bool place_not_stack;
+
+ double claw_out_angle;
+ // The height just above the box to move before lowering.
+ double over_box_before_place_height;
+
+ // Bottom position.
+ double bottom;
+
+ // If we are placing, clamp the stack with the claw.
+ double claw_clamp_angle;
+
+ // Amount to wait to release.
+ double clamp_pause_time;
+
+ // The value to move the arm forwards to clear the stack when lifting.
+ double arm_clearance;
+ // Hold height
+ double hold_height;
+};
+
+queue_group StackAndHoldActionQueueGroup {
+ implements aos.common.actions.ActionQueueGroup;
+
+ message Goal {
+ uint32_t run;
+ StackAndHoldParams params;
+ };
+
+ queue Goal goal;
+ queue aos.common.actions.Status status;
+};
+
+queue_group StackAndHoldActionQueueGroup stack_and_hold_action;
diff --git a/y2015/actors/stack_and_hold_actor.cc b/y2015/actors/stack_and_hold_actor.cc
new file mode 100644
index 0000000..ae1eeda
--- /dev/null
+++ b/y2015/actors/stack_and_hold_actor.cc
@@ -0,0 +1,132 @@
+#include "y2015/actors/stack_and_hold_actor.h"
+
+#include <math.h>
+
+#include "aos/common/time.h"
+#include "aos/common/util/phased_loop.h"
+
+#include "y2015/constants.h"
+#include "y2015/control_loops/claw/claw.q.h"
+#include "y2015/actors/stack_actor.h"
+
+namespace frc971 {
+namespace actors {
+namespace {
+constexpr ProfileParams kReallySlowArmMove{0.1, 1.0};
+constexpr ProfileParams kReallySlowElevatorMove{0.10, 1.0};
+
+constexpr ProfileParams kFastArmMove{0.8, 4.0};
+constexpr ProfileParams kFastElevatorMove{1.2, 4.0};
+} // namespace
+
+StackAndHoldActor::StackAndHoldActor(StackAndHoldActionQueueGroup *queues)
+ : FridgeActorBase<StackAndHoldActionQueueGroup>(queues) {}
+
+bool StackAndHoldActor::RunAction(const StackAndHoldParams ¶ms) {
+ // TODO(ben)): this action is no longer used (source Cameron) and my be broken
+ // by the stack action having the grabbers closed at the end for the fangs. So
+ // here I am disabling it until further information is provided.
+ if (params.place_not_stack) {
+ // Move the arm out of the way.
+ {
+ bool send_goal = true;
+ control_loops::claw_queue.status.FetchLatest();
+ if (control_loops::claw_queue.status.get()) {
+ if (control_loops::claw_queue.status->goal_angle <
+ params.claw_out_angle) {
+ send_goal = false;
+ }
+ }
+ if (send_goal) {
+ auto message = control_loops::claw_queue.goal.MakeMessage();
+ message->angle = params.claw_out_angle;
+ message->angular_velocity = 0.0;
+ message->intake = 0.0;
+ message->rollers_closed = true;
+ message->max_velocity = 6.0;
+ message->max_acceleration = 10.0;
+
+ LOG_STRUCT(DEBUG, "Sending claw goal", *message);
+ message.Send();
+ }
+ }
+
+ // Get close, but keep the arm forwards
+ DoFridgeProfile(params.bottom + 0.04, -0.05, kFastArmMove,
+ kFastElevatorMove, true);
+
+ // Lower and pull back.
+ if (ShouldCancel()) return true;
+ DoFridgeProfile(params.bottom, 0.0, kReallySlowArmMove,
+ kReallySlowElevatorMove, true, true, false);
+
+ // Release.
+ if (ShouldCancel()) return true;
+ DoFridgeProfile(params.bottom, 0.0, kReallySlowArmMove,
+ kReallySlowElevatorMove, false);
+ } else {
+ StackParams stack_params;
+ stack_params.only_place = true;
+ stack_params.arm_clearance = params.arm_clearance;
+ stack_params.claw_out_angle = params.claw_out_angle;
+ stack_params.over_box_before_place_height =
+ params.over_box_before_place_height;
+ stack_params.bottom = params.bottom;
+ ::std::unique_ptr<StackAction> stack_action =
+ MakeStackAction(stack_params);
+ stack_action->Start();
+ while (stack_action->Running()) {
+ ::aos::time::PhasedLoopXMS(::aos::controls::kLoopFrequency.ToMSec(),
+ 2500);
+
+ if (ShouldCancel()) {
+ stack_action->Cancel();
+ LOG(WARNING, "Cancelling fridge and claw.\n");
+ return true;
+ }
+ }
+ }
+
+ if (!WaitOrCancel(aos::time::Time::InSeconds(params.clamp_pause_time))) {
+ return true;
+ }
+
+ // Go up.
+ DoFridgeProfile(params.hold_height, params.arm_clearance, kFastArmMove,
+ kFastElevatorMove, false);
+
+ if (ShouldCancel()) return true;
+
+ if (params.place_not_stack) {
+ // Clamp the stack with the claw.
+ auto message = control_loops::claw_queue.goal.MakeMessage();
+ message->angle = params.claw_clamp_angle;
+ message->angular_velocity = 0.0;
+ message->intake = 0.0;
+ message->rollers_closed = true;
+ message->max_velocity = 6.0;
+ message->max_acceleration = 6.0;
+
+ LOG_STRUCT(DEBUG, "Sending claw goal", *message);
+ message.Send();
+ }
+ // Move back
+ DoFridgeProfile(params.hold_height, 0.0, kFastArmMove, kFastElevatorMove,
+ false);
+ if (ShouldCancel()) return true;
+ // Grab
+ DoFridgeProfile(params.hold_height, 0.0, kFastArmMove, kFastElevatorMove,
+ true);
+ if (ShouldCancel()) return true;
+
+ return true;
+}
+
+::std::unique_ptr<StackAndHoldAction> MakeStackAndHoldAction(
+ const StackAndHoldParams ¶ms) {
+ return ::std::unique_ptr<StackAndHoldAction>(
+ new StackAndHoldAction(&::frc971::actors::stack_and_hold_action, params));
+}
+
+} // namespace actors
+} // namespace frc971
diff --git a/y2015/actors/stack_and_hold_actor.h b/y2015/actors/stack_and_hold_actor.h
new file mode 100644
index 0000000..b3bf2c5
--- /dev/null
+++ b/y2015/actors/stack_and_hold_actor.h
@@ -0,0 +1,33 @@
+#ifndef Y2015_ACTORS_STACK_AND_HOLD_ACTOR_H_
+#define Y2015_ACTORS_STACK_AND_HOLD_ACTOR_H_
+
+#include <stdint.h>
+
+#include <memory>
+
+#include "aos/common/actions/actions.h"
+#include "aos/common/actions/actor.h"
+#include "y2015/actors/stack_and_hold_action.q.h"
+#include "y2015/actors/fridge_profile_lib.h"
+
+namespace frc971 {
+namespace actors {
+
+class StackAndHoldActor : public FridgeActorBase<StackAndHoldActionQueueGroup> {
+ public:
+ explicit StackAndHoldActor(StackAndHoldActionQueueGroup *queues);
+
+ bool RunAction(const StackAndHoldParams ¶ms) override;
+};
+
+typedef aos::common::actions::TypedAction<StackAndHoldActionQueueGroup>
+ StackAndHoldAction;
+
+// Makes a new stackActor action.
+::std::unique_ptr<StackAndHoldAction> MakeStackAndHoldAction(
+ const StackAndHoldParams ¶ms);
+
+} // namespace actors
+} // namespace frc971
+
+#endif // Y2015_ACTORS_STACK_AND_HOLD_ACTOR_H_
diff --git a/y2015/actors/stack_and_hold_actor_main.cc b/y2015/actors/stack_and_hold_actor_main.cc
new file mode 100644
index 0000000..3d5d1c1
--- /dev/null
+++ b/y2015/actors/stack_and_hold_actor_main.cc
@@ -0,0 +1,16 @@
+#include <stdio.h>
+
+#include "aos/linux_code/init.h"
+#include "y2015/actors/stack_and_hold_action.q.h"
+#include "y2015/actors/stack_and_hold_actor.h"
+
+int main(int /*argc*/, char* /*argv*/ []) {
+ ::aos::Init();
+
+ ::frc971::actors::StackAndHoldActor stack_and_hold(
+ &::frc971::actors::stack_and_hold_action);
+ stack_and_hold.Run();
+
+ ::aos::Cleanup();
+ return 0;
+}
diff --git a/y2015/actors/stack_and_lift_action.q b/y2015/actors/stack_and_lift_action.q
new file mode 100644
index 0000000..0ec971b
--- /dev/null
+++ b/y2015/actors/stack_and_lift_action.q
@@ -0,0 +1,34 @@
+package frc971.actors;
+
+import "aos/common/actions/actions.q";
+
+import "y2015/actors/stack_action_params.q";
+import "y2015/actors/lift_action_params.q";
+
+// Parameters to send with start.
+struct StackAndLiftParams {
+ // Stack parameters
+ StackParams stack_params;
+ // True if the fridge should be clamped while lifting.
+ bool grab_after_stack;
+ // The time after clamping to pause.
+ double clamp_pause_time;
+ // Lift parameters
+ LiftParams lift_params;
+ // True if the fridge should be clamped when done.
+ bool grab_after_lift;
+};
+
+queue_group StackAndLiftActionQueueGroup {
+ implements aos.common.actions.ActionQueueGroup;
+
+ message Goal {
+ uint32_t run;
+ StackAndLiftParams params;
+ };
+
+ queue Goal goal;
+ queue aos.common.actions.Status status;
+};
+
+queue_group StackAndLiftActionQueueGroup stack_and_lift_action;
diff --git a/y2015/actors/stack_and_lift_actor.cc b/y2015/actors/stack_and_lift_actor.cc
new file mode 100644
index 0000000..32f7417
--- /dev/null
+++ b/y2015/actors/stack_and_lift_actor.cc
@@ -0,0 +1,115 @@
+#include "y2015/actors/stack_and_lift_actor.h"
+
+#include <math.h>
+
+#include "aos/common/controls/control_loop.h"
+#include "aos/common/time.h"
+#include "aos/common/util/phased_loop.h"
+
+#include "y2015/constants.h"
+#include "y2015/control_loops/claw/claw.q.h"
+#include "y2015/actors/stack_actor.h"
+#include "y2015/actors/lift_actor.h"
+
+namespace frc971 {
+namespace actors {
+
+StackAndLiftActor::StackAndLiftActor(StackAndLiftActionQueueGroup *queues)
+ : FridgeActorBase<StackAndLiftActionQueueGroup>(queues) {}
+
+bool StackAndLiftActor::RunAction(const StackAndLiftParams ¶ms) {
+ control_loops::claw_queue.goal.FetchLatest();
+ double claw_goal_start;
+ bool have_claw_goal_start;
+ if (control_loops::claw_queue.goal.get()) {
+ claw_goal_start = control_loops::claw_queue.goal->angle;
+ have_claw_goal_start = true;
+ } else {
+ claw_goal_start = 0;
+ have_claw_goal_start = false;
+ }
+
+ {
+ StackParams stack_params = params.stack_params;
+ stack_params.only_place = false;
+ ::std::unique_ptr<StackAction> stack_action =
+ MakeStackAction(stack_params);
+ stack_action->Start();
+ while (stack_action->Running()) {
+ ::aos::time::PhasedLoopXMS(::aos::controls::kLoopFrequency.ToMSec(),
+ 2500);
+
+ if (ShouldCancel()) {
+ stack_action->Cancel();
+ LOG(WARNING, "Cancelling fridge and claw.\n");
+ return true;
+ }
+ }
+ }
+
+ {
+ control_loops::fridge_queue.goal.FetchLatest();
+ if (!control_loops::fridge_queue.goal.get()) {
+ return false;
+ }
+ auto new_fridge_goal = control_loops::fridge_queue.goal.MakeMessage();
+ *new_fridge_goal = *control_loops::fridge_queue.goal;
+ new_fridge_goal->grabbers.top_front = params.grab_after_stack;
+ new_fridge_goal->grabbers.top_back = params.grab_after_stack;
+ new_fridge_goal->grabbers.bottom_front = params.grab_after_stack;
+ new_fridge_goal->grabbers.bottom_back = params.grab_after_stack;
+ if (!new_fridge_goal.Send()) {
+ LOG(ERROR, "Failed to send fridge close.\n");
+ return false;
+ }
+ }
+
+ if (!WaitOrCancel(aos::time::Time::InSeconds(params.clamp_pause_time))) {
+ return true;
+ }
+
+ {
+ auto lift_params = params.lift_params;
+ lift_params.pack_claw = have_claw_goal_start;
+ lift_params.pack_claw_angle = claw_goal_start;
+ ::std::unique_ptr<LiftAction> lift_action = MakeLiftAction(lift_params);
+ lift_action->Start();
+ while (lift_action->Running()) {
+ ::aos::time::PhasedLoopXMS(::aos::controls::kLoopFrequency.ToMSec(),
+ 2500);
+
+ if (ShouldCancel()) {
+ lift_action->Cancel();
+ LOG(WARNING, "Cancelling fridge and claw.\n");
+ return true;
+ }
+ }
+ }
+
+ {
+ control_loops::fridge_queue.goal.FetchLatest();
+ if (!control_loops::fridge_queue.goal.get()) {
+ return false;
+ }
+ auto new_fridge_goal = control_loops::fridge_queue.goal.MakeMessage();
+ *new_fridge_goal = *control_loops::fridge_queue.goal;
+ new_fridge_goal->grabbers.top_front = params.grab_after_lift;
+ new_fridge_goal->grabbers.top_back = params.grab_after_lift;
+ new_fridge_goal->grabbers.bottom_front = params.grab_after_lift;
+ new_fridge_goal->grabbers.bottom_back = params.grab_after_lift;
+ if (!new_fridge_goal.Send()) {
+ LOG(ERROR, "Failed to send fridge close.\n");
+ return false;
+ }
+ }
+ return true;
+}
+
+::std::unique_ptr<StackAndLiftAction> MakeStackAndLiftAction(
+ const StackAndLiftParams ¶ms) {
+ return ::std::unique_ptr<StackAndLiftAction>(
+ new StackAndLiftAction(&::frc971::actors::stack_and_lift_action, params));
+}
+
+} // namespace actors
+} // namespace frc971
diff --git a/y2015/actors/stack_and_lift_actor.h b/y2015/actors/stack_and_lift_actor.h
new file mode 100644
index 0000000..1d5fe29
--- /dev/null
+++ b/y2015/actors/stack_and_lift_actor.h
@@ -0,0 +1,33 @@
+#ifndef Y2015_ACTORS_STACK_AND_LIFT_ACTOR_H_
+#define Y2015_ACTORS_STACK_AND_LIFT_ACTOR_H_
+
+#include <stdint.h>
+
+#include <memory>
+
+#include "aos/common/actions/actions.h"
+#include "aos/common/actions/actor.h"
+#include "y2015/actors/stack_and_lift_action.q.h"
+#include "y2015/actors/fridge_profile_lib.h"
+
+namespace frc971 {
+namespace actors {
+
+class StackAndLiftActor : public FridgeActorBase<StackAndLiftActionQueueGroup> {
+ public:
+ explicit StackAndLiftActor(StackAndLiftActionQueueGroup *queues);
+
+ bool RunAction(const StackAndLiftParams ¶ms) override;
+};
+
+typedef aos::common::actions::TypedAction<StackAndLiftActionQueueGroup>
+ StackAndLiftAction;
+
+// Makes a new stackActor action.
+::std::unique_ptr<StackAndLiftAction> MakeStackAndLiftAction(
+ const StackAndLiftParams ¶ms);
+
+} // namespace actors
+} // namespace frc971
+
+#endif // Y2015_ACTORS_STACK_AND_LIFT_ACTOR_H_
diff --git a/y2015/actors/stack_and_lift_actor_main.cc b/y2015/actors/stack_and_lift_actor_main.cc
new file mode 100644
index 0000000..fe9f4b5
--- /dev/null
+++ b/y2015/actors/stack_and_lift_actor_main.cc
@@ -0,0 +1,16 @@
+#include <stdio.h>
+
+#include "aos/linux_code/init.h"
+#include "y2015/actors/stack_and_lift_action.q.h"
+#include "y2015/actors/stack_and_lift_actor.h"
+
+int main(int /*argc*/, char* /*argv*/ []) {
+ ::aos::Init();
+
+ ::frc971::actors::StackAndLiftActor stack_and_lift(
+ &::frc971::actors::stack_and_lift_action);
+ stack_and_lift.Run();
+
+ ::aos::Cleanup();
+ return 0;
+}
diff --git a/y2015/autonomous/auto.cc b/y2015/autonomous/auto.cc
new file mode 100644
index 0000000..9ade207
--- /dev/null
+++ b/y2015/autonomous/auto.cc
@@ -0,0 +1,558 @@
+#include <stdio.h>
+
+#include <memory>
+
+#include "aos/common/util/phased_loop.h"
+#include "aos/common/time.h"
+#include "aos/common/util/trapezoid_profile.h"
+#include "aos/common/logging/logging.h"
+#include "aos/common/logging/queue_logging.h"
+
+#include "frc971/autonomous/auto.q.h"
+#include "y2015/constants.h"
+#include "y2015/control_loops/drivetrain/drivetrain.q.h"
+#include "y2015/actors/drivetrain_actor.h"
+#include "y2015/control_loops/claw/claw.q.h"
+#include "y2015/control_loops/fridge/fridge.q.h"
+#include "y2015/actors/pickup_actor.h"
+#include "y2015/actors/stack_actor.h"
+#include "y2015/actors/held_to_lift_actor.h"
+
+using ::aos::time::Time;
+using ::frc971::control_loops::claw_queue;
+using ::frc971::control_loops::fridge_queue;
+using ::frc971::control_loops::drivetrain_queue;
+
+namespace frc971 {
+namespace autonomous {
+
+constexpr double kClawAutoVelocity = 3.00;
+constexpr double kClawAutoAcceleration = 6.0;
+constexpr double kAngleEpsilon = 0.10;
+const double kClawTotePackAngle = 0.90;
+const double kArmRaiseLowerClearance = -0.08;
+const double kClawStackClearance = 0.55;
+const double kStackUpHeight = 0.60;
+const double kStackUpArm = 0.0;
+
+struct ProfileParams {
+ double velocity;
+ double acceleration;
+};
+
+namespace time = ::aos::time;
+
+static double left_initial_position, right_initial_position;
+
+bool ShouldExitAuto() {
+ ::frc971::autonomous::autonomous.FetchLatest();
+ bool ans = !::frc971::autonomous::autonomous->run_auto;
+ if (ans) {
+ LOG(INFO, "Time to exit auto mode\n");
+ }
+ return ans;
+}
+
+void StopDrivetrain() {
+ LOG(INFO, "Stopping the drivetrain\n");
+ control_loops::drivetrain_queue.goal.MakeWithBuilder()
+ .control_loop_driving(true)
+ .left_goal(left_initial_position)
+ .left_velocity_goal(0)
+ .right_goal(right_initial_position)
+ .right_velocity_goal(0)
+ .quickturn(false)
+ .Send();
+}
+
+void ResetDrivetrain() {
+ LOG(INFO, "resetting the drivetrain\n");
+ control_loops::drivetrain_queue.goal.MakeWithBuilder()
+ .control_loop_driving(false)
+ //.highgear(false)
+ .steering(0.0)
+ .throttle(0.0)
+ .left_goal(left_initial_position)
+ .left_velocity_goal(0)
+ .right_goal(right_initial_position)
+ .right_velocity_goal(0)
+ .Send();
+}
+
+void WaitUntilDoneOrCanceled(
+ ::std::unique_ptr<aos::common::actions::Action> action) {
+ if (!action) {
+ LOG(ERROR, "No action, not waiting\n");
+ return;
+ }
+ while (true) {
+ // Poll the running bit and auto done bits.
+ ::aos::time::PhasedLoopXMS(5, 2500);
+ if (!action->Running() || ShouldExitAuto()) {
+ return;
+ }
+ }
+}
+
+void StepDrive(double distance, double theta) {
+ double left_goal = (left_initial_position + distance -
+ theta * constants::GetValues().turn_width / 2.0);
+ double right_goal = (right_initial_position + distance +
+ theta * constants::GetValues().turn_width / 2.0);
+ control_loops::drivetrain_queue.goal.MakeWithBuilder()
+ .control_loop_driving(true)
+ .left_goal(left_goal)
+ .right_goal(right_goal)
+ .left_velocity_goal(0.0)
+ .right_velocity_goal(0.0)
+ .Send();
+ left_initial_position = left_goal;
+ right_initial_position = right_goal;
+}
+
+void WaitUntilNear(double distance) {
+ while (true) {
+ if (ShouldExitAuto()) return;
+ control_loops::drivetrain_queue.status.FetchAnother();
+ double left_error = ::std::abs(
+ left_initial_position -
+ control_loops::drivetrain_queue.status->filtered_left_position);
+ double right_error = ::std::abs(
+ right_initial_position -
+ control_loops::drivetrain_queue.status->filtered_right_position);
+ const double kPositionThreshold = 0.05 + distance;
+ if (right_error < kPositionThreshold && left_error < kPositionThreshold) {
+ LOG(INFO, "At the goal\n");
+ return;
+ }
+ }
+}
+
+const ProfileParams kFastDrive = {2.0, 3.5};
+const ProfileParams kFastKnockDrive = {2.0, 3.0};
+const ProfileParams kStackingSecondDrive = {2.0, 1.5};
+const ProfileParams kFastTurn = {3.0, 10.0};
+const ProfileParams kStackingFirstTurn = {1.0, 1.0};
+const ProfileParams kStackingSecondTurn = {2.0, 6.0};
+const ProfileParams kComboTurn = {1.2, 8.0};
+const ProfileParams kRaceTurn = {4.0, 10.0};
+const ProfileParams kRaceDrive = {2.0, 2.0};
+const ProfileParams kRaceBackupDrive = {2.0, 5.0};
+
+::std::unique_ptr<::frc971::actors::DrivetrainAction> SetDriveGoal(
+ double distance, const ProfileParams drive_params, double theta = 0, const ProfileParams &turn_params = kFastTurn) {
+ LOG(INFO, "Driving to %f\n", distance);
+
+ ::frc971::actors::DrivetrainActionParams params;
+ params.left_initial_position = left_initial_position;
+ params.right_initial_position = right_initial_position;
+ params.y_offset = distance;
+ params.theta_offset = theta;
+ params.maximum_turn_acceleration = turn_params.acceleration;
+ params.maximum_turn_velocity = turn_params.velocity;
+ params.maximum_velocity = drive_params.velocity;
+ params.maximum_acceleration = drive_params.acceleration;
+ auto drivetrain_action = actors::MakeDrivetrainAction(params);
+
+ drivetrain_action->Start();
+ left_initial_position +=
+ distance - theta * constants::GetValues().turn_width / 2.0;
+ right_initial_position +=
+ distance + theta * constants::GetValues().turn_width / 2.0;
+ return ::std::move(drivetrain_action);
+}
+
+const ProfileParams kFridgeYProfile{1.0, 4.0};
+const ProfileParams kFridgeXProfile{1.0, 2.0};
+const ProfileParams kFridgeFastXProfile{1.2, 5.0};
+
+static double fridge_goal_x = 0.0;
+static double fridge_goal_y = 0.0;
+
+void MoveFridge(double x, double y, bool grabbers, const ProfileParams x_params,
+ const ProfileParams y_params) {
+ auto new_fridge_goal = fridge_queue.goal.MakeMessage();
+ new_fridge_goal->profiling_type = 1;
+
+ new_fridge_goal->max_y_velocity = y_params.velocity;
+ new_fridge_goal->max_y_acceleration = y_params.acceleration;
+ new_fridge_goal->y = y;
+ fridge_goal_y = y;
+ new_fridge_goal->y_velocity = 0.0;
+
+ new_fridge_goal->max_x_velocity = x_params.velocity;
+ new_fridge_goal->max_x_acceleration = x_params.acceleration;
+ new_fridge_goal->x = x;
+ fridge_goal_x = x;
+ new_fridge_goal->x_velocity = 0.0;
+
+ new_fridge_goal->grabbers.top_front = grabbers;
+ new_fridge_goal->grabbers.top_back = grabbers;
+ new_fridge_goal->grabbers.bottom_front = grabbers;
+ new_fridge_goal->grabbers.bottom_back = grabbers;
+
+ if (!new_fridge_goal.Send()) {
+ LOG(ERROR, "Sending fridge goal failed.\n");
+ return;
+ }
+}
+
+void WaitForFridge() {
+ while (true) {
+ if (ShouldExitAuto()) return;
+ control_loops::fridge_queue.status.FetchAnother();
+
+ constexpr double kProfileError = 1e-5;
+ constexpr double kXEpsilon = 0.03, kYEpsilon = 0.03;
+
+ if (control_loops::fridge_queue.status->state != 4) {
+ LOG(ERROR, "Fridge no longer running, aborting action\n");
+ return;
+ }
+
+ if (::std::abs(control_loops::fridge_queue.status->goal_x - fridge_goal_x) <
+ kProfileError &&
+ ::std::abs(control_loops::fridge_queue.status->goal_y - fridge_goal_y) <
+ kProfileError &&
+ ::std::abs(control_loops::fridge_queue.status->goal_x_velocity) <
+ kProfileError &&
+ ::std::abs(control_loops::fridge_queue.status->goal_y_velocity) <
+ kProfileError) {
+ LOG(INFO, "Profile done.\n");
+ if (::std::abs(control_loops::fridge_queue.status->x - fridge_goal_x) <
+ kXEpsilon &&
+ ::std::abs(control_loops::fridge_queue.status->y - fridge_goal_y) <
+ kYEpsilon) {
+ LOG(INFO, "Near goal, done.\n");
+ return;
+ }
+ }
+ }
+}
+
+void InitializeEncoders() {
+ control_loops::drivetrain_queue.status.FetchAnother();
+ left_initial_position =
+ control_loops::drivetrain_queue.status->filtered_left_position;
+ right_initial_position =
+ control_loops::drivetrain_queue.status->filtered_right_position;
+}
+
+void WaitForClawZero() {
+ LOG(INFO, "Waiting for claw to zero.\n");
+ while (true) {
+ control_loops::claw_queue.status.FetchAnother();
+ LOG_STRUCT(DEBUG, "Got claw status", *control_loops::claw_queue.status);
+ if (control_loops::claw_queue.status->zeroed) {
+ LOG(INFO, "Claw zeroed\n");
+ return;
+ }
+
+ if (ShouldExitAuto()) return;
+ }
+}
+
+void WaitForFridgeZero() {
+ LOG(INFO, "Waiting for claw to zero.\n");
+ while (true) {
+ control_loops::fridge_queue.status.FetchAnother();
+ LOG_STRUCT(DEBUG, "Got fridge status", *control_loops::fridge_queue.status);
+ if (control_loops::fridge_queue.status->zeroed) {
+ LOG(INFO, "Fridge zeroed\n");
+ return;
+ }
+
+ if (ShouldExitAuto()) return;
+ }
+}
+
+constexpr ProfileParams kDefaultClawParams = {kClawAutoVelocity,
+ kClawAutoAcceleration};
+
+// Move the claw in a very small number of cycles.
+constexpr ProfileParams kInstantaneousClaw = {100.0, 100.0};
+constexpr ProfileParams kFastClaw = {8.0, 20.0};
+
+void SetClawStateNoWait(double angle, double intake_voltage,
+ double rollers_closed,
+ const ProfileParams &claw_params = kDefaultClawParams) {
+ auto message = control_loops::claw_queue.goal.MakeMessage();
+ message->angle = angle;
+ message->max_velocity = claw_params.velocity;
+ message->max_acceleration = claw_params.acceleration;
+ message->angular_velocity = 0.0;
+ message->intake = intake_voltage;
+ message->rollers_closed = rollers_closed;
+
+ LOG_STRUCT(DEBUG, "Sending claw goal", *message);
+ message.Send();
+}
+
+void SetClawState(double angle, double intake_voltage, double rollers_closed,
+ const ProfileParams &claw_params = kDefaultClawParams) {
+ SetClawStateNoWait(angle, intake_voltage, rollers_closed, claw_params);
+ while (true) {
+ control_loops::claw_queue.status.FetchAnother();
+ const double current_angle = control_loops::claw_queue.status->angle;
+ LOG_STRUCT(DEBUG, "Got claw status", *control_loops::claw_queue.status);
+
+ // I believe all we can care about here is the angle. Other values will
+ // either be set or not, but there is nothing we can do about it. If it
+ // never gets there we do not care, auto is over for us.
+ if (::std::abs(current_angle - angle) < kAngleEpsilon) {
+ break;
+ }
+ if (ShouldExitAuto()) return;
+ }
+}
+
+void HandleAuto() {
+ ::aos::time::Time start_time = ::aos::time::Time::Now();
+ LOG(INFO, "Starting auto mode at %f\n", start_time.ToSeconds());
+ ::std::unique_ptr<::frc971::actors::DrivetrainAction> drive;
+ ::std::unique_ptr<::frc971::actors::PickupAction> pickup;
+ ::std::unique_ptr<::frc971::actors::StackAction> stack;
+ ::std::unique_ptr<::frc971::actors::HeldToLiftAction> lift;
+
+ actors::PickupParams pickup_params;
+ // Lift to here initially.
+ pickup_params.pickup_angle = 0.9;
+ // Start sucking here
+ pickup_params.suck_angle = 0.8;
+ // Go back down to here to finish sucking.
+ pickup_params.suck_angle_finish = 0.4;
+ // Pack the box back in here.
+ pickup_params.pickup_finish_angle = kClawTotePackAngle;
+ pickup_params.intake_time = 0.70;
+ pickup_params.intake_voltage = 7.0;
+
+ if (ShouldExitAuto()) return;
+ InitializeEncoders();
+ ResetDrivetrain();
+
+ WaitForClawZero();
+ WaitForFridgeZero();
+
+ // Initialize the fridge.
+ MoveFridge(0.0, 0.3, true, kFridgeXProfile, kFridgeYProfile);
+
+ LOG(INFO, "Lowering claw into position.\n");
+ SetClawState(0.0, 2.0, false, kInstantaneousClaw);
+
+ LOG(INFO, "Sucking in tote.\n");
+ SetClawState(0.0, 6.0, true, kInstantaneousClaw);
+
+ time::SleepFor(time::Time::InSeconds(0.7));
+ LOG(INFO, "Done sucking in tote\n");
+
+ // Now pick it up
+ pickup = actors::MakePickupAction(pickup_params);
+ pickup->Start();
+
+ time::SleepFor(time::Time::InSeconds(0.9));
+ // Start turning.
+ LOG(INFO, "Turning in place\n");
+ drive = SetDriveGoal(0.0, kFastDrive, -0.23, kStackingFirstTurn);
+
+ WaitUntilDoneOrCanceled(::std::move(drive));
+ if (ShouldExitAuto()) return;
+
+ LOG(INFO, "Now driving next to the can\n");
+ drive = SetDriveGoal(0.60, kFastDrive);
+
+ WaitUntilDoneOrCanceled(::std::move(pickup));
+ if (ShouldExitAuto()) return;
+
+ // Now grab it in the fridge.
+ {
+ actors::StackParams params;
+
+ params.claw_out_angle = kClawTotePackAngle;
+ params.bottom = 0.020;
+ params.only_place = false;
+ params.arm_clearance = kArmRaiseLowerClearance;
+ params.over_box_before_place_height = 0.39;
+
+ stack = actors::MakeStackAction(params);
+ stack->Start();
+ }
+ WaitUntilDoneOrCanceled(::std::move(stack));
+ if (ShouldExitAuto()) return;
+
+ // Lower the claw to knock the tote.
+ LOG(INFO, "Lowering the claw to knock the tote\n");
+ SetClawStateNoWait(0.0, 0.0, true, kFastClaw);
+
+ time::SleepFor(time::Time::InSeconds(0.1));
+ if (ShouldExitAuto()) return;
+
+ WaitUntilDoneOrCanceled(::std::move(drive));
+ if (ShouldExitAuto()) return;
+
+ LOG(INFO, "Knocking the can over\n");
+ drive = SetDriveGoal(0.40, kFastKnockDrive, 1.05, kComboTurn);
+ WaitUntilDoneOrCanceled(::std::move(drive));
+ if (ShouldExitAuto()) return;
+ {
+ actors::HeldToLiftParams params;
+ params.arm_clearance = kArmRaiseLowerClearance;
+ params.clamp_pause_time = 0.1;
+ params.before_lift_settle_time = 0.1;
+ params.bottom_height = 0.020;
+ params.claw_out_angle = kClawStackClearance;
+ params.lift_params.lift_height = kStackUpHeight;
+ params.lift_params.lift_arm = kStackUpArm;
+ params.lift_params.second_lift = false;
+
+ lift = actors::MakeHeldToLiftAction(params);
+ lift->Start();
+ }
+
+ LOG(INFO, "Turning back to aim\n");
+ drive = SetDriveGoal(0.0, kFastDrive, -0.70);
+ WaitUntilDoneOrCanceled(::std::move(drive));
+ if (ShouldExitAuto()) return;
+
+ SetClawStateNoWait(0.0, 4.0, false, kFastClaw);
+ LOG(INFO, "Now driving the second tote\n");
+ drive = SetDriveGoal(1.05, kFastDrive);
+
+ // Wait until we are almost at the tote, and then start intaking.
+ WaitUntilNear(0.35);
+
+ SetClawState(0.0, 6.0, true, kFastClaw);
+ WaitUntilDoneOrCanceled(::std::move(drive));
+ if (ShouldExitAuto()) return;
+
+ if (ShouldExitAuto()) return;
+ time::SleepFor(time::Time::InSeconds(0.30));
+ if (ShouldExitAuto()) return;
+
+ SetClawStateNoWait(0.0, 4.0, true, kFastClaw);
+ if (ShouldExitAuto()) return;
+ time::SleepFor(time::Time::InSeconds(0.10));
+
+ WaitUntilDoneOrCanceled(::std::move(lift));
+ if (ShouldExitAuto()) return;
+
+ LOG(INFO, "Done sucking in tote\n");
+ SetClawState(0.0, 0.0, true, kFastClaw);
+ if (ShouldExitAuto()) return;
+
+ // Now pick it up
+ pickup = actors::MakePickupAction(pickup_params);
+ pickup->Start();
+
+ time::SleepFor(time::Time::InSeconds(1.0));
+ if (ShouldExitAuto()) return;
+
+ // Start turning.
+ LOG(INFO, "Turning in place\n");
+ drive = SetDriveGoal(0.0, kStackingSecondDrive, -0.40, kStackingSecondTurn);
+
+ WaitUntilDoneOrCanceled(::std::move(pickup));
+ if (ShouldExitAuto()) return;
+
+ // Now grab it in the fridge.
+ {
+ actors::StackParams params;
+
+ params.claw_out_angle = kClawTotePackAngle;
+ params.bottom = 0.020;
+ params.only_place = false;
+ params.arm_clearance = kArmRaiseLowerClearance;
+ params.over_box_before_place_height = 0.39;
+
+ stack = actors::MakeStackAction(params);
+ stack->Start();
+ }
+
+ WaitUntilDoneOrCanceled(::std::move(drive));
+ if (ShouldExitAuto()) return;
+ LOG(INFO, "Driving next to the can.\n");
+ drive = SetDriveGoal(0.65, kStackingSecondDrive);
+
+ WaitUntilDoneOrCanceled(::std::move(stack));
+ if (ShouldExitAuto()) return;
+
+ // Lower the claw to knock the tote.
+ LOG(INFO, "Lowering the claw to knock the tote\n");
+ SetClawStateNoWait(0.0, 0.0, true, kFastClaw);
+
+ // Lift the fridge.
+ MoveFridge(0.0, 0.3, true, kFridgeXProfile, kFridgeYProfile);
+
+ time::SleepFor(time::Time::InSeconds(0.1));
+ if (ShouldExitAuto()) return;
+
+ WaitUntilDoneOrCanceled(::std::move(drive));
+ if (ShouldExitAuto()) return;
+
+ LOG(INFO, "Knocking the can over\n");
+ drive = SetDriveGoal(0.40, kFastKnockDrive, 1.05, kComboTurn);
+ WaitUntilDoneOrCanceled(::std::move(drive));
+ if (ShouldExitAuto()) return;
+
+ LOG(INFO, "Turning back to aim\n");
+ drive = SetDriveGoal(0.0, kFastDrive, -0.60);
+ WaitUntilDoneOrCanceled(::std::move(drive));
+ if (ShouldExitAuto()) return;
+
+
+ SetClawStateNoWait(0.0, 4.0, false, kFastClaw);
+ LOG(INFO, "Now driving to the last tote\n");
+ drive = SetDriveGoal(1.05, kFastDrive);
+ WaitUntilNear(0.05);
+
+ SetClawState(0.0, 7.0, true, kFastClaw);
+ if (ShouldExitAuto()) return;
+
+ time::SleepFor(time::Time::InSeconds(0.2));
+ if (ShouldExitAuto()) return;
+
+ WaitUntilDoneOrCanceled(::std::move(drive));
+ if (ShouldExitAuto()) return;
+ SetClawState(0.0, 6.0, true, kFastClaw);
+
+ LOG(INFO, "Racing over\n");
+ //StepDrive(2.5, -1.4);
+ drive = SetDriveGoal(2.5, kRaceDrive, -1.4, kRaceTurn);
+
+ time::SleepFor(time::Time::InSeconds(0.5));
+
+ LOG(INFO, "Moving totes out\n");
+ MoveFridge(0.6, 0.32, true, kFridgeXProfile, kFridgeYProfile);
+
+ WaitForFridge();
+ if (ShouldExitAuto()) return;
+
+ LOG(INFO, "Lowering totes\n");
+ MoveFridge(0.6, 0.15, false, kFridgeXProfile, kFridgeYProfile);
+
+ WaitForFridge();
+ if (ShouldExitAuto()) return;
+
+ time::SleepFor(time::Time::InSeconds(0.1));
+
+ if (ShouldExitAuto()) return;
+
+ LOG(INFO, "Retracting\n");
+ MoveFridge(0.0, 0.10, false, kFridgeFastXProfile, kFridgeYProfile);
+
+ SetClawState(0.0, 0.0, false, kFastClaw);
+
+ if (ShouldExitAuto()) return;
+
+ WaitUntilDoneOrCanceled(::std::move(drive));
+ if (ShouldExitAuto()) return;
+
+ LOG(INFO, "Backing away to let the stack ago\n");
+ drive = SetDriveGoal(-0.1, kRaceBackupDrive);
+ WaitUntilDoneOrCanceled(::std::move(drive));
+
+ WaitForFridge();
+ if (ShouldExitAuto()) return;
+}
+
+} // namespace autonomous
+} // namespace frc971
diff --git a/y2015/autonomous/auto.h b/y2015/autonomous/auto.h
new file mode 100644
index 0000000..7733715
--- /dev/null
+++ b/y2015/autonomous/auto.h
@@ -0,0 +1,12 @@
+#ifndef Y2015_AUTONOMOUS_AUTO_H_
+#define Y2015_AUTONOMOUS_AUTO_H_
+
+namespace frc971 {
+namespace autonomous {
+
+void HandleAuto();
+
+} // namespace autonomous
+} // namespace frc971
+
+#endif // Y2015_AUTONOMOUS_AUTO_H_
diff --git a/y2015/autonomous/auto_main.cc b/y2015/autonomous/auto_main.cc
new file mode 100644
index 0000000..4a24e6c
--- /dev/null
+++ b/y2015/autonomous/auto_main.cc
@@ -0,0 +1,42 @@
+#include <stdio.h>
+
+#include "aos/common/time.h"
+#include "aos/linux_code/init.h"
+#include "aos/common/logging/logging.h"
+#include "frc971/autonomous/auto.q.h"
+#include "y2015/autonomous/auto.h"
+
+using ::aos::time::Time;
+
+int main(int /*argc*/, char * /*argv*/[]) {
+ ::aos::Init();
+
+ LOG(INFO, "Auto main started\n");
+ ::frc971::autonomous::autonomous.FetchLatest();
+ while (!::frc971::autonomous::autonomous.get()) {
+ ::frc971::autonomous::autonomous.FetchNextBlocking();
+ LOG(INFO, "Got another auto packet\n");
+ }
+
+ while (true) {
+ while (!::frc971::autonomous::autonomous->run_auto) {
+ ::frc971::autonomous::autonomous.FetchNextBlocking();
+ LOG(INFO, "Got another auto packet\n");
+ }
+ LOG(INFO, "Starting auto mode\n");
+ ::aos::time::Time start_time = ::aos::time::Time::Now();
+ ::frc971::autonomous::HandleAuto();
+
+ ::aos::time::Time elapsed_time = ::aos::time::Time::Now() - start_time;
+ LOG(INFO, "Auto mode exited in %f, waiting for it to finish.\n",
+ elapsed_time.ToSeconds());
+ while (::frc971::autonomous::autonomous->run_auto) {
+ ::frc971::autonomous::autonomous.FetchNextBlocking();
+ LOG(INFO, "Got another auto packet\n");
+ }
+ LOG(INFO, "Waiting for auto to start back up.\n");
+ }
+ ::aos::Cleanup();
+ return 0;
+}
+
diff --git a/y2015/autonomous/autonomous.gyp b/y2015/autonomous/autonomous.gyp
new file mode 100644
index 0000000..63898e8
--- /dev/null
+++ b/y2015/autonomous/autonomous.gyp
@@ -0,0 +1,43 @@
+{
+ 'targets': [
+ {
+ 'target_name': 'auto_lib',
+ 'type': 'static_library',
+ 'sources': [
+ 'auto.cc',
+ ],
+ 'dependencies': [
+ '<(DEPTH)/frc971/autonomous/autonomous.gyp:auto_queue',
+ '<(AOS)/common/controls/controls.gyp:control_loop',
+ '<(DEPTH)/y2015/control_loops/drivetrain/drivetrain.gyp:drivetrain_queue',
+ '<(DEPTH)/y2015/y2015.gyp:constants',
+ '<(AOS)/common/common.gyp:time',
+ '<(AOS)/common/util/util.gyp:phased_loop',
+ '<(AOS)/common/util/util.gyp:trapezoid_profile',
+ '<(AOS)/build/aos.gyp:logging',
+ '<(DEPTH)/y2015/actors/actors.gyp:drivetrain_action_lib',
+ '<(AOS)/common/logging/logging.gyp:queue_logging',
+ '<(DEPTH)/y2015/control_loops/claw/claw.gyp:claw_queue',
+ '<(DEPTH)/y2015/control_loops/fridge/fridge.gyp:fridge_queue',
+ '<(DEPTH)/y2015/actors/actors.gyp:stack_action_lib',
+ '<(DEPTH)/y2015/actors/actors.gyp:held_to_lift_action_lib',
+ '<(DEPTH)/y2015/actors/actors.gyp:pickup_action_lib',
+ ],
+ 'export_dependent_settings': [
+ '<(AOS)/common/controls/controls.gyp:control_loop',
+ ],
+ },
+ {
+ 'target_name': 'auto',
+ 'type': 'executable',
+ 'sources': [
+ 'auto_main.cc',
+ ],
+ 'dependencies': [
+ '<(AOS)/linux_code/linux_code.gyp:init',
+ '<(DEPTH)/frc971/autonomous/autonomous.gyp:auto_queue',
+ 'auto_lib',
+ ],
+ },
+ ],
+}
diff --git a/y2015/constants.cc b/y2015/constants.cc
new file mode 100644
index 0000000..50f6672
--- /dev/null
+++ b/y2015/constants.cc
@@ -0,0 +1,381 @@
+#include "y2015/constants.h"
+
+#include <math.h>
+#include <stdint.h>
+#include <inttypes.h>
+
+#include <map>
+
+#if __has_feature(address_sanitizer)
+#include "sanitizer/lsan_interface.h"
+#endif
+
+#include "aos/common/logging/logging.h"
+#include "aos/common/once.h"
+#include "aos/common/network/team_number.h"
+#include "aos/common/mutex.h"
+
+#include "y2015/control_loops/drivetrain/polydrivetrain_dog_motor_plant.h"
+#include "y2015/control_loops/drivetrain/drivetrain_dog_motor_plant.h"
+
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif
+
+namespace frc971 {
+namespace constants {
+namespace {
+
+const uint16_t kCompTeamNumber = 971;
+const uint16_t kPracticeTeamNumber = 9971;
+
+// ///// Drivetrain Constants
+
+// These three constants were set by Daniel on 2/13/15.
+const double kDrivetrainEncoderRatio = 20.0 / 50.0;
+const double kLowGearRatio = kDrivetrainEncoderRatio * 20.0 / 50.0;
+const double kHighGearRatio = kLowGearRatio;
+
+const ShifterHallEffect kCompRightDriveShifter{555, 657, 660, 560, 0.2, 0.7};
+const ShifterHallEffect kCompLeftDriveShifter{555, 660, 644, 552, 0.2, 0.7};
+
+const ShifterHallEffect kPracticeRightDriveShifter{2.95, 3.95, 3.95,
+ 2.95, 0.2, 0.7};
+const ShifterHallEffect kPracticeLeftDriveShifter{2.95, 4.2, 3.95,
+ 3.0, 0.2, 0.7};
+const double kToteHeight = 0.3;
+
+// Set by Daniel on 2/13/15.
+// Distance from the center of the left wheel to the center of the right wheel.
+const double kRobotWidth = 37.806 /*inches*/ * 0.0254;
+
+// ///// Superstructure Constants
+
+// Elevator gearbox pulley output constants.
+const int kElevatorGearboxOutputPulleyTeeth = 32; // 32 teeth
+const double kElevatorGearboxOutputPitch = 0.005; // 5 mm/tooth
+const double kElevatorGearboxOutputRadianDistance =
+ kElevatorGearboxOutputPulleyTeeth * kElevatorGearboxOutputPitch /
+ (2.0 * M_PI);
+
+const double kArmZeroingHeight = 0.2;
+
+const double kMaxAllowedLeftRightArmDifference = 0.12; // radians
+const double kMaxAllowedLeftRightElevatorDifference = 0.04; // meters
+
+const Values::ClawGeometry kClawGeometry{
+ // Horizontal distance from the center of the grabber to the end.
+ 0.5 * 18.0 * 0.0254,
+ // Vertical distance from the arm rotation center to the bottom of
+ // the
+ // grabber. Distance measured with arm vertical (theta = 0).
+ 5.1 * 0.0254,
+ // Vertical separation of the claw and arm rotation centers with the
+ // elevator at 0.0 and the arm angle set to zero.
+ 10.5 * 0.0254,
+ // Horizontal separation of the claw and arm rotation centers with
+ // the
+ // elevator at 0.0 and the arm angle set to zero.
+ 6.5 * 0.0254,
+ // Distance between the center of the claw to the top of the claw.
+ // 2.75 inches would work most of the time. Using 3.5 inches because
+ // of the
+ // pnumatics fitting on the piston.
+ 3.5 * 0.0254,
+ // The grabber is safe at any height if it is behind this location.
+ // The location of the grabber is used here and not the location of
+ // the end
+ // of the grabber. The grabber location is (0, 0) when the elevator
+ // is at 0
+ // and the arm angle is 0.
+ (18.0 - 0.3) * 0.0254,
+ // The grabber is safe at any x if it is above this location.
+ // The location of the grabber is used here and not the location of
+ // the end
+ // of the grabber. The grabber location is (0, 0) when the elevator
+ // is at 0
+ // and the arm angle is 0.
+ // The "-5.4" is the location of the bottom of the grabber when
+ // the elevator is at the bottom (-0.3 inches) and arm angle is 0.
+ -8.0 * 0.0254,
+};
+
+// Gearing ratios of the pots and encoders for the elevator and arm.
+// Ratio is output shaft rotations per encoder/pot rotation
+// Checked by Daniel on 2/13/15.
+const double kArmEncoderRatio = 18.0 / 48.0 * 16.0 / 72.0;
+const double kArmPotRatio = 48.0 / 48.0 * 16.0 / 72.0;
+const double kElevatorEncoderRatio = 14.0 / 84.0;
+const double kElevatorPotRatio = 1.0;
+const double kClawEncoderRatio = 18.0 / 72.0;
+const double kClawPotRatio = 18.0 / 72.0;
+
+// Number of radians between each index pulse on the arm.
+const double kArmEncoderIndexDifference = 2.0 * M_PI * kArmEncoderRatio;
+// Number of meters between each index pulse on the elevator.
+const double kElevatorEncoderIndexDifference =
+ kElevatorEncoderRatio * 2.0 * M_PI * // radians
+ kElevatorGearboxOutputRadianDistance;
+// Number of radians between index pulses on the claw.
+const double kClawEncoderIndexDifference = 2.0 * M_PI * kClawEncoderRatio;
+
+const int kZeroingSampleSize = 200;
+
+// The length of the arm.
+const double kArmLength = 0.7366;
+
+// TODO(danielp): All these values might need to change.
+const double kClawPistonSwitchTime = 0.4;
+const double kClawZeroingRange = 0.3;
+
+const Values *DoGetValuesForTeam(uint16_t team) {
+ switch (team) {
+ case 1: // for tests
+ return new Values{
+ kDrivetrainEncoderRatio,
+ kArmEncoderRatio,
+ kArmPotRatio,
+ kElevatorEncoderRatio,
+ kElevatorPotRatio,
+ kElevatorGearboxOutputRadianDistance,
+ kClawEncoderRatio,
+ kClawPotRatio,
+ kToteHeight,
+ kLowGearRatio,
+ kHighGearRatio,
+ kCompLeftDriveShifter,
+ kCompRightDriveShifter,
+ false,
+ 0.5,
+ control_loops::MakeVelocityDrivetrainLoop,
+ control_loops::MakeDrivetrainLoop,
+ 0.02, // drivetrain done delta
+ 5.0, // drivetrain max speed
+
+ // Motion ranges: hard_lower_limit, hard_upper_limit,
+ // soft_lower_limit, soft_upper_limit
+ {// Claw values, in radians.
+ // 0 is level with the ground.
+ // Positive moves in the direction of positive encoder values.
+ {-0.05, M_PI / 2.0 + 0.1, 0.0, M_PI / 2.0},
+
+ // Zeroing constants for wrist.
+ {kZeroingSampleSize, kClawEncoderIndexDifference, 0.9, 0.3},
+
+ 6.308141,
+ kClawPistonSwitchTime,
+ kClawZeroingRange},
+
+ {// Elevator values, in meters.
+ // 0 is the portion of the elevator carriage that Spencer removed
+ // lining up with the bolt.
+ // Positive is up.
+ {-0.005, 0.689000, 0.010000, 0.680000},
+
+ // Arm values, in radians.
+ // 0 is sticking straight out horizontally over the intake/front.
+ // Positive is rotating up and into the robot (towards the back).
+ {-M_PI / 2 - 0.05, M_PI / 2 + 0.05, -M_PI / 2, M_PI / 2},
+
+ // Elevator zeroing constants: left, right.
+ {kZeroingSampleSize, kElevatorEncoderIndexDifference, 0.0, 0.3},
+ {kZeroingSampleSize, kElevatorEncoderIndexDifference, 0.0, 0.3},
+ // Arm zeroing constants: left, right.
+ {kZeroingSampleSize, kArmEncoderIndexDifference, 0.0, 0.3},
+ {kZeroingSampleSize, kArmEncoderIndexDifference, 0.0, 0.3},
+ 0.7,
+ -0.08,
+ -3.5 - 0.01 - -0.02,
+ 3.5 - 0.17 - -0.15,
+
+ kArmZeroingHeight,
+ kArmLength,
+ },
+ // End "sensor" values.
+
+ kMaxAllowedLeftRightArmDifference,
+ kMaxAllowedLeftRightElevatorDifference,
+ kClawGeometry,
+ };
+ break;
+ case kPracticeTeamNumber:
+ return new Values{
+ kDrivetrainEncoderRatio,
+ kArmEncoderRatio,
+ kArmPotRatio,
+ kElevatorEncoderRatio,
+ kElevatorPotRatio,
+ kElevatorGearboxOutputRadianDistance,
+ kClawEncoderRatio,
+ kClawPotRatio,
+ kToteHeight,
+ kLowGearRatio,
+ kHighGearRatio,
+ kCompLeftDriveShifter,
+ kCompRightDriveShifter,
+ false,
+ kRobotWidth,
+ control_loops::MakeVelocityDrivetrainLoop,
+ control_loops::MakeDrivetrainLoop,
+ 0.02, // drivetrain done delta
+ 5.0, // drivetrain max speed
+
+ // Motion ranges: hard_lower_limit, hard_upper_limit,
+ // soft_lower_limit, soft_upper_limit
+ // TODO(sensors): Get actual bounds before turning on robot.
+ {// Claw values, in radians.
+ // 0 is level with the ground.
+ // Positive moves in the direction of positive encoder values.
+ {-0.05, M_PI / 2.0 + 0.1, 0.0, M_PI / 2.0},
+
+ // Zeroing constants for wrist.
+ {kZeroingSampleSize, kClawEncoderIndexDifference, 0.9104180000000001,
+ 0.3},
+
+ 6.308141,
+ kClawPistonSwitchTime,
+ kClawZeroingRange},
+
+ {// Elevator values, in meters.
+ // 0 is the portion of the elevator carriage that Spencer removed
+ // lining up with the bolt.
+ // Positive is up.
+ {-0.00500, 0.689000, 0.010000, 0.680000},
+
+ // Arm values, in radians.
+ // 0 is sticking straight out horizontally over the intake/front.
+ // Positive is rotating up and into the robot (towards the back).
+ {-M_PI / 2 - 0.05, M_PI / 2 + 0.05, -M_PI / 2, M_PI / 2},
+
+ // Elevator zeroing constants: left, right.
+ {kZeroingSampleSize, kElevatorEncoderIndexDifference, 0.110677, 0.3}, // Was 0.088984 (3 mm too high)
+ {kZeroingSampleSize, kElevatorEncoderIndexDifference, 0.109974, 0.3}, // Was 0.104557 (4 mm too low)
+ // Arm zeroing constants: left, right.
+ {kZeroingSampleSize, kArmEncoderIndexDifference, -0.324437, 0.3},
+ {kZeroingSampleSize, kArmEncoderIndexDifference, -0.064683, 0.3},
+ 0.722230 - -0.000594 - -0.026183 - 0.003442, // Left Elevator Poteniometer adjustment
+ -0.081354 - -0.000374 - -0.024793 - -0.006916, // Right Elevator Poteniometer adjustment
+ -3.509611 - 0.007415 - -0.019081,
+ 3.506927 - 0.170017 - -0.147970,
+
+ kArmZeroingHeight,
+ kArmLength,
+ },
+ // End "sensor" values.
+
+ kMaxAllowedLeftRightArmDifference,
+ kMaxAllowedLeftRightElevatorDifference,
+ kClawGeometry,
+ };
+ break;
+ case kCompTeamNumber:
+ return new Values{
+ kDrivetrainEncoderRatio,
+ kArmEncoderRatio,
+ kArmPotRatio,
+ kElevatorEncoderRatio,
+ kElevatorPotRatio,
+ kElevatorGearboxOutputRadianDistance,
+ kClawEncoderRatio,
+ kClawPotRatio,
+ kToteHeight,
+ kLowGearRatio,
+ kHighGearRatio,
+ kPracticeLeftDriveShifter,
+ kPracticeRightDriveShifter,
+ false,
+ kRobotWidth,
+ control_loops::MakeVelocityDrivetrainLoop,
+ control_loops::MakeDrivetrainLoop,
+ 0.02, // drivetrain done delta
+ 5.0, // drivetrain max speed
+
+ // Motion ranges: hard_lower_limit, hard_upper_limit,
+ // soft_lower_limit, soft_upper_limit
+ // TODO(sensors): Get actual bounds before turning on robot.
+ {// Claw values, in radians.
+ // 0 is level with the ground.
+ // Positive moves in the direction of positive encoder values.
+ {-0.05, M_PI / 2.0 + 0.1, 0.0, M_PI / 2.0},
+
+ // Zeroing constants for wrist.
+ // TODO(sensors): Get actual offsets for these.
+ {kZeroingSampleSize, kClawEncoderIndexDifference, 0.952602, 0.3},
+ 6.1663463999999992 + 0.015241,
+
+ kClawPistonSwitchTime,
+ kClawZeroingRange},
+
+ {// Elevator values, in meters.
+ // 0 is at the top of the elevator frame.
+ // Positive is down towards the drivebase.
+ {-0.00500, 0.689000, 0.010000, 0.680000},
+
+ // Arm values, in radians.
+ // 0 is sticking straight out horizontally over the intake/front.
+ // Positive is rotating up and into the robot (towards the back).
+ {-M_PI / 2 - 0.05, M_PI / 2 + 0.05, -M_PI / 2, M_PI / 2},
+
+ // Elevator zeroing constants: left, right.
+ // These are the encoder offsets.
+ // TODO(sensors): Get actual offsets for these.
+ {kZeroingSampleSize, kElevatorEncoderIndexDifference,
+ 0.240286 - 0.007969 - 0.025, 0.3},
+ {kZeroingSampleSize, kElevatorEncoderIndexDifference,
+ 0.234583 - 0.000143, 0.3},
+ // Arm zeroing constants: left, right.
+ {kZeroingSampleSize, kArmEncoderIndexDifference, 0.060592, 0.3},
+ {kZeroingSampleSize, kArmEncoderIndexDifference, 0.210155, 0.3},
+ // These are the potentiometer offsets.
+ 0.72069366666666679 - 0.026008 - 0.024948 + 0.025,
+ -0.078959636363636357 - 0.024646 - 0.020260,
+ -3.509611 - 0.007415 - -0.019081 - 0.029393 - -0.013585,
+ 3.506927 - 0.170017 - -0.147970 - 0.005045 - -0.026504,
+
+ kArmZeroingHeight,
+ kArmLength,
+ },
+ // TODO(sensors): End "sensor" values.
+
+ kMaxAllowedLeftRightArmDifference,
+ kMaxAllowedLeftRightElevatorDifference,
+ kClawGeometry,
+ };
+ break;
+ default:
+ LOG(FATAL, "unknown team #%" PRIu16 "\n", team);
+ }
+}
+
+const Values *DoGetValues() {
+ uint16_t team = ::aos::network::GetTeamNumber();
+ LOG(INFO, "creating a Constants for team %" PRIu16 "\n", team);
+ return DoGetValuesForTeam(team);
+}
+
+} // namespace
+
+const Values &GetValues() {
+ static ::aos::Once<const Values> once(DoGetValues);
+ return *once.Get();
+}
+
+const Values &GetValuesForTeam(uint16_t team_number) {
+ static ::aos::Mutex mutex;
+ ::aos::MutexLocker locker(&mutex);
+
+ // IMPORTANT: This declaration has to stay after the mutex is locked to avoid
+ // race conditions.
+ static ::std::map<uint16_t, const Values *> values;
+
+ if (values.count(team_number) == 0) {
+ values[team_number] = DoGetValuesForTeam(team_number);
+#if __has_feature(address_sanitizer)
+ __lsan_ignore_object(values[team_number]);
+#endif
+ }
+ return *values[team_number];
+}
+
+} // namespace constants
+} // namespace frc971
diff --git a/y2015/constants.h b/y2015/constants.h
new file mode 100644
index 0000000..a87315c
--- /dev/null
+++ b/y2015/constants.h
@@ -0,0 +1,149 @@
+#ifndef Y2015_CONSTANTS_H_
+#define Y2015_CONSTANTS_H_
+
+#include <stdint.h>
+
+#include "frc971/control_loops/state_feedback_loop.h"
+#include "frc971/shifter_hall_effect.h"
+#include "frc971/constants.h"
+
+namespace frc971 {
+namespace constants {
+
+// Has all of the numbers that change for both robots and makes it easy to
+// retrieve the values for the current one.
+
+// Everything is in SI units (volts, radians, meters, seconds, etc).
+// Some of these values are related to the conversion between raw values
+// (encoder counts, voltage, etc) to scaled units (radians, meters, etc).
+
+// This structure contains current values for all of the things that change.
+struct Values {
+ // Drivetrain Values /////
+
+ // The ratio from the encoder shaft to the drivetrain wheels.
+ double drivetrain_encoder_ratio;
+ // The ratio from the encoder shaft to the arm joint.
+ double arm_encoder_ratio;
+ // The ratio from the pot shaft to the arm joint.
+ double arm_pot_ratio;
+ // The ratio from the encoder shaft to the elevator output pulley.
+ double elev_encoder_ratio;
+ // The ratio from the pot shaft to the elevator output pulley.
+ double elev_pot_ratio;
+ // How far the elevator moves (meters) per radian on the output pulley.
+ double elev_distance_per_radian;
+ // The ratio from the encoder shaft to the claw joint.
+ double claw_encoder_ratio;
+ // The ratio from the pot shaft to the claw joint.
+ double claw_pot_ratio;
+
+ // How tall a tote is in meters.
+ double tote_height;
+
+ // The gear ratios from motor shafts to the drivetrain wheels for high and low
+ // gear.
+ double low_gear_ratio;
+ double high_gear_ratio;
+ ShifterHallEffect left_drive, right_drive;
+ bool clutch_transmission;
+
+ double turn_width;
+
+ ::std::function<StateFeedbackLoop<2, 2, 2>()> make_v_drivetrain_loop;
+ ::std::function<StateFeedbackLoop<4, 2, 2>()> make_drivetrain_loop;
+
+ double drivetrain_done_distance;
+ double drivetrain_max_speed;
+
+ // Superstructure Values /////
+
+ // Defines a range of motion for a subsystem.
+ // These are all absolute positions in scaled units.
+ struct Range {
+ double lower_hard_limit;
+ double upper_hard_limit;
+ double lower_limit;
+ double upper_limit;
+ };
+
+ struct Claw {
+ Range wrist;
+ ZeroingConstants zeroing;
+ // The value to add to potentiometer readings after they have been converted
+ // to radians so that the resulting value is 0 when the claw is at absolute
+ // 0 (horizontal straight out the front).
+ double potentiometer_offset;
+
+ // Time between sending commands to claw opening pistons and them reaching
+ // the new state.
+ double piston_switch_time;
+ // How far on either side we look for the index pulse before we give up.
+ double zeroing_range;
+ };
+ Claw claw;
+
+ struct Fridge {
+ Range elevator;
+ Range arm;
+
+ ZeroingConstants left_elev_zeroing;
+ ZeroingConstants right_elev_zeroing;
+ ZeroingConstants left_arm_zeroing;
+ ZeroingConstants right_arm_zeroing;
+
+ // Values to add to scaled potentiometer readings so 0 lines up with the
+ // physical absolute 0.
+ double left_elevator_potentiometer_offset;
+ double right_elevator_potentiometer_offset;
+ double left_arm_potentiometer_offset;
+ double right_arm_potentiometer_offset;
+
+ // How high the elevator has to be before we start zeroing the arm.
+ double arm_zeroing_height;
+
+ // The length of the arm, from the axis of the bottom pivot to the axis of
+ // the top pivot.
+ double arm_length;
+ };
+ Fridge fridge;
+
+ double max_allowed_left_right_arm_difference;
+ double max_allowed_left_right_elevator_difference;
+
+ struct ClawGeometry {
+ // Horizontal distance from the center of the grabber to the end.
+ double grabber_half_length;
+ // Vertical distance from the arm rotation center to the bottom of the
+ // grabber. Distance measured with arm vertical (theta = 0).
+ double grabber_delta_y;
+ // Vertical separation of the claw and arm rotation centers with the
+ // elevator at 0.0 and the arm angle set to zero.
+ double grabber_arm_vert_separation;
+ // Horizontal separation of the claw and arm rotation centers with the
+ // elevator at 0.0 and the arm angle set to zero.
+ double grabber_arm_horz_separation;
+ // Distance between the center of the claw to the top of the claw.
+ // The line drawn at this distance parallel to the claw centerline is used
+ // to determine if claw interfears with the grabber.
+ double claw_top_thickness;
+ // The grabber is safe at any height if it is behind this location.
+ double grabber_always_safe_h_min;
+ // The grabber is safe at any x if it is above this location.
+ double grabber_always_safe_x_max;
+ };
+ ClawGeometry clawGeometry;
+};
+
+// Creates (once) a Values instance for ::aos::network::GetTeamNumber() and
+// returns a reference to it.
+const Values &GetValues();
+
+// Creates Values instances for each team number it is called with and returns
+// them.
+const Values &GetValuesForTeam(uint16_t team_number);
+
+} // namespace constants
+} // namespace frc971
+
+#endif // Y2015_CONSTANTS_H_
diff --git a/y2015/control_loops/claw/claw.cc b/y2015/control_loops/claw/claw.cc
new file mode 100644
index 0000000..c1a734e
--- /dev/null
+++ b/y2015/control_loops/claw/claw.cc
@@ -0,0 +1,311 @@
+#include "y2015/control_loops/claw/claw.h"
+
+#include <algorithm>
+
+#include "aos/common/controls/control_loops.q.h"
+#include "aos/common/logging/logging.h"
+
+#include "y2015/constants.h"
+#include "y2015/control_loops/claw/claw_motor_plant.h"
+#include "aos/common/util/trapezoid_profile.h"
+
+namespace frc971 {
+namespace control_loops {
+
+using ::aos::time::Time;
+
+constexpr double kZeroingVoltage = 4.0;
+
+void ClawCappedStateFeedbackLoop::CapU() {
+ mutable_U(0, 0) = ::std::min(mutable_U(0, 0), max_voltage_);
+ mutable_U(0, 0) = ::std::max(mutable_U(0, 0), -max_voltage_);
+}
+
+double ClawCappedStateFeedbackLoop::UnsaturateOutputGoalChange() {
+ // Compute K matrix to compensate for position errors.
+ double Kp = K(0, 0);
+
+ // Compute how much we need to change R in order to achieve the change in U
+ // that was observed.
+ return -(1.0 / Kp) * (U_uncapped() - U())(0, 0);
+}
+
+Claw::Claw(control_loops::ClawQueue *claw)
+ : aos::controls::ControlLoop<control_loops::ClawQueue>(claw),
+ last_piston_edge_(Time::Now()),
+ claw_loop_(new ClawCappedStateFeedbackLoop(MakeClawLoop())),
+ claw_estimator_(constants::GetValues().claw.zeroing),
+ profile_(::aos::controls::kLoopFrequency) {}
+
+void Claw::UpdateZeroingState() {
+ if (claw_estimator_.offset_ratio_ready() < 1.0) {
+ state_ = INITIALIZING;
+ } else if (!claw_estimator_.zeroed()) {
+ state_ = ZEROING;
+ } else {
+ state_ = RUNNING;
+ }
+}
+
+void Claw::Correct() {
+ Eigen::Matrix<double, 1, 1> Y;
+ Y << claw_position();
+ claw_loop_->Correct(Y);
+}
+
+void Claw::SetClawOffset(double offset) {
+ LOG(INFO, "Changing claw offset from %f to %f.\n", claw_offset_, offset);
+ const double doffset = offset - claw_offset_;
+
+ // Adjust the height. The derivative should not need to be updated since the
+ // speed is not changing.
+ claw_loop_->mutable_X_hat(0, 0) += doffset;
+
+ // Modify claw zeroing goal.
+ claw_goal_ += doffset;
+ // Update the cached offset value to the actual value.
+ claw_offset_ = offset;
+}
+
+double Claw::estimated_claw_position() const {
+ return current_position_.joint.encoder + claw_estimator_.offset();
+}
+
+double Claw::claw_position() const {
+ return current_position_.joint.encoder + claw_offset_;
+}
+
+constexpr double kClawZeroingVelocity = 0.2;
+
+double Claw::claw_zeroing_velocity() {
+ const auto &values = constants::GetValues();
+
+ // Zeroing will work as following. At startup, record the offset of the claw.
+ // Then, start moving the claw towards where the index pulse should be. We
+ // search around it a little, and if we don't find anything, we estop.
+ // Otherwise, we're done.
+
+ const double target_pos = values.claw.zeroing.measured_index_position;
+ // How far away we need to stay from the ends of the range while zeroing.
+ constexpr double zeroing_limit = 0.1375;
+ // Keep the zeroing range within the bounds of the mechanism.
+ const double zeroing_range =
+ ::std::min(target_pos - values.claw.wrist.lower_limit - zeroing_limit,
+ values.claw.zeroing_range);
+
+ if (claw_zeroing_velocity_ == 0) {
+ if (estimated_claw_position() > target_pos) {
+ claw_zeroing_velocity_ = -kClawZeroingVelocity;
+ } else {
+ claw_zeroing_velocity_ = kClawZeroingVelocity;
+ }
+ } else if (claw_zeroing_velocity_ > 0 &&
+ estimated_claw_position() > target_pos + zeroing_range) {
+ claw_zeroing_velocity_ = -kClawZeroingVelocity;
+ } else if (claw_zeroing_velocity_ < 0 &&
+ estimated_claw_position() < target_pos - zeroing_range) {
+ claw_zeroing_velocity_ = kClawZeroingVelocity;
+ }
+
+ return claw_zeroing_velocity_;
+}
+
+void Claw::RunIteration(const control_loops::ClawQueue::Goal *unsafe_goal,
+ const control_loops::ClawQueue::Position *position,
+ control_loops::ClawQueue::Output *output,
+ control_loops::ClawQueue::Status *status) {
+ const auto &values = constants::GetValues();
+
+ if (WasReset()) {
+ LOG(ERROR, "WPILib reset! Restarting.\n");
+ claw_estimator_.Reset();
+ state_ = UNINITIALIZED;
+ }
+
+ current_position_ = *position;
+
+ // Bool to track if we should turn the motor on or not.
+ bool disable = output == nullptr;
+ double claw_goal_velocity = 0.0;
+
+ claw_estimator_.UpdateEstimate(position->joint);
+
+ if (state_ != UNINITIALIZED) {
+ Correct();
+ }
+
+ switch (state_) {
+ case UNINITIALIZED:
+ LOG(INFO, "Uninitialized.\n");
+ // Startup. Assume that we are at the origin.
+ claw_offset_ = -position->joint.encoder;
+ claw_loop_->mutable_X_hat().setZero();
+ Correct();
+ state_ = INITIALIZING;
+ disable = true;
+ break;
+
+ case INITIALIZING:
+ LOG(INFO, "Waiting for accurate initial position.\n");
+ disable = true;
+ // Update state_ to accurately represent the state of the zeroing
+ // estimator.
+ UpdateZeroingState();
+
+ if (state_ != INITIALIZING) {
+ // Set the goals to where we are now.
+ claw_goal_ = claw_position();
+ }
+ break;
+
+ case ZEROING:
+ LOG(DEBUG, "Zeroing.\n");
+
+ // Update state_.
+ UpdateZeroingState();
+ if (claw_estimator_.zeroed()) {
+ LOG(INFO, "Zeroed!\n");
+ SetClawOffset(claw_estimator_.offset());
+ } else if (!disable) {
+ claw_goal_velocity = claw_zeroing_velocity();
+ claw_goal_ +=
+ claw_goal_velocity * ::aos::controls::kLoopFrequency.ToSeconds();
+ }
+
+ // Clear the current profile state if we are zeroing.
+ {
+ Eigen::Matrix<double, 2, 1> current;
+ current.setZero();
+ current << claw_goal_, claw_goal_velocity;
+ profile_.MoveCurrentState(current);
+ }
+ break;
+
+ case RUNNING:
+ LOG(DEBUG, "Running!\n");
+
+ // Update state_.
+ UpdateZeroingState();
+ if (unsafe_goal) {
+ // Pick a set of sane defaults if none are specified.
+ if (unsafe_goal->max_velocity != 0.0) {
+ profile_.set_maximum_velocity(unsafe_goal->max_velocity);
+ } else {
+ profile_.set_maximum_velocity(2.5);
+ }
+ if (unsafe_goal->max_acceleration != 0.0) {
+ profile_.set_maximum_acceleration(unsafe_goal->max_acceleration);
+ } else {
+ profile_.set_maximum_acceleration(4.0);
+ }
+
+ const double unfiltered_goal = ::std::max(
+ ::std::min(unsafe_goal->angle, values.claw.wrist.upper_limit),
+ values.claw.wrist.lower_limit);
+ ::Eigen::Matrix<double, 2, 1> goal_state =
+ profile_.Update(unfiltered_goal, unsafe_goal->angular_velocity);
+ claw_goal_ = goal_state(0, 0);
+ claw_goal_velocity = goal_state(1, 0);
+ }
+
+ if (state_ != RUNNING && state_ != ESTOP) {
+ state_ = UNINITIALIZED;
+ }
+ break;
+
+ case ESTOP:
+ LOG(ERROR, "Estop!\n");
+ disable = true;
+ break;
+ }
+
+ // Make sure goal and position do not exceed the hardware limits if we are
+ // RUNNING.
+ if (state_ == RUNNING) {
+ // Limit goal.
+ claw_goal_ = ::std::min(claw_goal_, values.claw.wrist.upper_limit);
+ claw_goal_ = ::std::max(claw_goal_, values.claw.wrist.lower_limit);
+
+ // Check position.
+ if (claw_position() >= values.claw.wrist.upper_hard_limit ||
+ claw_position() <= values.claw.wrist.lower_hard_limit) {
+ LOG(ERROR, "Claw at %f out of bounds [%f, %f].\n", claw_position(),
+ values.claw.wrist.lower_limit, values.claw.wrist.upper_limit);
+ }
+ }
+
+ // Set the goals.
+ claw_loop_->mutable_R() << claw_goal_, claw_goal_velocity;
+
+ const double max_voltage = (state_ == RUNNING) ? 12.0 : kZeroingVoltage;
+ claw_loop_->set_max_voltage(max_voltage);
+
+ if (state_ == ESTOP) {
+ disable = true;
+ }
+ claw_loop_->Update(disable);
+
+ if (state_ == INITIALIZING || state_ == ZEROING) {
+ if (claw_loop_->U() != claw_loop_->U_uncapped()) {
+ double deltaR = claw_loop_->UnsaturateOutputGoalChange();
+
+ // Move the claw goal by the amount observed.
+ LOG(WARNING, "Moving claw goal by %f to handle saturation.\n", deltaR);
+ claw_goal_ += deltaR;
+ }
+ }
+
+ if (output) {
+ output->voltage = claw_loop_->U(0, 0);
+ if (state_ != RUNNING) {
+ output->intake_voltage = 0.0;
+ output->rollers_closed = false;
+ } else {
+ if (unsafe_goal) {
+ output->intake_voltage = unsafe_goal->intake;
+ output->rollers_closed = unsafe_goal->rollers_closed;
+ } else {
+ output->intake_voltage = 0.0;
+ output->rollers_closed = false;
+ }
+ }
+ if (output->rollers_closed != last_rollers_closed_) {
+ last_piston_edge_ = Time::Now();
+ }
+ }
+
+ status->zeroed = state_ == RUNNING;
+ status->estopped = state_ == ESTOP;
+ status->state = state_;
+ zeroing::PopulateEstimatorState(claw_estimator_, &status->zeroing_state);
+
+ status->angle = claw_loop_->X_hat(0, 0);
+ status->angular_velocity = claw_loop_->X_hat(1, 0);
+
+ if (output) {
+ status->intake = output->intake_voltage;
+ } else {
+ status->intake = 0;
+ }
+ status->goal_angle = claw_goal_;
+ status->goal_velocity = claw_goal_velocity;
+
+ if (output) {
+ status->rollers_open =
+ !output->rollers_closed &&
+ (Time::Now() - last_piston_edge_ >= values.claw.piston_switch_time);
+ status->rollers_closed =
+ output->rollers_closed &&
+ (Time::Now() - last_piston_edge_ >= values.claw.piston_switch_time);
+ } else {
+ status->rollers_open = false;
+ status->rollers_closed = false;
+ }
+
+ if (output) {
+ last_rollers_closed_ = output->rollers_closed;
+ }
+}
+
+} // namespace control_loops
+} // namespace frc971
diff --git a/y2015/control_loops/claw/claw.gyp b/y2015/control_loops/claw/claw.gyp
new file mode 100644
index 0000000..780f065
--- /dev/null
+++ b/y2015/control_loops/claw/claw.gyp
@@ -0,0 +1,89 @@
+{
+ 'targets': [
+ {
+ 'target_name': 'replay_claw',
+ 'type': 'executable',
+ 'variables': {
+ 'no_rsync': 1,
+ },
+ 'sources': [
+ 'replay_claw.cc',
+ ],
+ 'dependencies': [
+ 'claw_queue',
+ '<(AOS)/common/controls/controls.gyp:replay_control_loop',
+ '<(AOS)/linux_code/linux_code.gyp:init',
+ ],
+ },
+ {
+ 'target_name': 'claw_queue',
+ 'type': 'static_library',
+ 'sources': ['claw.q'],
+ 'variables': {
+ 'header_path': 'y2015/control_loops/claw',
+ },
+ 'dependencies': [
+ '<(AOS)/common/controls/controls.gyp:control_loop_queues',
+ '<(DEPTH)/frc971/control_loops/control_loops.gyp:queues',
+ ],
+ 'export_dependent_settings': [
+ '<(AOS)/common/controls/controls.gyp:control_loop_queues',
+ '<(DEPTH)/frc971/control_loops/control_loops.gyp:queues',
+
+ ],
+ 'includes': ['../../../aos/build/queues.gypi'],
+ },
+ {
+ 'target_name': 'claw_lib',
+ 'type': 'static_library',
+ 'sources': [
+ 'claw.cc',
+ 'claw_motor_plant.cc',
+ ],
+ 'dependencies': [
+ 'claw_queue',
+ '<(AOS)/common/controls/controls.gyp:control_loop',
+ '<(AOS)/common/common.gyp:time',
+ '<(AOS)/common/util/util.gyp:trapezoid_profile',
+ '<(DEPTH)/y2015/y2015.gyp:constants',
+ '<(DEPTH)/frc971/control_loops/control_loops.gyp:state_feedback_loop',
+ '<(DEPTH)/frc971/zeroing/zeroing.gyp:zeroing',
+ ],
+ 'export_dependent_settings': [
+ 'claw_queue',
+ '<(AOS)/common/controls/controls.gyp:control_loop',
+ '<(AOS)/common/common.gyp:time',
+ '<(AOS)/common/util/util.gyp:trapezoid_profile',
+ '<(DEPTH)/frc971/control_loops/control_loops.gyp:state_feedback_loop',
+ '<(DEPTH)/frc971/zeroing/zeroing.gyp:zeroing',
+ ],
+ },
+ {
+ 'target_name': 'claw_lib_test',
+ 'type': 'executable',
+ 'sources': [
+ 'claw_lib_test.cc',
+ ],
+ 'dependencies': [
+ '<(EXTERNALS):gtest',
+ 'claw_lib',
+ '<(DEPTH)/frc971/control_loops/control_loops.gyp:state_feedback_loop',
+ '<(AOS)/common/controls/controls.gyp:control_loop_test',
+ '<(AOS)/common/common.gyp:time',
+ '<(DEPTH)/frc971/control_loops/control_loops.gyp:position_sensor_sim',
+ '<(DEPTH)/frc971/control_loops/control_loops.gyp:team_number_test_environment',
+ ],
+ },
+ {
+ 'target_name': 'claw',
+ 'type': 'executable',
+ 'sources': [
+ 'claw_main.cc',
+ ],
+ 'dependencies': [
+ '<(AOS)/linux_code/linux_code.gyp:init',
+ 'claw_lib',
+ ],
+ },
+ ],
+}
diff --git a/y2015/control_loops/claw/claw.h b/y2015/control_loops/claw/claw.h
new file mode 100644
index 0000000..02e5398
--- /dev/null
+++ b/y2015/control_loops/claw/claw.h
@@ -0,0 +1,116 @@
+#ifndef Y2015_CONTROL_LOOPS_CLAW_H_
+#define Y2015_CONTROL_LOOPS_CLAW_H_
+
+#include <memory>
+
+#include "aos/common/controls/control_loop.h"
+#include "aos/common/time.h"
+#include "aos/common/util/trapezoid_profile.h"
+#include "frc971/control_loops/state_feedback_loop.h"
+#include "y2015/control_loops/claw/claw.q.h"
+#include "y2015/control_loops/claw/claw_motor_plant.h"
+#include "frc971/zeroing/zeroing.h"
+
+namespace frc971 {
+namespace control_loops {
+namespace testing {
+class ClawTest_DisabledGoal_Test;
+class ClawTest_GoalPositiveWindup_Test;
+class ClawTest_GoalNegativeWindup_Test;
+} // namespace testing
+
+class ClawCappedStateFeedbackLoop : public StateFeedbackLoop<2, 1, 1> {
+ public:
+ ClawCappedStateFeedbackLoop(StateFeedbackLoop<2, 1, 1> &&loop)
+ : StateFeedbackLoop<2, 1, 1>(::std::move(loop)), max_voltage_(12.0) {}
+
+ void set_max_voltage(double max_voltage) {
+ max_voltage_ = ::std::max(0.0, ::std::min(12.0, max_voltage));
+ }
+
+ void CapU() override;
+
+ // Returns the amount to change the position goal in order to no longer
+ // saturate the controller.
+ double UnsaturateOutputGoalChange();
+
+ private:
+ double max_voltage_;
+};
+
+class Claw : public aos::controls::ControlLoop<control_loops::ClawQueue> {
+ public:
+ enum State {
+ // Waiting to receive data before doing anything.
+ UNINITIALIZED = 0,
+ // Estimating the starting location.
+ INITIALIZING = 1,
+ // Moving to find an index pulse.
+ ZEROING = 2,
+ // Normal operation.
+ RUNNING = 3,
+ // Internal error caused the claw to abort.
+ ESTOP = 4,
+ };
+
+ int state() { return state_; }
+
+ explicit Claw(
+ control_loops::ClawQueue *claw_queue = &control_loops::claw_queue);
+
+ protected:
+ virtual void RunIteration(const control_loops::ClawQueue::Goal *goal,
+ const control_loops::ClawQueue::Position *position,
+ control_loops::ClawQueue::Output *output,
+ control_loops::ClawQueue::Status *status);
+
+ private:
+ friend class testing::ClawTest_DisabledGoal_Test;
+ friend class testing::ClawTest_GoalPositiveWindup_Test;
+ friend class testing::ClawTest_GoalNegativeWindup_Test;
+
+ // Sets state_ to the correct state given the current state of the zeroing
+ // estimator.
+ void UpdateZeroingState();
+ void SetClawOffset(double offset);
+ // Corrects the Observer with the current position.
+ void Correct();
+
+ // Getter for the current claw position.
+ double claw_position() const;
+ // Our best guess at the current position of the claw.
+ double estimated_claw_position() const;
+ // Returns the current zeroing velocity for the claw. If the subsystem is too
+ // far away from the center, it will switch directions.
+ double claw_zeroing_velocity();
+
+ State state_ = UNINITIALIZED;
+
+ // The time when we last changed the claw piston state.
+ ::aos::time::Time last_piston_edge_;
+
+ // The state feedback control loop to talk to.
+ ::std::unique_ptr<ClawCappedStateFeedbackLoop> claw_loop_;
+
+ // Latest position from queue.
+ control_loops::ClawQueue::Position current_position_;
+ // Zeroing estimator for claw.
+ zeroing::ZeroingEstimator claw_estimator_;
+
+ // The goal for the claw.
+ double claw_goal_ = 0.0;
+ // Current velocity to move at while zeroing.
+ double claw_zeroing_velocity_ = 0.0;
+ // Offsets from the encoder position to the absolute position.
+ double claw_offset_ = 0.0;
+
+ // Whether claw was closed last cycle.
+ bool last_rollers_closed_ = false;
+
+ ::aos::util::TrapezoidProfile profile_;
+};
+
+} // namespace control_loops
+} // namespace frc971
+
+#endif // Y2015_CONTROL_LOOPS_CLAW_H_
diff --git a/y2015/control_loops/claw/claw.q b/y2015/control_loops/claw/claw.q
new file mode 100644
index 0000000..8da1c20
--- /dev/null
+++ b/y2015/control_loops/claw/claw.q
@@ -0,0 +1,81 @@
+package frc971.control_loops;
+
+import "aos/common/controls/control_loops.q";
+import "frc971/control_loops/control_loops.q";
+
+queue_group ClawQueue {
+ implements aos.control_loops.ControlLoop;
+
+ // All angles are in radians with 0 sticking straight out the front. Rotating
+ // up and into the robot is positive. Positive output voltage moves in the
+ // direction of positive encoder values.
+
+ message Goal {
+ // Angle of wrist joint.
+ double angle;
+ // Angular velocity of wrist.
+ float angular_velocity;
+ // Maximum profile velocity, or 0 for the default.
+ float max_velocity;
+ // Maximum profile acceleration, or 0 for the default.
+ float max_acceleration;
+ // Voltage of intake rollers. Positive means sucking in, negative means
+ // spitting out.
+ double intake;
+
+ // true to signal the rollers to close.
+ bool rollers_closed;
+ };
+
+ message Position {
+ PotAndIndexPosition joint;
+ };
+
+ message Output {
+ // Voltage for intake motors. Positive is sucking in, negative is spitting
+ // out.
+ double intake_voltage;
+ // Voltage for claw motor.
+ double voltage;
+
+ // true to signal the rollers to close.
+ bool rollers_closed;
+ };
+
+ message Status {
+ // Is claw zeroed?
+ bool zeroed;
+ // Did the claw estop?
+ bool estopped;
+ // The internal state of the claw state machine.
+ uint32_t state;
+ EstimatorState zeroing_state;
+
+ // Estimated angle of wrist joint.
+ double angle;
+ // Estimated angular velocity of wrist.
+ float angular_velocity;
+
+ // Goal angle of wrist joint.
+ double goal_angle;
+ float goal_velocity;
+ // Voltage of intake rollers. Positive means sucking in, negative means
+ // spitting out.
+ double intake;
+
+ // True iff there has been enough time since we actuated the rollers outward
+ // that they should be there.
+ bool rollers_open;
+ // True iff there has been enough time since we actuated the rollers closed
+ // that they should be there.
+ bool rollers_closed;
+ };
+
+ queue Goal goal;
+ queue Position position;
+ queue Output output;
+ queue Status status;
+};
+
+queue_group ClawQueue claw_queue;
+
diff --git a/y2015/control_loops/claw/claw_lib_test.cc b/y2015/control_loops/claw/claw_lib_test.cc
new file mode 100644
index 0000000..10fcc07
--- /dev/null
+++ b/y2015/control_loops/claw/claw_lib_test.cc
@@ -0,0 +1,347 @@
+#include <math.h>
+#include <unistd.h>
+
+#include <memory>
+
+#include "gtest/gtest.h"
+#include "aos/common/queue.h"
+#include "aos/common/time.h"
+#include "aos/common/controls/control_loop_test.h"
+#include "y2015/control_loops/claw/claw.q.h"
+#include "y2015/control_loops/claw/claw.h"
+#include "frc971/control_loops/position_sensor_sim.h"
+#include "y2015/constants.h"
+#include "frc971/control_loops/team_number_test_environment.h"
+
+using ::aos::time::Time;
+
+namespace frc971 {
+namespace control_loops {
+namespace testing {
+
+// Class which simulates the claw and sends out queue messages with the
+// position.
+class ClawSimulation {
+ public:
+ // Constructs a claw simulation.
+ ClawSimulation()
+ : claw_plant_(new StateFeedbackPlant<2, 1, 1>(MakeClawPlant())),
+ pot_and_encoder_(constants::GetValues().claw.zeroing.index_difference),
+ claw_queue_(".frc971.control_loops.claw_queue", 0x9d7452fb,
+ ".frc971.control_loops.claw_queue.goal",
+ ".frc971.control_loops.claw_queue.position",
+ ".frc971.control_loops.claw_queue.output",
+ ".frc971.control_loops.claw_queue.status") {
+ InitializePosition(constants::GetValues().claw.wrist.lower_limit);
+ }
+
+ void InitializePosition(double start_pos) {
+ InitializePosition(start_pos,
+ constants::GetValues().claw.zeroing.measured_index_position);
+ }
+
+ void InitializePosition(double start_pos, double index_pos) {
+ InitializePosition(start_pos,
+ // This gives us a standard deviation of ~9 degrees on the pot noise.
+ constants::GetValues().claw.zeroing.index_difference / 10.0,
+ index_pos);
+ }
+
+ // Do specific initialization for the sensors.
+ void InitializePosition(double start_pos,
+ double pot_noise_stddev,
+ double index_pos) {
+ // Update internal state.
+ claw_plant_->mutable_X(0, 0) = start_pos;
+ // Zero velocity.
+ claw_plant_->mutable_X(1, 0) = 0.0;
+
+ pot_and_encoder_.Initialize(start_pos, pot_noise_stddev, index_pos);
+ }
+
+ // Sends a queue message with the position.
+ void SendPositionMessage() {
+ ::aos::ScopedMessagePtr<control_loops::ClawQueue::Position> position =
+ claw_queue_.position.MakeMessage();
+ pot_and_encoder_.GetSensorValues(&position->joint);
+ position.Send();
+ }
+
+ // Simulates for a single timestep.
+ void Simulate() {
+ EXPECT_TRUE(claw_queue_.output.FetchLatest());
+
+ // Feed voltages into physics simulation.
+ claw_plant_->mutable_U() << claw_queue_.output->voltage;
+ claw_plant_->Update();
+
+ const double wrist_angle = claw_plant_->Y(0, 0);
+
+ // TODO(danielp): Sanity checks.
+
+ pot_and_encoder_.MoveTo(wrist_angle);
+ }
+
+ private:
+ ::std::unique_ptr<StateFeedbackPlant<2, 1, 1>> claw_plant_;
+ PositionSensorSimulator pot_and_encoder_;
+
+ ClawQueue claw_queue_;
+};
+
+class ClawTest : public ::aos::testing::ControlLoopTest {
+ protected:
+ ClawTest()
+ : claw_queue_(".frc971.control_loops.claw_queue", 0x9d7452fb,
+ ".frc971.control_loops.claw_queue.goal",
+ ".frc971.control_loops.claw_queue.position",
+ ".frc971.control_loops.claw_queue.output",
+ ".frc971.control_loops.claw_queue.status"),
+ claw_(&claw_queue_),
+ claw_plant_() {
+ set_team_id(kTeamNumber);
+ }
+
+ void VerifyNearGoal() {
+ claw_queue_.goal.FetchLatest();
+ claw_queue_.status.FetchLatest();
+ EXPECT_NEAR(claw_queue_.goal->angle,
+ claw_queue_.status->angle,
+ 10.0);
+
+ EXPECT_TRUE(claw_queue_.status->zeroed);
+ EXPECT_FALSE(claw_queue_.status->estopped);
+ }
+
+ // Runs one iteration of the whole simulation.
+ void RunIteration(bool enabled = true) {
+ SendMessages(enabled);
+
+ claw_plant_.SendPositionMessage();
+ claw_.Iterate();
+ claw_plant_.Simulate();
+
+ TickTime();
+ }
+
+ // Runs iterations until the specified amount of simulated time has elapsed.
+ void RunForTime(const Time &run_for, bool enabled = true) {
+ const auto start_time = Time::Now();
+ while (Time::Now() < start_time + run_for) {
+ RunIteration(enabled);
+ }
+ }
+
+ // Create a new instance of the test queue so that it invalidates the queue
+ // that it points to. Otherwise, we will have a pointed to
+ // shared memory that is no longer valid.
+ ClawQueue claw_queue_;
+
+ // Create a control loop and simulation.
+ Claw claw_;
+ ClawSimulation claw_plant_;
+};
+
+// Tests that the loop does nothing when the goal is our lower limit.
+TEST_F(ClawTest, DoesNothing) {
+ const auto &values = constants::GetValues();
+ ASSERT_TRUE(claw_queue_.goal.MakeWithBuilder()
+ .angle(values.claw.wrist.lower_limit)
+ .Send());
+
+ RunForTime(Time::InSeconds(6));
+
+ // We should not have moved.
+ VerifyNearGoal();
+}
+
+// NOTE: Claw zeroing is a little annoying because we only hit one index pulse
+// in our entire range of motion.
+
+// Tests that the loop zeroing works with normal values.
+TEST_F(ClawTest, Zeroes) {
+ // It should zero itself if we run it for awhile.
+ ASSERT_TRUE(claw_queue_.goal.MakeWithBuilder()
+ .angle(M_PI / 4.0)
+ .Send());
+
+ RunForTime(Time::InSeconds(6));
+
+ ASSERT_TRUE(claw_queue_.status.FetchLatest());
+ EXPECT_TRUE(claw_queue_.status->zeroed);
+ EXPECT_EQ(Claw::RUNNING, claw_queue_.status->state);
+}
+
+// Tests that claw zeroing fails if the index pulse occurs too close to the end
+// of the range.
+TEST_F(ClawTest, BadIndexPosition) {
+ const auto values = constants::GetValues();
+ claw_plant_.InitializePosition(values.claw.wrist.lower_limit,
+ values.claw.wrist.upper_limit);
+
+ ASSERT_TRUE(claw_queue_.goal.MakeWithBuilder()
+ .angle(M_PI / 4.0)
+ .Send());
+
+ // The zeroing is going to take its sweet time on this one, so we had better
+ // run it for longer.
+ RunForTime(Time::InMS(12000));
+
+ ASSERT_TRUE(claw_queue_.status.FetchLatest());
+ EXPECT_FALSE(claw_queue_.status->zeroed);
+ EXPECT_FALSE(claw_queue_.status->estopped);
+}
+
+// Tests that we can reach a reasonable goal.
+TEST_F(ClawTest, ReachesGoal) {
+ ASSERT_TRUE(claw_queue_.goal.MakeWithBuilder()
+ .angle(M_PI / 4.0)
+ .Send());
+
+ RunForTime(Time::InSeconds(6));
+
+ VerifyNearGoal();
+}
+
+// Tests that it doesn't try to move past the physical range of the mechanism.
+TEST_F(ClawTest, RespectsRange) {
+ const auto &values = constants::GetValues();
+ // Upper limit
+ ASSERT_TRUE(claw_queue_.goal.MakeWithBuilder()
+ .angle(values.claw.wrist.upper_hard_limit + 5.0)
+ .Send());
+
+ RunForTime(Time::InSeconds(7));
+
+ claw_queue_.status.FetchLatest();
+ EXPECT_NEAR(values.claw.wrist.upper_limit,
+ claw_queue_.status->angle,
+ 2.0 * M_PI / 180.0);
+
+ // Lower limit.
+ ASSERT_TRUE(claw_queue_.goal.MakeWithBuilder()
+ .angle(values.claw.wrist.lower_hard_limit - 5.0)
+ .Send());
+
+ RunForTime(Time::InMS(6000));
+
+ claw_queue_.status.FetchLatest();
+ EXPECT_NEAR(values.claw.wrist.lower_limit,
+ claw_queue_.status->angle,
+ 2.0 * M_PI / 180.0);
+}
+
+// Tests that starting at the lower hardstop doesn't cause an abort.
+TEST_F(ClawTest, LowerHardstopStartup) {
+ claw_plant_.InitializePosition(
+ constants::GetValues().claw.wrist.lower_hard_limit);
+ ASSERT_TRUE(claw_queue_.goal.MakeWithBuilder()
+ .angle(M_PI / 4.0)
+ .Send());
+ RunForTime(Time::InSeconds(6));
+
+ VerifyNearGoal();
+}
+
+// Tests that starting at the upper hardstop doesn't cause an abort.
+TEST_F(ClawTest, UpperHardstopStartup) {
+ claw_plant_.InitializePosition(
+ constants::GetValues().claw.wrist.upper_hard_limit);
+ ASSERT_TRUE(claw_queue_.goal.MakeWithBuilder()
+ .angle(M_PI / 4.0)
+ .Send());
+ // Zeroing will take a long time here.
+ RunForTime(Time::InSeconds(15));
+
+ VerifyNearGoal();
+}
+
+
+// Tests that not having a goal doesn't break anything.
+TEST_F(ClawTest, NoGoal) {
+ RunForTime(Time::InMS(50));
+}
+
+// Tests that a WPILib reset results in rezeroing.
+TEST_F(ClawTest, WpilibReset) {
+ ASSERT_TRUE(claw_queue_.goal.MakeWithBuilder()
+ .angle(M_PI / 4.0)
+ .Send());
+
+ RunForTime(Time::InSeconds(6));
+ VerifyNearGoal();
+
+ SimulateSensorReset();
+ RunForTime(Time::InMS(100));
+ ASSERT_TRUE(claw_queue_.status.FetchLatest());
+ EXPECT_NE(Claw::RUNNING, claw_queue_.status->state);
+
+ // Once again, it's going to take us awhile to rezero since we moved away from
+ // our index pulse.
+ RunForTime(Time::InSeconds(6));
+ VerifyNearGoal();
+}
+
+// Tests that internal goals don't change while disabled.
+TEST_F(ClawTest, DisabledGoal) {
+ ASSERT_TRUE(claw_queue_.goal.MakeWithBuilder()
+ .angle(M_PI / 4.0)
+ .Send());
+
+ RunForTime(Time::InMS(100), false);
+ EXPECT_EQ(0.0, claw_.claw_goal_);
+
+ // Now make sure they move correctly.
+ RunForTime(Time::InMS(1000), true);
+ EXPECT_NE(0.0, claw_.claw_goal_);
+}
+
+// Tests that the claw zeroing goals don't wind up too far.
+TEST_F(ClawTest, GoalPositiveWindup) {
+ ASSERT_TRUE(claw_queue_.goal.MakeWithBuilder()
+ .angle(M_PI / 4.0)
+ .Send());
+
+ while (claw_.state() != Claw::ZEROING) {
+ RunIteration();
+ }
+
+ // Kick it.
+ RunForTime(Time::InMS(50));
+ const double orig_claw_goal = claw_.claw_goal_;
+ claw_.claw_goal_ += 100.0;
+
+ RunIteration();
+ EXPECT_NEAR(orig_claw_goal, claw_.claw_goal_, 0.10);
+
+ RunIteration();
+
+ EXPECT_EQ(claw_.claw_loop_->U(), claw_.claw_loop_->U_uncapped());
+}
+
+// Tests that the claw zeroing goals don't wind up too far.
+TEST_F(ClawTest, GoalNegativeWindup) {
+ ASSERT_TRUE(claw_queue_.goal.MakeWithBuilder()
+ .angle(M_PI / 4.0)
+ .Send());
+
+ while (claw_.state() != Claw::ZEROING) {
+ RunIteration();
+ }
+
+ // Kick it.
+ RunForTime(Time::InMS(50));
+ double orig_claw_goal = claw_.claw_goal_;
+ claw_.claw_goal_ -= 100.0;
+
+ RunIteration();
+ EXPECT_NEAR(orig_claw_goal, claw_.claw_goal_, 0.10);
+
+ RunIteration();
+
+ EXPECT_EQ(claw_.claw_loop_->U(), claw_.claw_loop_->U_uncapped());
+}
+
+} // namespace testing
+} // namespace control_loops
+} // namespace frc971
diff --git a/y2015/control_loops/claw/claw_main.cc b/y2015/control_loops/claw/claw_main.cc
new file mode 100644
index 0000000..d1b869c
--- /dev/null
+++ b/y2015/control_loops/claw/claw_main.cc
@@ -0,0 +1,11 @@
+#include "y2015/control_loops/claw/claw.h"
+
+#include "aos/linux_code/init.h"
+
+int main() {
+ ::aos::Init();
+ frc971::control_loops::Claw claw;
+ claw.Run();
+ ::aos::Cleanup();
+ return 0;
+}
diff --git a/y2015/control_loops/claw/claw_motor_plant.cc b/y2015/control_loops/claw/claw_motor_plant.cc
new file mode 100644
index 0000000..5f5c206
--- /dev/null
+++ b/y2015/control_loops/claw/claw_motor_plant.cc
@@ -0,0 +1,49 @@
+#include "y2015/control_loops/claw/claw_motor_plant.h"
+
+#include <vector>
+
+#include "frc971/control_loops/state_feedback_loop.h"
+
+namespace frc971 {
+namespace control_loops {
+
+StateFeedbackPlantCoefficients<2, 1, 1> MakeClawPlantCoefficients() {
+ Eigen::Matrix<double, 2, 2> A;
+ A << 1.0, 0.00482455476758, 0.0, 0.930652495326;
+ Eigen::Matrix<double, 2, 1> B;
+ B << 6.97110924671e-05, 0.0275544125308;
+ Eigen::Matrix<double, 1, 2> C;
+ C << 1, 0;
+ Eigen::Matrix<double, 1, 1> D;
+ D << 0;
+ Eigen::Matrix<double, 1, 1> U_max;
+ U_max << 12.0;
+ Eigen::Matrix<double, 1, 1> U_min;
+ U_min << -12.0;
+ return StateFeedbackPlantCoefficients<2, 1, 1>(A, B, C, D, U_max, U_min);
+}
+
+StateFeedbackController<2, 1, 1> MakeClawController() {
+ Eigen::Matrix<double, 2, 1> L;
+ L << 0.998251427366, 26.9874526231;
+ Eigen::Matrix<double, 1, 2> K;
+ K << 74.4310031124, 4.72251126222;
+ Eigen::Matrix<double, 2, 2> A_inv;
+ A_inv << 1.0, -0.00518405612386, 0.0, 1.07451492907;
+ return StateFeedbackController<2, 1, 1>(L, K, A_inv, MakeClawPlantCoefficients());
+}
+
+StateFeedbackPlant<2, 1, 1> MakeClawPlant() {
+ ::std::vector< ::std::unique_ptr<StateFeedbackPlantCoefficients<2, 1, 1>>> plants(1);
+ plants[0] = ::std::unique_ptr<StateFeedbackPlantCoefficients<2, 1, 1>>(new StateFeedbackPlantCoefficients<2, 1, 1>(MakeClawPlantCoefficients()));
+ return StateFeedbackPlant<2, 1, 1>(&plants);
+}
+
+StateFeedbackLoop<2, 1, 1> MakeClawLoop() {
+ ::std::vector< ::std::unique_ptr<StateFeedbackController<2, 1, 1>>> controllers(1);
+ controllers[0] = ::std::unique_ptr<StateFeedbackController<2, 1, 1>>(new StateFeedbackController<2, 1, 1>(MakeClawController()));
+ return StateFeedbackLoop<2, 1, 1>(&controllers);
+}
+
+} // namespace control_loops
+} // namespace frc971
diff --git a/y2015/control_loops/claw/claw_motor_plant.h b/y2015/control_loops/claw/claw_motor_plant.h
new file mode 100644
index 0000000..6cb9cdb
--- /dev/null
+++ b/y2015/control_loops/claw/claw_motor_plant.h
@@ -0,0 +1,20 @@
+#ifndef Y2015_CONTROL_LOOPS_CLAW_CLAW_MOTOR_PLANT_H_
+#define Y2015_CONTROL_LOOPS_CLAW_CLAW_MOTOR_PLANT_H_
+
+#include "frc971/control_loops/state_feedback_loop.h"
+
+namespace frc971 {
+namespace control_loops {
+
+StateFeedbackPlantCoefficients<2, 1, 1> MakeClawPlantCoefficients();
+
+StateFeedbackController<2, 1, 1> MakeClawController();
+
+StateFeedbackPlant<2, 1, 1> MakeClawPlant();
+
+StateFeedbackLoop<2, 1, 1> MakeClawLoop();
+
+} // namespace control_loops
+} // namespace frc971
+
+#endif // Y2015_CONTROL_LOOPS_CLAW_CLAW_MOTOR_PLANT_H_
diff --git a/y2015/control_loops/claw/replay_claw.cc b/y2015/control_loops/claw/replay_claw.cc
new file mode 100644
index 0000000..50673e4
--- /dev/null
+++ b/y2015/control_loops/claw/replay_claw.cc
@@ -0,0 +1,24 @@
+#include "aos/common/controls/replay_control_loop.h"
+#include "aos/linux_code/init.h"
+
+#include "y2015/control_loops/claw/claw.q.h"
+
+// Reads one or more log files and sends out all the queue messages (in the
+// correct order and at the correct time) to feed a "live" claw process.
+
+int main(int argc, char **argv) {
+ if (argc <= 1) {
+ fprintf(stderr, "Need at least one file to replay!\n");
+ return EXIT_FAILURE;
+ }
+
+ ::aos::InitNRT();
+
+ ::aos::controls::ControlLoopReplayer<::frc971::control_loops::ClawQueue>
+ replayer(&::frc971::control_loops::claw_queue, "claw");
+ for (int i = 1; i < argc; ++i) {
+ replayer.ProcessFile(argv[i]);
+ }
+
+ ::aos::Cleanup();
+}
diff --git a/y2015/control_loops/drivetrain/drivetrain.cc b/y2015/control_loops/drivetrain/drivetrain.cc
new file mode 100644
index 0000000..22a5d48
--- /dev/null
+++ b/y2015/control_loops/drivetrain/drivetrain.cc
@@ -0,0 +1,777 @@
+#include "y2015/control_loops/drivetrain/drivetrain.h"
+
+#include <stdio.h>
+#include <sched.h>
+#include <cmath>
+#include <memory>
+#include "Eigen/Dense"
+
+#include "aos/common/logging/logging.h"
+#include "aos/common/controls/polytope.h"
+#include "aos/common/commonmath.h"
+#include "aos/common/logging/queue_logging.h"
+#include "aos/common/logging/matrix_logging.h"
+
+#include "y2015/constants.h"
+#include "frc971/control_loops/state_feedback_loop.h"
+#include "frc971/control_loops/coerce_goal.h"
+#include "y2015/control_loops/drivetrain/polydrivetrain_cim_plant.h"
+#include "y2015/control_loops/drivetrain/drivetrain.q.h"
+#include "frc971/queues/gyro.q.h"
+#include "frc971/shifter_hall_effect.h"
+
+// A consistent way to mark code that goes away without shifters. It's still
+// here because we will have shifters again in the future.
+#define HAVE_SHIFTERS 0
+
+using frc971::sensors::gyro_reading;
+
+namespace frc971 {
+namespace control_loops {
+
+class DrivetrainMotorsSS {
+ public:
+ class LimitedDrivetrainLoop : public StateFeedbackLoop<4, 2, 2> {
+ public:
+ LimitedDrivetrainLoop(StateFeedbackLoop<4, 2, 2> &&loop)
+ : StateFeedbackLoop<4, 2, 2>(::std::move(loop)),
+ U_Poly_((Eigen::Matrix<double, 4, 2>() << 1, 0,
+ -1, 0,
+ 0, 1,
+ 0, -1).finished(),
+ (Eigen::Matrix<double, 4, 1>() << 12.0, 12.0,
+ 12.0, 12.0).finished()) {
+ ::aos::controls::HPolytope<0>::Init();
+ T << 1, -1, 1, 1;
+ T_inverse = T.inverse();
+ }
+
+ bool output_was_capped() const {
+ return output_was_capped_;
+ }
+
+ private:
+ virtual void CapU() {
+ const Eigen::Matrix<double, 4, 1> error = R() - X_hat();
+
+ if (::std::abs(U(0, 0)) > 12.0 || ::std::abs(U(1, 0)) > 12.0) {
+ mutable_U() =
+ U() * 12.0 / ::std::max(::std::abs(U(0, 0)), ::std::abs(U(1, 0)));
+ LOG_MATRIX(DEBUG, "U is now", U());
+ // TODO(Austin): Figure out why the polytope stuff wasn't working and
+ // remove this hack.
+ output_was_capped_ = true;
+ return;
+
+ LOG_MATRIX(DEBUG, "U at start", U());
+ LOG_MATRIX(DEBUG, "R at start", R());
+ LOG_MATRIX(DEBUG, "Xhat at start", X_hat());
+
+ Eigen::Matrix<double, 2, 2> position_K;
+ position_K << K(0, 0), K(0, 2),
+ K(1, 0), K(1, 2);
+ Eigen::Matrix<double, 2, 2> velocity_K;
+ velocity_K << K(0, 1), K(0, 3),
+ K(1, 1), K(1, 3);
+
+ Eigen::Matrix<double, 2, 1> position_error;
+ position_error << error(0, 0), error(2, 0);
+ const auto drive_error = T_inverse * position_error;
+ Eigen::Matrix<double, 2, 1> velocity_error;
+ velocity_error << error(1, 0), error(3, 0);
+ LOG_MATRIX(DEBUG, "error", error);
+
+ const auto &poly = U_Poly_;
+ const Eigen::Matrix<double, 4, 2> pos_poly_H =
+ poly.H() * position_K * T;
+ const Eigen::Matrix<double, 4, 1> pos_poly_k =
+ poly.k() - poly.H() * velocity_K * velocity_error;
+ const ::aos::controls::HPolytope<2> pos_poly(pos_poly_H, pos_poly_k);
+
+ Eigen::Matrix<double, 2, 1> adjusted_pos_error;
+ {
+ const auto &P = drive_error;
+
+ Eigen::Matrix<double, 1, 2> L45;
+ L45 << ::aos::sign(P(1, 0)), -::aos::sign(P(0, 0));
+ const double w45 = 0;
+
+ Eigen::Matrix<double, 1, 2> LH;
+ if (::std::abs(P(0, 0)) > ::std::abs(P(1, 0))) {
+ LH << 0, 1;
+ } else {
+ LH << 1, 0;
+ }
+ const double wh = LH.dot(P);
+
+ Eigen::Matrix<double, 2, 2> standard;
+ standard << L45, LH;
+ Eigen::Matrix<double, 2, 1> W;
+ W << w45, wh;
+ const Eigen::Matrix<double, 2, 1> intersection =
+ standard.inverse() * W;
+
+ bool is_inside_h;
+ const auto adjusted_pos_error_h =
+ DoCoerceGoal(pos_poly, LH, wh, drive_error, &is_inside_h);
+ const auto adjusted_pos_error_45 =
+ DoCoerceGoal(pos_poly, L45, w45, intersection, nullptr);
+ if (pos_poly.IsInside(intersection)) {
+ adjusted_pos_error = adjusted_pos_error_h;
+ } else {
+ if (is_inside_h) {
+ if (adjusted_pos_error_h.norm() > adjusted_pos_error_45.norm()) {
+ adjusted_pos_error = adjusted_pos_error_h;
+ } else {
+ adjusted_pos_error = adjusted_pos_error_45;
+ }
+ } else {
+ adjusted_pos_error = adjusted_pos_error_45;
+ }
+ }
+ }
+
+ LOG_MATRIX(DEBUG, "adjusted_pos_error", adjusted_pos_error);
+ mutable_U() =
+ velocity_K * velocity_error + position_K * T * adjusted_pos_error;
+ LOG_MATRIX(DEBUG, "U is now", U());
+ } else {
+ output_was_capped_ = false;
+ }
+ }
+
+ const ::aos::controls::HPolytope<2> U_Poly_;
+ Eigen::Matrix<double, 2, 2> T, T_inverse;
+ bool output_was_capped_ = false;;
+ };
+
+ DrivetrainMotorsSS()
+ : loop_(new LimitedDrivetrainLoop(
+ constants::GetValues().make_drivetrain_loop())),
+ filtered_offset_(0.0),
+ gyro_(0.0),
+ left_goal_(0.0),
+ right_goal_(0.0),
+ raw_left_(0.0),
+ raw_right_(0.0) {
+ // Low gear on both.
+ loop_->set_controller_index(0);
+ }
+
+ void SetGoal(double left, double left_velocity, double right,
+ double right_velocity) {
+ left_goal_ = left;
+ right_goal_ = right;
+ loop_->mutable_R() << left, left_velocity, right, right_velocity;
+ }
+ void SetRawPosition(double left, double right) {
+ raw_right_ = right;
+ raw_left_ = left;
+ Eigen::Matrix<double, 2, 1> Y;
+ Y << left + filtered_offset_, right - filtered_offset_;
+ loop_->Correct(Y);
+ }
+ void SetPosition(double left, double right, double gyro) {
+ // Decay the offset quickly because this gyro is great.
+ const double offset =
+ (right - left - gyro * constants::GetValues().turn_width) / 2.0;
+ filtered_offset_ = 0.25 * offset + 0.75 * filtered_offset_;
+ gyro_ = gyro;
+ SetRawPosition(left, right);
+ }
+
+ void SetExternalMotors(double left_voltage, double right_voltage) {
+ loop_->mutable_U() << left_voltage, right_voltage;
+ }
+
+ void Update(bool stop_motors, bool enable_control_loop) {
+ if (enable_control_loop) {
+ loop_->Update(stop_motors);
+ } else {
+ if (stop_motors) {
+ loop_->mutable_U().setZero();
+ loop_->mutable_U_uncapped().setZero();
+ }
+ loop_->UpdateObserver();
+ }
+ ::Eigen::Matrix<double, 4, 1> E = loop_->R() - loop_->X_hat();
+ LOG_MATRIX(DEBUG, "E", E);
+ }
+
+ double GetEstimatedRobotSpeed() const {
+ // lets just call the average of left and right velocities close enough
+ return (loop_->X_hat(1, 0) + loop_->X_hat(3, 0)) / 2;
+ }
+
+ double GetEstimatedLeftEncoder() const {
+ return loop_->X_hat(0, 0);
+ }
+
+ double GetEstimatedRightEncoder() const {
+ return loop_->X_hat(2, 0);
+ }
+
+ bool OutputWasCapped() const {
+ return loop_->output_was_capped();
+ }
+
+ void SendMotors(DrivetrainQueue::Output *output) const {
+ if (output) {
+ output->left_voltage = loop_->U(0, 0);
+ output->right_voltage = loop_->U(1, 0);
+ output->left_high = false;
+ output->right_high = false;
+ }
+ }
+
+ const LimitedDrivetrainLoop &loop() const { return *loop_; }
+
+ private:
+ ::std::unique_ptr<LimitedDrivetrainLoop> loop_;
+
+ double filtered_offset_;
+ double gyro_;
+ double left_goal_;
+ double right_goal_;
+ double raw_left_;
+ double raw_right_;
+};
+
+class PolyDrivetrain {
+ public:
+
+ enum Gear {
+ HIGH,
+ LOW,
+ SHIFTING_UP,
+ SHIFTING_DOWN
+ };
+ // Stall Torque in N m
+ static constexpr double kStallTorque = 2.42;
+ // Stall Current in Amps
+ static constexpr double kStallCurrent = 133.0;
+ // Free Speed in RPM. Used number from last year.
+ static constexpr double kFreeSpeed = 4650.0;
+ // Free Current in Amps
+ static constexpr double kFreeCurrent = 2.7;
+ // Moment of inertia of the drivetrain in kg m^2
+ // Just borrowed from last year.
+ static constexpr double J = 10;
+ // Mass of the robot, in kg.
+ static constexpr double m = 68;
+ // Radius of the robot, in meters (from last year).
+ static constexpr double rb = 0.9603 / 2.0;
+ static constexpr double kWheelRadius = 0.0515938;
+ // Resistance of the motor, divided by the number of motors.
+ static constexpr double kR = (12.0 / kStallCurrent / 2 + 0.03) / (0.93 * 0.93);
+ // Motor velocity constant
+ static constexpr double Kv =
+ ((kFreeSpeed / 60.0 * 2.0 * M_PI) / (12.0 - kR * kFreeCurrent));
+ // Torque constant
+ static constexpr double Kt = kStallTorque / kStallCurrent;
+
+ PolyDrivetrain()
+ : U_Poly_((Eigen::Matrix<double, 4, 2>() << /*[[*/ 1, 0 /*]*/,
+ /*[*/ -1, 0 /*]*/,
+ /*[*/ 0, 1 /*]*/,
+ /*[*/ 0, -1 /*]]*/).finished(),
+ (Eigen::Matrix<double, 4, 1>() << /*[[*/ 12 /*]*/,
+ /*[*/ 12 /*]*/,
+ /*[*/ 12 /*]*/,
+ /*[*/ 12 /*]]*/).finished()),
+ loop_(new StateFeedbackLoop<2, 2, 2>(
+ constants::GetValues().make_v_drivetrain_loop())),
+ ttrust_(1.1),
+ wheel_(0.0),
+ throttle_(0.0),
+ quickturn_(false),
+ stale_count_(0),
+ position_time_delta_(0.01),
+ left_gear_(LOW),
+ right_gear_(LOW),
+ counter_(0) {
+
+ last_position_.Zero();
+ position_.Zero();
+ }
+ static bool IsInGear(Gear gear) { return gear == LOW || gear == HIGH; }
+
+ static double MotorSpeed(const constants::ShifterHallEffect &hall_effect,
+ double shifter_position, double velocity) {
+ // TODO(austin): G_high, G_low and kWheelRadius
+ const double avg_hall_effect =
+ (hall_effect.clear_high + hall_effect.clear_low) / 2.0;
+
+ if (shifter_position > avg_hall_effect) {
+ return velocity / constants::GetValues().high_gear_ratio / kWheelRadius;
+ } else {
+ return velocity / constants::GetValues().low_gear_ratio / kWheelRadius;
+ }
+ }
+
+ Gear ComputeGear(const constants::ShifterHallEffect &hall_effect,
+ double velocity, Gear current) {
+ const double low_omega = MotorSpeed(hall_effect, 0.0, ::std::abs(velocity));
+ const double high_omega =
+ MotorSpeed(hall_effect, 1.0, ::std::abs(velocity));
+
+ double high_torque = ((12.0 - high_omega / Kv) * Kt / kR);
+ double low_torque = ((12.0 - low_omega / Kv) * Kt / kR);
+ double high_power = high_torque * high_omega;
+ double low_power = low_torque * low_omega;
+
+ // TODO(aschuh): Do this right!
+ if ((current == HIGH || high_power > low_power + 160) &&
+ ::std::abs(velocity) > 0.14) {
+ return HIGH;
+ } else {
+ return LOW;
+ }
+ }
+
+ void SetGoal(double wheel, double throttle, bool quickturn, bool highgear) {
+ const double kWheelNonLinearity = 0.5;
+ // Apply a sin function that's scaled to make it feel better.
+ const double angular_range = M_PI_2 * kWheelNonLinearity;
+
+ wheel_ = sin(angular_range * wheel) / sin(angular_range);
+ wheel_ = sin(angular_range * wheel_) / sin(angular_range);
+ wheel_ *= 2.3;
+ quickturn_ = quickturn;
+
+ static const double kThrottleDeadband = 0.05;
+ if (::std::abs(throttle) < kThrottleDeadband) {
+ throttle_ = 0;
+ } else {
+ throttle_ = copysign((::std::abs(throttle) - kThrottleDeadband) /
+ (1.0 - kThrottleDeadband), throttle);
+ }
+
+ // TODO(austin): Fix the upshift logic to include states.
+ Gear requested_gear;
+ if (false) {
+ const auto &values = constants::GetValues();
+ const double current_left_velocity =
+ (position_.left_encoder - last_position_.left_encoder) /
+ position_time_delta_;
+ const double current_right_velocity =
+ (position_.right_encoder - last_position_.right_encoder) /
+ position_time_delta_;
+
+ Gear left_requested =
+ ComputeGear(values.left_drive, current_left_velocity, left_gear_);
+ Gear right_requested =
+ ComputeGear(values.right_drive, current_right_velocity, right_gear_);
+ requested_gear =
+ (left_requested == HIGH || right_requested == HIGH) ? HIGH : LOW;
+ } else {
+ requested_gear = highgear ? HIGH : LOW;
+ }
+
+ const Gear shift_up =
+ constants::GetValues().clutch_transmission ? HIGH : SHIFTING_UP;
+ const Gear shift_down =
+ constants::GetValues().clutch_transmission ? LOW : SHIFTING_DOWN;
+
+ if (left_gear_ != requested_gear) {
+ if (IsInGear(left_gear_)) {
+ if (requested_gear == HIGH) {
+ left_gear_ = shift_up;
+ } else {
+ left_gear_ = shift_down;
+ }
+ } else {
+ if (requested_gear == HIGH && left_gear_ == SHIFTING_DOWN) {
+ left_gear_ = SHIFTING_UP;
+ } else if (requested_gear == LOW && left_gear_ == SHIFTING_UP) {
+ left_gear_ = SHIFTING_DOWN;
+ }
+ }
+ }
+ if (right_gear_ != requested_gear) {
+ if (IsInGear(right_gear_)) {
+ if (requested_gear == HIGH) {
+ right_gear_ = shift_up;
+ } else {
+ right_gear_ = shift_down;
+ }
+ } else {
+ if (requested_gear == HIGH && right_gear_ == SHIFTING_DOWN) {
+ right_gear_ = SHIFTING_UP;
+ } else if (requested_gear == LOW && right_gear_ == SHIFTING_UP) {
+ right_gear_ = SHIFTING_DOWN;
+ }
+ }
+ }
+ }
+ void SetPosition(const DrivetrainQueue::Position *position) {
+ const auto &values = constants::GetValues();
+ if (position == NULL) {
+ ++stale_count_;
+ } else {
+ last_position_ = position_;
+ position_ = *position;
+ position_time_delta_ = (stale_count_ + 1) * 0.01;
+ stale_count_ = 0;
+ }
+
+#if HAVE_SHIFTERS
+ if (position) {
+ GearLogging gear_logging;
+ // Switch to the correct controller.
+ const double left_middle_shifter_position =
+ (values.left_drive.clear_high + values.left_drive.clear_low) / 2.0;
+ const double right_middle_shifter_position =
+ (values.right_drive.clear_high + values.right_drive.clear_low) / 2.0;
+
+ if (position->left_shifter_position < left_middle_shifter_position ||
+ left_gear_ == LOW) {
+ if (position->right_shifter_position < right_middle_shifter_position ||
+ right_gear_ == LOW) {
+ gear_logging.left_loop_high = false;
+ gear_logging.right_loop_high = false;
+ loop_->set_controller_index(gear_logging.controller_index = 0);
+ } else {
+ gear_logging.left_loop_high = false;
+ gear_logging.right_loop_high = true;
+ loop_->set_controller_index(gear_logging.controller_index = 1);
+ }
+ } else {
+ if (position->right_shifter_position < right_middle_shifter_position ||
+ right_gear_ == LOW) {
+ gear_logging.left_loop_high = true;
+ gear_logging.right_loop_high = false;
+ loop_->set_controller_index(gear_logging.controller_index = 2);
+ } else {
+ gear_logging.left_loop_high = true;
+ gear_logging.right_loop_high = true;
+ loop_->set_controller_index(gear_logging.controller_index = 3);
+ }
+ }
+
+ // TODO(austin): Constants.
+ if (position->left_shifter_position > values.left_drive.clear_high && left_gear_ == SHIFTING_UP) {
+ left_gear_ = HIGH;
+ }
+ if (position->left_shifter_position < values.left_drive.clear_low && left_gear_ == SHIFTING_DOWN) {
+ left_gear_ = LOW;
+ }
+ if (position->right_shifter_position > values.right_drive.clear_high && right_gear_ == SHIFTING_UP) {
+ right_gear_ = HIGH;
+ }
+ if (position->right_shifter_position < values.right_drive.clear_low && right_gear_ == SHIFTING_DOWN) {
+ right_gear_ = LOW;
+ }
+
+ gear_logging.left_state = left_gear_;
+ gear_logging.right_state = right_gear_;
+ LOG_STRUCT(DEBUG, "state", gear_logging);
+ }
+#else
+ (void) values;
+#endif
+ }
+
+ double FilterVelocity(double throttle) {
+ const Eigen::Matrix<double, 2, 2> FF =
+ loop_->B().inverse() *
+ (Eigen::Matrix<double, 2, 2>::Identity() - loop_->A());
+
+ constexpr int kHighGearController = 3;
+ const Eigen::Matrix<double, 2, 2> FF_high =
+ loop_->controller(kHighGearController).plant.B().inverse() *
+ (Eigen::Matrix<double, 2, 2>::Identity() -
+ loop_->controller(kHighGearController).plant.A());
+
+ ::Eigen::Matrix<double, 1, 2> FF_sum = FF.colwise().sum();
+ int min_FF_sum_index;
+ const double min_FF_sum = FF_sum.minCoeff(&min_FF_sum_index);
+ const double min_K_sum = loop_->K().col(min_FF_sum_index).sum();
+ const double high_min_FF_sum = FF_high.col(0).sum();
+
+ const double adjusted_ff_voltage = ::aos::Clip(
+ throttle * 12.0 * min_FF_sum / high_min_FF_sum, -12.0, 12.0);
+ return (adjusted_ff_voltage +
+ ttrust_ * min_K_sum * (loop_->X_hat(0, 0) + loop_->X_hat(1, 0)) /
+ 2.0) /
+ (ttrust_ * min_K_sum + min_FF_sum);
+ }
+
+ double MaxVelocity() {
+ const Eigen::Matrix<double, 2, 2> FF =
+ loop_->B().inverse() *
+ (Eigen::Matrix<double, 2, 2>::Identity() - loop_->A());
+
+ constexpr int kHighGearController = 3;
+ const Eigen::Matrix<double, 2, 2> FF_high =
+ loop_->controller(kHighGearController).plant.B().inverse() *
+ (Eigen::Matrix<double, 2, 2>::Identity() -
+ loop_->controller(kHighGearController).plant.A());
+
+ ::Eigen::Matrix<double, 1, 2> FF_sum = FF.colwise().sum();
+ int min_FF_sum_index;
+ const double min_FF_sum = FF_sum.minCoeff(&min_FF_sum_index);
+ //const double min_K_sum = loop_->K().col(min_FF_sum_index).sum();
+ const double high_min_FF_sum = FF_high.col(0).sum();
+
+ const double adjusted_ff_voltage = ::aos::Clip(
+ 12.0 * min_FF_sum / high_min_FF_sum, -12.0, 12.0);
+ return adjusted_ff_voltage / min_FF_sum;
+ }
+
+ void Update() {
+ const auto &values = constants::GetValues();
+ // TODO(austin): Observer for the current velocity instead of difference
+ // calculations.
+ ++counter_;
+#if HAVE_SHIFTERS
+ const double current_left_velocity =
+ (position_.left_encoder - last_position_.left_encoder) /
+ position_time_delta_;
+ const double current_right_velocity =
+ (position_.right_encoder - last_position_.right_encoder) /
+ position_time_delta_;
+ const double left_motor_speed =
+ MotorSpeed(values.left_drive, position_.left_shifter_position,
+ current_left_velocity);
+ const double right_motor_speed =
+ MotorSpeed(values.right_drive, position_.right_shifter_position,
+ current_right_velocity);
+
+ {
+ CIMLogging logging;
+
+ // Reset the CIM model to the current conditions to be ready for when we
+ // shift.
+ if (IsInGear(left_gear_)) {
+ logging.left_in_gear = true;
+ } else {
+ logging.left_in_gear = false;
+ }
+ logging.left_motor_speed = left_motor_speed;
+ logging.left_velocity = current_left_velocity;
+ if (IsInGear(right_gear_)) {
+ logging.right_in_gear = true;
+ } else {
+ logging.right_in_gear = false;
+ }
+ logging.right_motor_speed = right_motor_speed;
+ logging.right_velocity = current_right_velocity;
+
+ LOG_STRUCT(DEBUG, "currently", logging);
+ }
+#else
+ (void) values;
+#endif
+
+#if HAVE_SHIFTERS
+ if (IsInGear(left_gear_) && IsInGear(right_gear_)) {
+#else
+ {
+#endif
+ // FF * X = U (steady state)
+ const Eigen::Matrix<double, 2, 2> FF =
+ loop_->B().inverse() *
+ (Eigen::Matrix<double, 2, 2>::Identity() - loop_->A());
+
+ // Invert the plant to figure out how the velocity filter would have to
+ // work
+ // out in order to filter out the forwards negative inertia.
+ // This math assumes that the left and right power and velocity are
+ // equals,
+ // and that the plant is the same on the left and right.
+ const double fvel = FilterVelocity(throttle_);
+
+ const double sign_svel = wheel_ * ((fvel > 0.0) ? 1.0 : -1.0);
+ double steering_velocity;
+ if (quickturn_) {
+ steering_velocity = wheel_ * MaxVelocity();
+ } else {
+ steering_velocity = ::std::abs(fvel) * wheel_;
+ }
+ const double left_velocity = fvel - steering_velocity;
+ const double right_velocity = fvel + steering_velocity;
+ LOG(DEBUG, "l=%f r=%f\n", left_velocity, right_velocity);
+
+ // Integrate velocity to get the position.
+ // This position is used to get integral control.
+ loop_->mutable_R() << left_velocity, right_velocity;
+
+ if (!quickturn_) {
+ // K * R = w
+ Eigen::Matrix<double, 1, 2> equality_k;
+ equality_k << 1 + sign_svel, -(1 - sign_svel);
+ const double equality_w = 0.0;
+
+ // Construct a constraint on R by manipulating the constraint on U
+ ::aos::controls::HPolytope<2> R_poly = ::aos::controls::HPolytope<2>(
+ U_Poly_.H() * (loop_->K() + FF),
+ U_Poly_.k() + U_Poly_.H() * loop_->K() * loop_->X_hat());
+
+ // Limit R back inside the box.
+ loop_->mutable_R() =
+ CoerceGoal(R_poly, equality_k, equality_w, loop_->R());
+ }
+
+ const Eigen::Matrix<double, 2, 1> FF_volts = FF * loop_->R();
+ const Eigen::Matrix<double, 2, 1> U_ideal =
+ loop_->K() * (loop_->R() - loop_->X_hat()) + FF_volts;
+
+ for (int i = 0; i < 2; i++) {
+ loop_->mutable_U()[i] = ::aos::Clip(U_ideal[i], -12, 12);
+ }
+
+ // TODO(austin): Model this better.
+ // TODO(austin): Feed back?
+ loop_->mutable_X_hat() =
+ loop_->A() * loop_->X_hat() + loop_->B() * loop_->U();
+#if HAVE_SHIFTERS
+ } else {
+ // Any motor is not in gear. Speed match.
+ ::Eigen::Matrix<double, 1, 1> R_left;
+ ::Eigen::Matrix<double, 1, 1> R_right;
+ R_left(0, 0) = left_motor_speed;
+ R_right(0, 0) = right_motor_speed;
+
+ const double wiggle =
+ (static_cast<double>((counter_ % 20) / 10) - 0.5) * 5.0;
+
+ loop_->mutable_U(0, 0) = ::aos::Clip(
+ (R_left / Kv)(0, 0) + (IsInGear(left_gear_) ? 0 : wiggle),
+ -12.0, 12.0);
+ loop_->mutable_U(1, 0) = ::aos::Clip(
+ (R_right / Kv)(0, 0) + (IsInGear(right_gear_) ? 0 : wiggle),
+ -12.0, 12.0);
+ loop_->mutable_U() *= 12.0 / ::aos::robot_state->voltage_battery;
+#endif
+ }
+ }
+
+ void SendMotors(DrivetrainQueue::Output *output) {
+ if (output != NULL) {
+ output->left_voltage = loop_->U(0, 0);
+ output->right_voltage = loop_->U(1, 0);
+ output->left_high = left_gear_ == HIGH || left_gear_ == SHIFTING_UP;
+ output->right_high = right_gear_ == HIGH || right_gear_ == SHIFTING_UP;
+ }
+ }
+
+ private:
+ const ::aos::controls::HPolytope<2> U_Poly_;
+
+ ::std::unique_ptr<StateFeedbackLoop<2, 2, 2>> loop_;
+
+ const double ttrust_;
+ double wheel_;
+ double throttle_;
+ bool quickturn_;
+ int stale_count_;
+ double position_time_delta_;
+ Gear left_gear_;
+ Gear right_gear_;
+ DrivetrainQueue::Position last_position_;
+ DrivetrainQueue::Position position_;
+ int counter_;
+};
+constexpr double PolyDrivetrain::kStallTorque;
+constexpr double PolyDrivetrain::kStallCurrent;
+constexpr double PolyDrivetrain::kFreeSpeed;
+constexpr double PolyDrivetrain::kFreeCurrent;
+constexpr double PolyDrivetrain::J;
+constexpr double PolyDrivetrain::m;
+constexpr double PolyDrivetrain::rb;
+constexpr double PolyDrivetrain::kWheelRadius;
+constexpr double PolyDrivetrain::kR;
+constexpr double PolyDrivetrain::Kv;
+constexpr double PolyDrivetrain::Kt;
+
+
+void DrivetrainLoop::RunIteration(const DrivetrainQueue::Goal *goal,
+ const DrivetrainQueue::Position *position,
+ DrivetrainQueue::Output *output,
+ DrivetrainQueue::Status * status) {
+ // TODO(aschuh): These should be members of the class.
+ static DrivetrainMotorsSS dt_closedloop;
+ static PolyDrivetrain dt_openloop;
+
+ bool bad_pos = false;
+ if (position == nullptr) {
+ LOG_INTERVAL(no_position_);
+ bad_pos = true;
+ }
+ no_position_.Print();
+
+ bool control_loop_driving = false;
+ if (goal) {
+ double wheel = goal->steering;
+ double throttle = goal->throttle;
+ bool quickturn = goal->quickturn;
+#if HAVE_SHIFTERS
+ bool highgear = goal->highgear;
+#endif
+
+ control_loop_driving = goal->control_loop_driving;
+ double left_goal = goal->left_goal;
+ double right_goal = goal->right_goal;
+
+ dt_closedloop.SetGoal(left_goal, goal->left_velocity_goal, right_goal,
+ goal->right_velocity_goal);
+#if HAVE_SHIFTERS
+ dt_openloop.SetGoal(wheel, throttle, quickturn, highgear);
+#else
+ dt_openloop.SetGoal(wheel, throttle, quickturn, false);
+#endif
+ }
+
+ if (!bad_pos) {
+ const double left_encoder = position->left_encoder;
+ const double right_encoder = position->right_encoder;
+ if (gyro_reading.FetchLatest()) {
+ LOG_STRUCT(DEBUG, "using", *gyro_reading.get());
+ dt_closedloop.SetPosition(left_encoder, right_encoder,
+ gyro_reading->angle);
+ } else {
+ dt_closedloop.SetRawPosition(left_encoder, right_encoder);
+ }
+ }
+ dt_openloop.SetPosition(position);
+ dt_openloop.Update();
+
+ if (control_loop_driving) {
+ dt_closedloop.Update(output == NULL, true);
+ dt_closedloop.SendMotors(output);
+ } else {
+ dt_openloop.SendMotors(output);
+ if (output) {
+ dt_closedloop.SetExternalMotors(output->left_voltage,
+ output->right_voltage);
+ }
+ dt_closedloop.Update(output == NULL, false);
+ }
+
+ // set the output status of the control loop state
+ if (status) {
+ bool done = false;
+ if (goal) {
+ done = ((::std::abs(goal->left_goal -
+ dt_closedloop.GetEstimatedLeftEncoder()) <
+ constants::GetValues().drivetrain_done_distance) &&
+ (::std::abs(goal->right_goal -
+ dt_closedloop.GetEstimatedRightEncoder()) <
+ constants::GetValues().drivetrain_done_distance));
+ }
+ status->is_done = done;
+ status->robot_speed = dt_closedloop.GetEstimatedRobotSpeed();
+ status->filtered_left_position = dt_closedloop.GetEstimatedLeftEncoder();
+ status->filtered_right_position = dt_closedloop.GetEstimatedRightEncoder();
+
+ status->filtered_left_velocity = dt_closedloop.loop().X_hat(1, 0);
+ status->filtered_right_velocity = dt_closedloop.loop().X_hat(3, 0);
+ status->output_was_capped = dt_closedloop.OutputWasCapped();
+ status->uncapped_left_voltage = dt_closedloop.loop().U_uncapped(0, 0);
+ status->uncapped_right_voltage = dt_closedloop.loop().U_uncapped(1, 0);
+ }
+}
+
+} // namespace control_loops
+} // namespace frc971
diff --git a/y2015/control_loops/drivetrain/drivetrain.gyp b/y2015/control_loops/drivetrain/drivetrain.gyp
new file mode 100644
index 0000000..4333258
--- /dev/null
+++ b/y2015/control_loops/drivetrain/drivetrain.gyp
@@ -0,0 +1,103 @@
+{
+ 'targets': [
+ {
+ 'target_name': 'replay_drivetrain',
+ 'type': 'executable',
+ 'variables': {
+ 'no_rsync': 1,
+ },
+ 'sources': [
+ 'replay_drivetrain.cc',
+ ],
+ 'dependencies': [
+ 'drivetrain_queue',
+ '<(AOS)/common/controls/controls.gyp:replay_control_loop',
+ '<(AOS)/linux_code/linux_code.gyp:init',
+ ],
+ },
+ {
+ 'target_name': 'drivetrain_queue',
+ 'type': 'static_library',
+ 'sources': ['drivetrain.q'],
+ 'variables': {
+ 'header_path': 'y2015/control_loops/drivetrain',
+ },
+ 'dependencies': [
+ '<(AOS)/common/controls/controls.gyp:control_loop_queues',
+ ],
+ 'export_dependent_settings': [
+ '<(AOS)/common/controls/controls.gyp:control_loop_queues',
+ ],
+ 'includes': ['../../../aos/build/queues.gypi'],
+ },
+ {
+ 'target_name': 'polydrivetrain_plants',
+ 'type': 'static_library',
+ 'sources': [
+ 'polydrivetrain_dog_motor_plant.cc',
+ 'drivetrain_dog_motor_plant.cc',
+ ],
+ 'dependencies': [
+ '<(DEPTH)/frc971/control_loops/control_loops.gyp:state_feedback_loop',
+ ],
+ 'export_dependent_settings': [
+ '<(DEPTH)/frc971/control_loops/control_loops.gyp:state_feedback_loop',
+ ],
+ },
+ {
+ 'target_name': 'drivetrain_lib',
+ 'type': 'static_library',
+ 'sources': [
+ 'drivetrain.cc',
+ 'polydrivetrain_cim_plant.cc',
+ ],
+ 'dependencies': [
+ 'drivetrain_queue',
+ '<(AOS)/common/controls/controls.gyp:control_loop',
+ '<(DEPTH)/y2015/y2015.gyp:constants',
+ '<(AOS)/common/controls/controls.gyp:polytope',
+ '<(DEPTH)/frc971/control_loops/control_loops.gyp:state_feedback_loop',
+ '<(DEPTH)/frc971/control_loops/control_loops.gyp:coerce_goal',
+ '<(DEPTH)/frc971/queues/queues.gyp:gyro',
+ '<(AOS)/common/util/util.gyp:log_interval',
+ '<(AOS)/common/logging/logging.gyp:queue_logging',
+ '<(AOS)/common/logging/logging.gyp:matrix_logging',
+ ],
+ 'export_dependent_settings': [
+ '<(AOS)/common/controls/controls.gyp:polytope',
+ '<(DEPTH)/frc971/control_loops/control_loops.gyp:state_feedback_loop',
+ '<(DEPTH)/frc971/control_loops/control_loops.gyp:coerce_goal',
+ '<(AOS)/common/controls/controls.gyp:control_loop',
+ 'drivetrain_queue',
+ ],
+ },
+ {
+ 'target_name': 'drivetrain_lib_test',
+ 'type': 'executable',
+ 'sources': [
+ 'drivetrain_lib_test.cc',
+ ],
+ 'dependencies': [
+ '<(EXTERNALS):gtest',
+ 'drivetrain_queue',
+ 'drivetrain_lib',
+ '<(AOS)/common/controls/controls.gyp:control_loop_test',
+ '<(DEPTH)/frc971/control_loops/control_loops.gyp:state_feedback_loop',
+ '<(DEPTH)/frc971/queues/queues.gyp:gyro',
+ '<(AOS)/common/common.gyp:queues',
+ ],
+ },
+ {
+ 'target_name': 'drivetrain',
+ 'type': 'executable',
+ 'sources': [
+ 'drivetrain_main.cc',
+ ],
+ 'dependencies': [
+ '<(AOS)/linux_code/linux_code.gyp:init',
+ 'drivetrain_lib',
+ 'drivetrain_queue',
+ ],
+ },
+ ],
+}
diff --git a/y2015/control_loops/drivetrain/drivetrain.h b/y2015/control_loops/drivetrain/drivetrain.h
new file mode 100644
index 0000000..8e8768e
--- /dev/null
+++ b/y2015/control_loops/drivetrain/drivetrain.h
@@ -0,0 +1,43 @@
+#ifndef Y2015_CONTROL_LOOPS_DRIVETRAIN_H_
+#define Y2015_CONTROL_LOOPS_DRIVETRAIN_H_
+
+#include "Eigen/Dense"
+
+#include "aos/common/controls/polytope.h"
+#include "aos/common/controls/control_loop.h"
+#include "aos/common/controls/polytope.h"
+#include "y2015/control_loops/drivetrain/drivetrain.q.h"
+#include "aos/common/util/log_interval.h"
+
+namespace frc971 {
+namespace control_loops {
+
+class DrivetrainLoop
+ : public aos::controls::ControlLoop<control_loops::DrivetrainQueue> {
+ public:
+ // Constructs a control loop which can take a Drivetrain or defaults to the
+ // drivetrain at frc971::control_loops::drivetrain
+ explicit DrivetrainLoop(control_loops::DrivetrainQueue *my_drivetrain =
+ &control_loops::drivetrain_queue)
+ : aos::controls::ControlLoop<control_loops::DrivetrainQueue>(
+ my_drivetrain) {
+ ::aos::controls::HPolytope<0>::Init();
+ }
+
+ protected:
+ // Executes one cycle of the control loop.
+ virtual void RunIteration(
+ const control_loops::DrivetrainQueue::Goal *goal,
+ const control_loops::DrivetrainQueue::Position *position,
+ control_loops::DrivetrainQueue::Output *output,
+ control_loops::DrivetrainQueue::Status *status);
+
+ typedef ::aos::util::SimpleLogInterval SimpleLogInterval;
+ SimpleLogInterval no_position_ = SimpleLogInterval(
+ ::aos::time::Time::InSeconds(0.25), WARNING, "no position");
+};
+
+} // namespace control_loops
+} // namespace frc971
+
+#endif // Y2015_CONTROL_LOOPS_DRIVETRAIN_H_
diff --git a/y2015/control_loops/drivetrain/drivetrain.q b/y2015/control_loops/drivetrain/drivetrain.q
new file mode 100644
index 0000000..942c5b3
--- /dev/null
+++ b/y2015/control_loops/drivetrain/drivetrain.q
@@ -0,0 +1,71 @@
+package frc971.control_loops;
+
+import "aos/common/controls/control_loops.q";
+
+struct GearLogging {
+ int8_t controller_index;
+ bool left_loop_high;
+ bool right_loop_high;
+ int8_t left_state;
+ int8_t right_state;
+};
+
+struct CIMLogging {
+ bool left_in_gear;
+ bool right_in_gear;
+ double left_motor_speed;
+ double right_motor_speed;
+ double left_velocity;
+ double right_velocity;
+};
+
+queue_group DrivetrainQueue {
+ implements aos.control_loops.ControlLoop;
+
+ message Goal {
+ double steering;
+ double throttle;
+ //bool highgear;
+ bool quickturn;
+ bool control_loop_driving;
+ double left_goal;
+ double left_velocity_goal;
+ double right_goal;
+ double right_velocity_goal;
+ };
+
+ message Position {
+ double left_encoder;
+ double right_encoder;
+ //double left_shifter_position;
+ //double right_shifter_position;
+ };
+
+ message Output {
+ double left_voltage;
+ double right_voltage;
+ bool left_high;
+ bool right_high;
+ };
+
+ message Status {
+ double robot_speed;
+ double filtered_left_position;
+ double filtered_right_position;
+ double filtered_left_velocity;
+ double filtered_right_velocity;
+
+ double uncapped_left_voltage;
+ double uncapped_right_voltage;
+ bool output_was_capped;
+
+ bool is_done;
+ };
+
+ queue Goal goal;
+ queue Position position;
+ queue Output output;
+ queue Status status;
+};
+
+queue_group DrivetrainQueue drivetrain_queue;
diff --git a/y2015/control_loops/drivetrain/drivetrain_dog_motor_plant.cc b/y2015/control_loops/drivetrain/drivetrain_dog_motor_plant.cc
new file mode 100644
index 0000000..e2f5a9a
--- /dev/null
+++ b/y2015/control_loops/drivetrain/drivetrain_dog_motor_plant.cc
@@ -0,0 +1,133 @@
+#include "y2015/control_loops/drivetrain/drivetrain_dog_motor_plant.h"
+
+#include <vector>
+
+#include "frc971/control_loops/state_feedback_loop.h"
+
+namespace frc971 {
+namespace control_loops {
+
+StateFeedbackPlantCoefficients<4, 2, 2> MakeDrivetrainLowLowPlantCoefficients() {
+ Eigen::Matrix<double, 4, 4> A;
+ A << 1.0, 0.00494348612824, 0.0, 1.24048455404e-05, 0.0, 0.977484092703, 0.0, 0.00492433284048, 0.0, 1.24048455404e-05, 1.0, 0.00494348612824, 0.0, 0.00492433284048, 0.0, 0.977484092703;
+ Eigen::Matrix<double, 4, 2> B;
+ B << 2.09180756177e-05, -4.59153636043e-06, 0.00833405032795, -0.00182269526971, -4.59153636043e-06, 2.09180756177e-05, -0.00182269526971, 0.00833405032795;
+ Eigen::Matrix<double, 2, 4> C;
+ C << 1, 0, 0, 0, 0, 0, 1, 0;
+ Eigen::Matrix<double, 2, 2> D;
+ D << 0, 0, 0, 0;
+ Eigen::Matrix<double, 2, 1> U_max;
+ U_max << 12.0, 12.0;
+ Eigen::Matrix<double, 2, 1> U_min;
+ U_min << -12.0, -12.0;
+ return StateFeedbackPlantCoefficients<4, 2, 2>(A, B, C, D, U_max, U_min);
+}
+
+StateFeedbackPlantCoefficients<4, 2, 2> MakeDrivetrainLowHighPlantCoefficients() {
+ Eigen::Matrix<double, 4, 4> A;
+ A << 1.0, 0.00494348612824, 0.0, 1.24048455404e-05, 0.0, 0.977484092703, 0.0, 0.00492433284048, 0.0, 1.24048455404e-05, 1.0, 0.00494348612824, 0.0, 0.00492433284048, 0.0, 0.977484092703;
+ Eigen::Matrix<double, 4, 2> B;
+ B << 2.09180756177e-05, -4.59153636043e-06, 0.00833405032795, -0.00182269526971, -4.59153636043e-06, 2.09180756177e-05, -0.00182269526971, 0.00833405032795;
+ Eigen::Matrix<double, 2, 4> C;
+ C << 1, 0, 0, 0, 0, 0, 1, 0;
+ Eigen::Matrix<double, 2, 2> D;
+ D << 0, 0, 0, 0;
+ Eigen::Matrix<double, 2, 1> U_max;
+ U_max << 12.0, 12.0;
+ Eigen::Matrix<double, 2, 1> U_min;
+ U_min << -12.0, -12.0;
+ return StateFeedbackPlantCoefficients<4, 2, 2>(A, B, C, D, U_max, U_min);
+}
+
+StateFeedbackPlantCoefficients<4, 2, 2> MakeDrivetrainHighLowPlantCoefficients() {
+ Eigen::Matrix<double, 4, 4> A;
+ A << 1.0, 0.00494348612824, 0.0, 1.24048455404e-05, 0.0, 0.977484092703, 0.0, 0.00492433284048, 0.0, 1.24048455404e-05, 1.0, 0.00494348612824, 0.0, 0.00492433284048, 0.0, 0.977484092703;
+ Eigen::Matrix<double, 4, 2> B;
+ B << 2.09180756177e-05, -4.59153636043e-06, 0.00833405032795, -0.00182269526971, -4.59153636043e-06, 2.09180756177e-05, -0.00182269526971, 0.00833405032795;
+ Eigen::Matrix<double, 2, 4> C;
+ C << 1, 0, 0, 0, 0, 0, 1, 0;
+ Eigen::Matrix<double, 2, 2> D;
+ D << 0, 0, 0, 0;
+ Eigen::Matrix<double, 2, 1> U_max;
+ U_max << 12.0, 12.0;
+ Eigen::Matrix<double, 2, 1> U_min;
+ U_min << -12.0, -12.0;
+ return StateFeedbackPlantCoefficients<4, 2, 2>(A, B, C, D, U_max, U_min);
+}
+
+StateFeedbackPlantCoefficients<4, 2, 2> MakeDrivetrainHighHighPlantCoefficients() {
+ Eigen::Matrix<double, 4, 4> A;
+ A << 1.0, 0.00494348612824, 0.0, 1.24048455404e-05, 0.0, 0.977484092703, 0.0, 0.00492433284048, 0.0, 1.24048455404e-05, 1.0, 0.00494348612824, 0.0, 0.00492433284048, 0.0, 0.977484092703;
+ Eigen::Matrix<double, 4, 2> B;
+ B << 2.09180756177e-05, -4.59153636043e-06, 0.00833405032795, -0.00182269526971, -4.59153636043e-06, 2.09180756177e-05, -0.00182269526971, 0.00833405032795;
+ Eigen::Matrix<double, 2, 4> C;
+ C << 1, 0, 0, 0, 0, 0, 1, 0;
+ Eigen::Matrix<double, 2, 2> D;
+ D << 0, 0, 0, 0;
+ Eigen::Matrix<double, 2, 1> U_max;
+ U_max << 12.0, 12.0;
+ Eigen::Matrix<double, 2, 1> U_min;
+ U_min << -12.0, -12.0;
+ return StateFeedbackPlantCoefficients<4, 2, 2>(A, B, C, D, U_max, U_min);
+}
+
+StateFeedbackController<4, 2, 2> MakeDrivetrainLowLowController() {
+ Eigen::Matrix<double, 4, 2> L;
+ L << 1.2774840927, 0.00492433284048, 79.1440456622, 1.05150722636, 0.00492433284048, 1.2774840927, 1.05150722636, 79.1440456622;
+ Eigen::Matrix<double, 2, 4> K;
+ K << 160.19677247, 15.5358468938, 1.60401194139, 1.33861213093, 1.60401194142, 1.33861213094, 160.19677247, 15.5358468938;
+ Eigen::Matrix<double, 4, 4> A_inv;
+ A_inv << 1.0, -0.00505742153256, 0.0, 1.27875036472e-05, 0.0, 1.02306051542, 0.0, -0.00515393603998, 0.0, 1.27875036472e-05, 1.0, -0.00505742153256, 0.0, -0.00515393603998, 0.0, 1.02306051542;
+ return StateFeedbackController<4, 2, 2>(L, K, A_inv, MakeDrivetrainLowLowPlantCoefficients());
+}
+
+StateFeedbackController<4, 2, 2> MakeDrivetrainLowHighController() {
+ Eigen::Matrix<double, 4, 2> L;
+ L << 1.2774840927, 0.00492433284048, 79.1440456622, 1.05150722636, 0.00492433284048, 1.2774840927, 1.05150722636, 79.1440456622;
+ Eigen::Matrix<double, 2, 4> K;
+ K << 160.19677247, 15.5358468938, 1.60401194139, 1.33861213093, 1.60401194142, 1.33861213094, 160.19677247, 15.5358468938;
+ Eigen::Matrix<double, 4, 4> A_inv;
+ A_inv << 1.0, -0.00505742153256, 0.0, 1.27875036472e-05, 0.0, 1.02306051542, 0.0, -0.00515393603998, 0.0, 1.27875036472e-05, 1.0, -0.00505742153256, 0.0, -0.00515393603998, 0.0, 1.02306051542;
+ return StateFeedbackController<4, 2, 2>(L, K, A_inv, MakeDrivetrainLowHighPlantCoefficients());
+}
+
+StateFeedbackController<4, 2, 2> MakeDrivetrainHighLowController() {
+ Eigen::Matrix<double, 4, 2> L;
+ L << 1.2774840927, 0.00492433284048, 79.1440456622, 1.05150722636, 0.00492433284048, 1.2774840927, 1.05150722636, 79.1440456622;
+ Eigen::Matrix<double, 2, 4> K;
+ K << 160.19677247, 15.5358468938, 1.60401194139, 1.33861213093, 1.60401194142, 1.33861213094, 160.19677247, 15.5358468938;
+ Eigen::Matrix<double, 4, 4> A_inv;
+ A_inv << 1.0, -0.00505742153256, 0.0, 1.27875036472e-05, 0.0, 1.02306051542, 0.0, -0.00515393603998, 0.0, 1.27875036472e-05, 1.0, -0.00505742153256, 0.0, -0.00515393603998, 0.0, 1.02306051542;
+ return StateFeedbackController<4, 2, 2>(L, K, A_inv, MakeDrivetrainHighLowPlantCoefficients());
+}
+
+StateFeedbackController<4, 2, 2> MakeDrivetrainHighHighController() {
+ Eigen::Matrix<double, 4, 2> L;
+ L << 1.2774840927, 0.00492433284048, 79.1440456622, 1.05150722636, 0.00492433284048, 1.2774840927, 1.05150722636, 79.1440456622;
+ Eigen::Matrix<double, 2, 4> K;
+ K << 160.19677247, 15.5358468938, 1.60401194139, 1.33861213093, 1.60401194142, 1.33861213094, 160.19677247, 15.5358468938;
+ Eigen::Matrix<double, 4, 4> A_inv;
+ A_inv << 1.0, -0.00505742153256, 0.0, 1.27875036472e-05, 0.0, 1.02306051542, 0.0, -0.00515393603998, 0.0, 1.27875036472e-05, 1.0, -0.00505742153256, 0.0, -0.00515393603998, 0.0, 1.02306051542;
+ return StateFeedbackController<4, 2, 2>(L, K, A_inv, MakeDrivetrainHighHighPlantCoefficients());
+}
+
+StateFeedbackPlant<4, 2, 2> MakeDrivetrainPlant() {
+ ::std::vector< ::std::unique_ptr<StateFeedbackPlantCoefficients<4, 2, 2>>> plants(4);
+ plants[0] = ::std::unique_ptr<StateFeedbackPlantCoefficients<4, 2, 2>>(new StateFeedbackPlantCoefficients<4, 2, 2>(MakeDrivetrainLowLowPlantCoefficients()));
+ plants[1] = ::std::unique_ptr<StateFeedbackPlantCoefficients<4, 2, 2>>(new StateFeedbackPlantCoefficients<4, 2, 2>(MakeDrivetrainLowHighPlantCoefficients()));
+ plants[2] = ::std::unique_ptr<StateFeedbackPlantCoefficients<4, 2, 2>>(new StateFeedbackPlantCoefficients<4, 2, 2>(MakeDrivetrainHighLowPlantCoefficients()));
+ plants[3] = ::std::unique_ptr<StateFeedbackPlantCoefficients<4, 2, 2>>(new StateFeedbackPlantCoefficients<4, 2, 2>(MakeDrivetrainHighHighPlantCoefficients()));
+ return StateFeedbackPlant<4, 2, 2>(&plants);
+}
+
+StateFeedbackLoop<4, 2, 2> MakeDrivetrainLoop() {
+ ::std::vector< ::std::unique_ptr<StateFeedbackController<4, 2, 2>>> controllers(4);
+ controllers[0] = ::std::unique_ptr<StateFeedbackController<4, 2, 2>>(new StateFeedbackController<4, 2, 2>(MakeDrivetrainLowLowController()));
+ controllers[1] = ::std::unique_ptr<StateFeedbackController<4, 2, 2>>(new StateFeedbackController<4, 2, 2>(MakeDrivetrainLowHighController()));
+ controllers[2] = ::std::unique_ptr<StateFeedbackController<4, 2, 2>>(new StateFeedbackController<4, 2, 2>(MakeDrivetrainHighLowController()));
+ controllers[3] = ::std::unique_ptr<StateFeedbackController<4, 2, 2>>(new StateFeedbackController<4, 2, 2>(MakeDrivetrainHighHighController()));
+ return StateFeedbackLoop<4, 2, 2>(&controllers);
+}
+
+} // namespace control_loops
+} // namespace frc971
diff --git a/y2015/control_loops/drivetrain/drivetrain_dog_motor_plant.h b/y2015/control_loops/drivetrain/drivetrain_dog_motor_plant.h
new file mode 100644
index 0000000..a2848be
--- /dev/null
+++ b/y2015/control_loops/drivetrain/drivetrain_dog_motor_plant.h
@@ -0,0 +1,32 @@
+#ifndef Y2015_CONTROL_LOOPS_DRIVETRAIN_DRIVETRAIN_DOG_MOTOR_PLANT_H_
+#define Y2015_CONTROL_LOOPS_DRIVETRAIN_DRIVETRAIN_DOG_MOTOR_PLANT_H_
+
+#include "frc971/control_loops/state_feedback_loop.h"
+
+namespace frc971 {
+namespace control_loops {
+
+StateFeedbackPlantCoefficients<4, 2, 2> MakeDrivetrainLowLowPlantCoefficients();
+
+StateFeedbackController<4, 2, 2> MakeDrivetrainLowLowController();
+
+StateFeedbackPlantCoefficients<4, 2, 2> MakeDrivetrainLowHighPlantCoefficients();
+
+StateFeedbackController<4, 2, 2> MakeDrivetrainLowHighController();
+
+StateFeedbackPlantCoefficients<4, 2, 2> MakeDrivetrainHighLowPlantCoefficients();
+
+StateFeedbackController<4, 2, 2> MakeDrivetrainHighLowController();
+
+StateFeedbackPlantCoefficients<4, 2, 2> MakeDrivetrainHighHighPlantCoefficients();
+
+StateFeedbackController<4, 2, 2> MakeDrivetrainHighHighController();
+
+StateFeedbackPlant<4, 2, 2> MakeDrivetrainPlant();
+
+StateFeedbackLoop<4, 2, 2> MakeDrivetrainLoop();
+
+} // namespace control_loops
+} // namespace frc971
+
+#endif // Y2015_CONTROL_LOOPS_DRIVETRAIN_DRIVETRAIN_DOG_MOTOR_PLANT_H_
diff --git a/y2015/control_loops/drivetrain/drivetrain_lib_test.cc b/y2015/control_loops/drivetrain/drivetrain_lib_test.cc
new file mode 100644
index 0000000..d3869aa
--- /dev/null
+++ b/y2015/control_loops/drivetrain/drivetrain_lib_test.cc
@@ -0,0 +1,297 @@
+#include <unistd.h>
+
+#include <memory>
+
+#include "gtest/gtest.h"
+#include "aos/common/network/team_number.h"
+#include "aos/common/queue_testutils.h"
+#include "aos/common/controls/polytope.h"
+#include "aos/common/controls/control_loop_test.h"
+
+#include "y2015/control_loops/drivetrain/drivetrain.q.h"
+#include "y2015/control_loops/drivetrain/drivetrain.h"
+#include "frc971/control_loops/state_feedback_loop.h"
+#include "frc971/control_loops/coerce_goal.h"
+#include "y2015/control_loops/drivetrain/drivetrain_dog_motor_plant.h"
+#include "frc971/queues/gyro.q.h"
+
+
+namespace frc971 {
+namespace control_loops {
+namespace testing {
+
+class Environment : public ::testing::Environment {
+ public:
+ virtual ~Environment() {}
+ // how to set up the environment.
+ virtual void SetUp() {
+ aos::controls::HPolytope<0>::Init();
+ }
+};
+::testing::Environment* const holder_env =
+ ::testing::AddGlobalTestEnvironment(new Environment);
+
+class TeamNumberEnvironment : public ::testing::Environment {
+ public:
+ // Override this to define how to set up the environment.
+ virtual void SetUp() { aos::network::OverrideTeamNumber(971); }
+};
+
+::testing::Environment* const team_number_env =
+ ::testing::AddGlobalTestEnvironment(new TeamNumberEnvironment);
+
+// Class which simulates the drivetrain and sends out queue messages containing
+// the position.
+class DrivetrainSimulation {
+ public:
+ // Constructs a motor simulation.
+ // TODO(aschuh) Do we want to test the clutch one too?
+ DrivetrainSimulation()
+ : drivetrain_plant_(
+ new StateFeedbackPlant<4, 2, 2>(MakeDrivetrainPlant())),
+ my_drivetrain_queue_(".frc971.control_loops.drivetrain",
+ 0x8a8dde77, ".frc971.control_loops.drivetrain.goal",
+ ".frc971.control_loops.drivetrain.position",
+ ".frc971.control_loops.drivetrain.output",
+ ".frc971.control_loops.drivetrain.status") {
+ Reinitialize();
+ }
+
+ // Resets the plant.
+ void Reinitialize() {
+ drivetrain_plant_->mutable_X(0, 0) = 0.0;
+ drivetrain_plant_->mutable_X(1, 0) = 0.0;
+ drivetrain_plant_->mutable_Y() =
+ drivetrain_plant_->C() * drivetrain_plant_->X();
+ last_left_position_ = drivetrain_plant_->Y(0, 0);
+ last_right_position_ = drivetrain_plant_->Y(1, 0);
+ }
+
+ // Returns the position of the drivetrain.
+ double GetLeftPosition() const { return drivetrain_plant_->Y(0, 0); }
+ double GetRightPosition() const { return drivetrain_plant_->Y(1, 0); }
+
+ // Sends out the position queue messages.
+ void SendPositionMessage() {
+ const double left_encoder = GetLeftPosition();
+ const double right_encoder = GetRightPosition();
+
+ ::aos::ScopedMessagePtr<control_loops::DrivetrainQueue::Position> position =
+ my_drivetrain_queue_.position.MakeMessage();
+ position->left_encoder = left_encoder;
+ position->right_encoder = right_encoder;
+ position.Send();
+ }
+
+ // Simulates the drivetrain moving for one timestep.
+ void Simulate() {
+ last_left_position_ = drivetrain_plant_->Y(0, 0);
+ last_right_position_ = drivetrain_plant_->Y(1, 0);
+ EXPECT_TRUE(my_drivetrain_queue_.output.FetchLatest());
+ drivetrain_plant_->mutable_U() << my_drivetrain_queue_.output->left_voltage,
+ my_drivetrain_queue_.output->right_voltage;
+ drivetrain_plant_->Update();
+ }
+
+ ::std::unique_ptr<StateFeedbackPlant<4, 2, 2>> drivetrain_plant_;
+ private:
+ DrivetrainQueue my_drivetrain_queue_;
+ double last_left_position_;
+ double last_right_position_;
+};
+
+class DrivetrainTest : public ::aos::testing::ControlLoopTest {
+ protected:
+ // Create a new instance of the test queue so that it invalidates the queue
+ // that it points to. Otherwise, we will have a pointer to shared memory that
+ // is no longer valid.
+ DrivetrainQueue my_drivetrain_queue_;
+
+ // Create a loop and simulation plant.
+ DrivetrainLoop drivetrain_motor_;
+ DrivetrainSimulation drivetrain_motor_plant_;
+
+ DrivetrainTest() : my_drivetrain_queue_(".frc971.control_loops.drivetrain",
+ 0x8a8dde77,
+ ".frc971.control_loops.drivetrain.goal",
+ ".frc971.control_loops.drivetrain.position",
+ ".frc971.control_loops.drivetrain.output",
+ ".frc971.control_loops.drivetrain.status"),
+ drivetrain_motor_(&my_drivetrain_queue_),
+ drivetrain_motor_plant_() {
+ ::frc971::sensors::gyro_reading.Clear();
+ }
+
+ void VerifyNearGoal() {
+ my_drivetrain_queue_.goal.FetchLatest();
+ my_drivetrain_queue_.position.FetchLatest();
+ EXPECT_NEAR(my_drivetrain_queue_.goal->left_goal,
+ drivetrain_motor_plant_.GetLeftPosition(),
+ 1e-2);
+ EXPECT_NEAR(my_drivetrain_queue_.goal->right_goal,
+ drivetrain_motor_plant_.GetRightPosition(),
+ 1e-2);
+ }
+
+ virtual ~DrivetrainTest() {
+ ::frc971::sensors::gyro_reading.Clear();
+ }
+};
+
+// Tests that the drivetrain converges on a goal.
+TEST_F(DrivetrainTest, ConvergesCorrectly) {
+ my_drivetrain_queue_.goal.MakeWithBuilder().control_loop_driving(true)
+ .left_goal(-1.0)
+ .right_goal(1.0).Send();
+ for (int i = 0; i < 200; ++i) {
+ drivetrain_motor_plant_.SendPositionMessage();
+ drivetrain_motor_.Iterate();
+ drivetrain_motor_plant_.Simulate();
+ SimulateTimestep(true);
+ }
+ VerifyNearGoal();
+}
+
+// Tests that it survives disabling.
+TEST_F(DrivetrainTest, SurvivesDisabling) {
+ my_drivetrain_queue_.goal.MakeWithBuilder().control_loop_driving(true)
+ .left_goal(-1.0)
+ .right_goal(1.0).Send();
+ for (int i = 0; i < 500; ++i) {
+ drivetrain_motor_plant_.SendPositionMessage();
+ drivetrain_motor_.Iterate();
+ drivetrain_motor_plant_.Simulate();
+ if (i > 20 && i < 200) {
+ SimulateTimestep(false);
+ } else {
+ SimulateTimestep(true);
+ }
+ }
+ VerifyNearGoal();
+}
+
+// Tests that never having a goal doesn't break.
+TEST_F(DrivetrainTest, NoGoalStart) {
+ for (int i = 0; i < 20; ++i) {
+ drivetrain_motor_plant_.SendPositionMessage();
+ drivetrain_motor_.Iterate();
+ drivetrain_motor_plant_.Simulate();
+ }
+}
+
+// Tests that never having a goal, but having driver's station messages, doesn't
+// break.
+TEST_F(DrivetrainTest, NoGoalWithRobotState) {
+ for (int i = 0; i < 20; ++i) {
+ drivetrain_motor_plant_.SendPositionMessage();
+ drivetrain_motor_.Iterate();
+ drivetrain_motor_plant_.Simulate();
+ SimulateTimestep(true);
+ }
+}
+
+::aos::controls::HPolytope<2> MakeBox(double x1_min, double x1_max,
+ double x2_min, double x2_max) {
+ Eigen::Matrix<double, 4, 2> box_H;
+ box_H << /*[[*/ 1.0, 0.0 /*]*/,
+ /*[*/-1.0, 0.0 /*]*/,
+ /*[*/ 0.0, 1.0 /*]*/,
+ /*[*/ 0.0,-1.0 /*]]*/;
+ Eigen::Matrix<double, 4, 1> box_k;
+ box_k << /*[[*/ x1_max /*]*/,
+ /*[*/-x1_min /*]*/,
+ /*[*/ x2_max /*]*/,
+ /*[*/-x2_min /*]]*/;
+ ::aos::controls::HPolytope<2> t_poly(box_H, box_k);
+ return t_poly;
+}
+
+class CoerceGoalTest : public ::testing::Test {
+ public:
+ EIGEN_MAKE_ALIGNED_OPERATOR_NEW
+};
+
+// WHOOOHH!
+TEST_F(CoerceGoalTest, Inside) {
+ ::aos::controls::HPolytope<2> box = MakeBox(1, 2, 1, 2);
+
+ Eigen::Matrix<double, 1, 2> K;
+ K << /*[[*/ 1, -1 /*]]*/;
+
+ Eigen::Matrix<double, 2, 1> R;
+ R << /*[[*/ 1.5, 1.5 /*]]*/;
+
+ Eigen::Matrix<double, 2, 1> output =
+ ::frc971::control_loops::CoerceGoal(box, K, 0, R);
+
+ EXPECT_EQ(R(0, 0), output(0, 0));
+ EXPECT_EQ(R(1, 0), output(1, 0));
+}
+
+TEST_F(CoerceGoalTest, Outside_Inside_Intersect) {
+ ::aos::controls::HPolytope<2> box = MakeBox(1, 2, 1, 2);
+
+ Eigen::Matrix<double, 1, 2> K;
+ K << 1, -1;
+
+ Eigen::Matrix<double, 2, 1> R;
+ R << 5, 5;
+
+ Eigen::Matrix<double, 2, 1> output =
+ ::frc971::control_loops::CoerceGoal(box, K, 0, R);
+
+ EXPECT_EQ(2.0, output(0, 0));
+ EXPECT_EQ(2.0, output(1, 0));
+}
+
+TEST_F(CoerceGoalTest, Outside_Inside_no_Intersect) {
+ ::aos::controls::HPolytope<2> box = MakeBox(3, 4, 1, 2);
+
+ Eigen::Matrix<double, 1, 2> K;
+ K << 1, -1;
+
+ Eigen::Matrix<double, 2, 1> R;
+ R << 5, 5;
+
+ Eigen::Matrix<double, 2, 1> output =
+ ::frc971::control_loops::CoerceGoal(box, K, 0, R);
+
+ EXPECT_EQ(3.0, output(0, 0));
+ EXPECT_EQ(2.0, output(1, 0));
+}
+
+TEST_F(CoerceGoalTest, Middle_Of_Edge) {
+ ::aos::controls::HPolytope<2> box = MakeBox(0, 4, 1, 2);
+
+ Eigen::Matrix<double, 1, 2> K;
+ K << -1, 1;
+
+ Eigen::Matrix<double, 2, 1> R;
+ R << 5, 5;
+
+ Eigen::Matrix<double, 2, 1> output =
+ ::frc971::control_loops::CoerceGoal(box, K, 0, R);
+
+ EXPECT_EQ(2.0, output(0, 0));
+ EXPECT_EQ(2.0, output(1, 0));
+}
+
+TEST_F(CoerceGoalTest, PerpendicularLine) {
+ ::aos::controls::HPolytope<2> box = MakeBox(1, 2, 1, 2);
+
+ Eigen::Matrix<double, 1, 2> K;
+ K << 1, 1;
+
+ Eigen::Matrix<double, 2, 1> R;
+ R << 5, 5;
+
+ Eigen::Matrix<double, 2, 1> output =
+ ::frc971::control_loops::CoerceGoal(box, K, 0, R);
+
+ EXPECT_EQ(1.0, output(0, 0));
+ EXPECT_EQ(1.0, output(1, 0));
+}
+
+} // namespace testing
+} // namespace control_loops
+} // namespace frc971
diff --git a/y2015/control_loops/drivetrain/drivetrain_main.cc b/y2015/control_loops/drivetrain/drivetrain_main.cc
new file mode 100644
index 0000000..10a50f3
--- /dev/null
+++ b/y2015/control_loops/drivetrain/drivetrain_main.cc
@@ -0,0 +1,11 @@
+#include "y2015/control_loops/drivetrain/drivetrain.h"
+
+#include "aos/linux_code/init.h"
+
+int main() {
+ ::aos::Init();
+ frc971::control_loops::DrivetrainLoop drivetrain;
+ drivetrain.Run();
+ ::aos::Cleanup();
+ return 0;
+}
diff --git a/y2015/control_loops/drivetrain/polydrivetrain_cim_plant.cc b/y2015/control_loops/drivetrain/polydrivetrain_cim_plant.cc
new file mode 100644
index 0000000..36ebb59
--- /dev/null
+++ b/y2015/control_loops/drivetrain/polydrivetrain_cim_plant.cc
@@ -0,0 +1,49 @@
+#include "y2015/control_loops/drivetrain/polydrivetrain_cim_plant.h"
+
+#include <vector>
+
+#include "frc971/control_loops/state_feedback_loop.h"
+
+namespace frc971 {
+namespace control_loops {
+
+StateFeedbackPlantCoefficients<1, 1, 1> MakeCIMPlantCoefficients() {
+ Eigen::Matrix<double, 1, 1> A;
+ A << 0.783924473544;
+ Eigen::Matrix<double, 1, 1> B;
+ B << 8.94979586973;
+ Eigen::Matrix<double, 1, 1> C;
+ C << 1;
+ Eigen::Matrix<double, 1, 1> D;
+ D << 0;
+ Eigen::Matrix<double, 1, 1> U_max;
+ U_max << 12.0;
+ Eigen::Matrix<double, 1, 1> U_min;
+ U_min << -12.0;
+ return StateFeedbackPlantCoefficients<1, 1, 1>(A, B, C, D, U_max, U_min);
+}
+
+StateFeedbackController<1, 1, 1> MakeCIMController() {
+ Eigen::Matrix<double, 1, 1> L;
+ L << 0.773924473544;
+ Eigen::Matrix<double, 1, 1> K;
+ K << 0.086473980503;
+ Eigen::Matrix<double, 1, 1> A_inv;
+ A_inv << 1.2756330919;
+ return StateFeedbackController<1, 1, 1>(L, K, A_inv, MakeCIMPlantCoefficients());
+}
+
+StateFeedbackPlant<1, 1, 1> MakeCIMPlant() {
+ ::std::vector< ::std::unique_ptr<StateFeedbackPlantCoefficients<1, 1, 1>>> plants(1);
+ plants[0] = ::std::unique_ptr<StateFeedbackPlantCoefficients<1, 1, 1>>(new StateFeedbackPlantCoefficients<1, 1, 1>(MakeCIMPlantCoefficients()));
+ return StateFeedbackPlant<1, 1, 1>(&plants);
+}
+
+StateFeedbackLoop<1, 1, 1> MakeCIMLoop() {
+ ::std::vector< ::std::unique_ptr<StateFeedbackController<1, 1, 1>>> controllers(1);
+ controllers[0] = ::std::unique_ptr<StateFeedbackController<1, 1, 1>>(new StateFeedbackController<1, 1, 1>(MakeCIMController()));
+ return StateFeedbackLoop<1, 1, 1>(&controllers);
+}
+
+} // namespace control_loops
+} // namespace frc971
diff --git a/y2015/control_loops/drivetrain/polydrivetrain_cim_plant.h b/y2015/control_loops/drivetrain/polydrivetrain_cim_plant.h
new file mode 100644
index 0000000..1c445a7
--- /dev/null
+++ b/y2015/control_loops/drivetrain/polydrivetrain_cim_plant.h
@@ -0,0 +1,20 @@
+#ifndef Y2015_CONTROL_LOOPS_DRIVETRAIN_POLYDRIVETRAIN_CIM_PLANT_H_
+#define Y2015_CONTROL_LOOPS_DRIVETRAIN_POLYDRIVETRAIN_CIM_PLANT_H_
+
+#include "frc971/control_loops/state_feedback_loop.h"
+
+namespace frc971 {
+namespace control_loops {
+
+StateFeedbackPlantCoefficients<1, 1, 1> MakeCIMPlantCoefficients();
+
+StateFeedbackController<1, 1, 1> MakeCIMController();
+
+StateFeedbackPlant<1, 1, 1> MakeCIMPlant();
+
+StateFeedbackLoop<1, 1, 1> MakeCIMLoop();
+
+} // namespace control_loops
+} // namespace frc971
+
+#endif // Y2015_CONTROL_LOOPS_DRIVETRAIN_POLYDRIVETRAIN_CIM_PLANT_H_
diff --git a/y2015/control_loops/drivetrain/polydrivetrain_dog_motor_plant.cc b/y2015/control_loops/drivetrain/polydrivetrain_dog_motor_plant.cc
new file mode 100644
index 0000000..811991c
--- /dev/null
+++ b/y2015/control_loops/drivetrain/polydrivetrain_dog_motor_plant.cc
@@ -0,0 +1,133 @@
+#include "y2015/control_loops/drivetrain/polydrivetrain_dog_motor_plant.h"
+
+#include <vector>
+
+#include "frc971/control_loops/state_feedback_loop.h"
+
+namespace frc971 {
+namespace control_loops {
+
+StateFeedbackPlantCoefficients<2, 2, 2> MakeVelocityDrivetrainLowLowPlantCoefficients() {
+ Eigen::Matrix<double, 2, 2> A;
+ A << 0.955499400541, 0.00962691403749, 0.00962691403749, 0.955499400541;
+ Eigen::Matrix<double, 2, 2> B;
+ B << 0.0164714763931, -0.00356331126397, -0.00356331126397, 0.0164714763931;
+ Eigen::Matrix<double, 2, 2> C;
+ C << 1.0, 0.0, 0.0, 1.0;
+ Eigen::Matrix<double, 2, 2> D;
+ D << 0.0, 0.0, 0.0, 0.0;
+ Eigen::Matrix<double, 2, 1> U_max;
+ U_max << 12.0, 12.0;
+ Eigen::Matrix<double, 2, 1> U_min;
+ U_min << -12.0, -12.0;
+ return StateFeedbackPlantCoefficients<2, 2, 2>(A, B, C, D, U_max, U_min);
+}
+
+StateFeedbackPlantCoefficients<2, 2, 2> MakeVelocityDrivetrainLowHighPlantCoefficients() {
+ Eigen::Matrix<double, 2, 2> A;
+ A << 0.955499400541, 0.00962691403749, 0.00962691403749, 0.955499400541;
+ Eigen::Matrix<double, 2, 2> B;
+ B << 0.0164714763931, -0.00356331126397, -0.00356331126397, 0.0164714763931;
+ Eigen::Matrix<double, 2, 2> C;
+ C << 1.0, 0.0, 0.0, 1.0;
+ Eigen::Matrix<double, 2, 2> D;
+ D << 0.0, 0.0, 0.0, 0.0;
+ Eigen::Matrix<double, 2, 1> U_max;
+ U_max << 12.0, 12.0;
+ Eigen::Matrix<double, 2, 1> U_min;
+ U_min << -12.0, -12.0;
+ return StateFeedbackPlantCoefficients<2, 2, 2>(A, B, C, D, U_max, U_min);
+}
+
+StateFeedbackPlantCoefficients<2, 2, 2> MakeVelocityDrivetrainHighLowPlantCoefficients() {
+ Eigen::Matrix<double, 2, 2> A;
+ A << 0.955499400541, 0.00962691403749, 0.00962691403749, 0.955499400541;
+ Eigen::Matrix<double, 2, 2> B;
+ B << 0.0164714763931, -0.00356331126397, -0.00356331126397, 0.0164714763931;
+ Eigen::Matrix<double, 2, 2> C;
+ C << 1.0, 0.0, 0.0, 1.0;
+ Eigen::Matrix<double, 2, 2> D;
+ D << 0.0, 0.0, 0.0, 0.0;
+ Eigen::Matrix<double, 2, 1> U_max;
+ U_max << 12.0, 12.0;
+ Eigen::Matrix<double, 2, 1> U_min;
+ U_min << -12.0, -12.0;
+ return StateFeedbackPlantCoefficients<2, 2, 2>(A, B, C, D, U_max, U_min);
+}
+
+StateFeedbackPlantCoefficients<2, 2, 2> MakeVelocityDrivetrainHighHighPlantCoefficients() {
+ Eigen::Matrix<double, 2, 2> A;
+ A << 0.955499400541, 0.00962691403749, 0.00962691403749, 0.955499400541;
+ Eigen::Matrix<double, 2, 2> B;
+ B << 0.0164714763931, -0.00356331126397, -0.00356331126397, 0.0164714763931;
+ Eigen::Matrix<double, 2, 2> C;
+ C << 1.0, 0.0, 0.0, 1.0;
+ Eigen::Matrix<double, 2, 2> D;
+ D << 0.0, 0.0, 0.0, 0.0;
+ Eigen::Matrix<double, 2, 1> U_max;
+ U_max << 12.0, 12.0;
+ Eigen::Matrix<double, 2, 1> U_min;
+ U_min << -12.0, -12.0;
+ return StateFeedbackPlantCoefficients<2, 2, 2>(A, B, C, D, U_max, U_min);
+}
+
+StateFeedbackController<2, 2, 2> MakeVelocityDrivetrainLowLowController() {
+ Eigen::Matrix<double, 2, 2> L;
+ L << 0.935499400541, 0.00962691403749, 0.00962691403749, 0.935499400541;
+ Eigen::Matrix<double, 2, 2> K;
+ K << 22.7750288573, 5.51143253556, 5.51143253556, 22.7750288573;
+ Eigen::Matrix<double, 2, 2> A_inv;
+ A_inv << 1.04667938127, -0.010545576923, -0.010545576923, 1.04667938127;
+ return StateFeedbackController<2, 2, 2>(L, K, A_inv, MakeVelocityDrivetrainLowLowPlantCoefficients());
+}
+
+StateFeedbackController<2, 2, 2> MakeVelocityDrivetrainLowHighController() {
+ Eigen::Matrix<double, 2, 2> L;
+ L << 0.935499400541, 0.00962691403749, 0.00962691403749, 0.935499400541;
+ Eigen::Matrix<double, 2, 2> K;
+ K << 22.7750288573, 5.51143253556, 5.51143253556, 22.7750288573;
+ Eigen::Matrix<double, 2, 2> A_inv;
+ A_inv << 1.04667938127, -0.010545576923, -0.010545576923, 1.04667938127;
+ return StateFeedbackController<2, 2, 2>(L, K, A_inv, MakeVelocityDrivetrainLowHighPlantCoefficients());
+}
+
+StateFeedbackController<2, 2, 2> MakeVelocityDrivetrainHighLowController() {
+ Eigen::Matrix<double, 2, 2> L;
+ L << 0.935499400541, 0.00962691403749, 0.00962691403749, 0.935499400541;
+ Eigen::Matrix<double, 2, 2> K;
+ K << 22.7750288573, 5.51143253556, 5.51143253556, 22.7750288573;
+ Eigen::Matrix<double, 2, 2> A_inv;
+ A_inv << 1.04667938127, -0.010545576923, -0.010545576923, 1.04667938127;
+ return StateFeedbackController<2, 2, 2>(L, K, A_inv, MakeVelocityDrivetrainHighLowPlantCoefficients());
+}
+
+StateFeedbackController<2, 2, 2> MakeVelocityDrivetrainHighHighController() {
+ Eigen::Matrix<double, 2, 2> L;
+ L << 0.935499400541, 0.00962691403749, 0.00962691403749, 0.935499400541;
+ Eigen::Matrix<double, 2, 2> K;
+ K << 22.7750288573, 5.51143253556, 5.51143253556, 22.7750288573;
+ Eigen::Matrix<double, 2, 2> A_inv;
+ A_inv << 1.04667938127, -0.010545576923, -0.010545576923, 1.04667938127;
+ return StateFeedbackController<2, 2, 2>(L, K, A_inv, MakeVelocityDrivetrainHighHighPlantCoefficients());
+}
+
+StateFeedbackPlant<2, 2, 2> MakeVelocityDrivetrainPlant() {
+ ::std::vector< ::std::unique_ptr<StateFeedbackPlantCoefficients<2, 2, 2>>> plants(4);
+ plants[0] = ::std::unique_ptr<StateFeedbackPlantCoefficients<2, 2, 2>>(new StateFeedbackPlantCoefficients<2, 2, 2>(MakeVelocityDrivetrainLowLowPlantCoefficients()));
+ plants[1] = ::std::unique_ptr<StateFeedbackPlantCoefficients<2, 2, 2>>(new StateFeedbackPlantCoefficients<2, 2, 2>(MakeVelocityDrivetrainLowHighPlantCoefficients()));
+ plants[2] = ::std::unique_ptr<StateFeedbackPlantCoefficients<2, 2, 2>>(new StateFeedbackPlantCoefficients<2, 2, 2>(MakeVelocityDrivetrainHighLowPlantCoefficients()));
+ plants[3] = ::std::unique_ptr<StateFeedbackPlantCoefficients<2, 2, 2>>(new StateFeedbackPlantCoefficients<2, 2, 2>(MakeVelocityDrivetrainHighHighPlantCoefficients()));
+ return StateFeedbackPlant<2, 2, 2>(&plants);
+}
+
+StateFeedbackLoop<2, 2, 2> MakeVelocityDrivetrainLoop() {
+ ::std::vector< ::std::unique_ptr<StateFeedbackController<2, 2, 2>>> controllers(4);
+ controllers[0] = ::std::unique_ptr<StateFeedbackController<2, 2, 2>>(new StateFeedbackController<2, 2, 2>(MakeVelocityDrivetrainLowLowController()));
+ controllers[1] = ::std::unique_ptr<StateFeedbackController<2, 2, 2>>(new StateFeedbackController<2, 2, 2>(MakeVelocityDrivetrainLowHighController()));
+ controllers[2] = ::std::unique_ptr<StateFeedbackController<2, 2, 2>>(new StateFeedbackController<2, 2, 2>(MakeVelocityDrivetrainHighLowController()));
+ controllers[3] = ::std::unique_ptr<StateFeedbackController<2, 2, 2>>(new StateFeedbackController<2, 2, 2>(MakeVelocityDrivetrainHighHighController()));
+ return StateFeedbackLoop<2, 2, 2>(&controllers);
+}
+
+} // namespace control_loops
+} // namespace frc971
diff --git a/y2015/control_loops/drivetrain/polydrivetrain_dog_motor_plant.h b/y2015/control_loops/drivetrain/polydrivetrain_dog_motor_plant.h
new file mode 100644
index 0000000..5a99caf
--- /dev/null
+++ b/y2015/control_loops/drivetrain/polydrivetrain_dog_motor_plant.h
@@ -0,0 +1,32 @@
+#ifndef Y2015_CONTROL_LOOPS_DRIVETRAIN_POLYDRIVETRAIN_DOG_MOTOR_PLANT_H_
+#define Y2015_CONTROL_LOOPS_DRIVETRAIN_POLYDRIVETRAIN_DOG_MOTOR_PLANT_H_
+
+#include "frc971/control_loops/state_feedback_loop.h"
+
+namespace frc971 {
+namespace control_loops {
+
+StateFeedbackPlantCoefficients<2, 2, 2> MakeVelocityDrivetrainLowLowPlantCoefficients();
+
+StateFeedbackController<2, 2, 2> MakeVelocityDrivetrainLowLowController();
+
+StateFeedbackPlantCoefficients<2, 2, 2> MakeVelocityDrivetrainLowHighPlantCoefficients();
+
+StateFeedbackController<2, 2, 2> MakeVelocityDrivetrainLowHighController();
+
+StateFeedbackPlantCoefficients<2, 2, 2> MakeVelocityDrivetrainHighLowPlantCoefficients();
+
+StateFeedbackController<2, 2, 2> MakeVelocityDrivetrainHighLowController();
+
+StateFeedbackPlantCoefficients<2, 2, 2> MakeVelocityDrivetrainHighHighPlantCoefficients();
+
+StateFeedbackController<2, 2, 2> MakeVelocityDrivetrainHighHighController();
+
+StateFeedbackPlant<2, 2, 2> MakeVelocityDrivetrainPlant();
+
+StateFeedbackLoop<2, 2, 2> MakeVelocityDrivetrainLoop();
+
+} // namespace control_loops
+} // namespace frc971
+
+#endif // Y2015_CONTROL_LOOPS_DRIVETRAIN_POLYDRIVETRAIN_DOG_MOTOR_PLANT_H_
diff --git a/y2015/control_loops/drivetrain/replay_drivetrain.cc b/y2015/control_loops/drivetrain/replay_drivetrain.cc
new file mode 100644
index 0000000..030a945
--- /dev/null
+++ b/y2015/control_loops/drivetrain/replay_drivetrain.cc
@@ -0,0 +1,24 @@
+#include "aos/common/controls/replay_control_loop.h"
+#include "aos/linux_code/init.h"
+
+#include "y2015/control_loops/drivetrain/drivetrain.q.h"
+
+// Reads one or more log files and sends out all the queue messages (in the
+// correct order and at the correct time) to feed a "live" drivetrain process.
+
+int main(int argc, char **argv) {
+ if (argc <= 1) {
+ fprintf(stderr, "Need at least one file to replay!\n");
+ return EXIT_FAILURE;
+ }
+
+ ::aos::InitNRT();
+
+ ::aos::controls::ControlLoopReplayer<::frc971::control_loops::DrivetrainQueue>
+ replayer(&::frc971::control_loops::drivetrain_queue, "drivetrain");
+ for (int i = 1; i < argc; ++i) {
+ replayer.ProcessFile(argv[i]);
+ }
+
+ ::aos::Cleanup();
+}
diff --git a/y2015/control_loops/fridge/arm_motor_plant.cc b/y2015/control_loops/fridge/arm_motor_plant.cc
new file mode 100644
index 0000000..6e3205a
--- /dev/null
+++ b/y2015/control_loops/fridge/arm_motor_plant.cc
@@ -0,0 +1,49 @@
+#include "y2015/control_loops/fridge/arm_motor_plant.h"
+
+#include <vector>
+
+#include "frc971/control_loops/state_feedback_loop.h"
+
+namespace frc971 {
+namespace control_loops {
+
+StateFeedbackPlantCoefficients<4, 2, 2> MakeArmPlantCoefficients() {
+ Eigen::Matrix<double, 4, 4> A;
+ A << 1.0, 0.00479642025454, 0.0, 0.0, 0.0, 0.919688585028, 0.0, 0.0, 0.0, 0.0, 0.999539771613, 0.00479566382645, 0.0, 0.0, -0.18154390621, 0.919241022297;
+ Eigen::Matrix<double, 4, 2> B;
+ B << 2.46496779984e-05, 2.46496779984e-05, 0.00972420175808, 0.00972420175808, 2.46477449538e-05, -2.46477449538e-05, 0.00972266818532, -0.00972266818532;
+ Eigen::Matrix<double, 2, 4> C;
+ C << 1, 0, 1, 0, 1, 0, -1, 0;
+ Eigen::Matrix<double, 2, 2> D;
+ D << 0, 0, 0, 0;
+ Eigen::Matrix<double, 2, 1> U_max;
+ U_max << 12.0, 12.0;
+ Eigen::Matrix<double, 2, 1> U_min;
+ U_min << -12.0, -12.0;
+ return StateFeedbackPlantCoefficients<4, 2, 2>(A, B, C, D, U_max, U_min);
+}
+
+StateFeedbackController<4, 2, 2> MakeArmController() {
+ Eigen::Matrix<double, 4, 2> L;
+ L << 0.759844292514, 0.759844292514, 54.2541762188, 54.2541762188, 0.759390396955, -0.759390396955, 54.1048167043, -54.1048167043;
+ Eigen::Matrix<double, 2, 4> K;
+ K << 320.979606093, 21.0129517955, 884.233784759, 36.3637782119, 320.979606095, 21.0129517956, -884.233784749, -36.3637782119;
+ Eigen::Matrix<double, 4, 4> A_inv;
+ A_inv << 1.0, -0.00521526561559, 0.0, 0.0, 0.0, 1.08732457517, 0.0, 0.0, 0.0, 0.0, 0.999513354044, -0.00521444313273, 0.0, 0.0, 0.197397150694, 1.08682415753;
+ return StateFeedbackController<4, 2, 2>(L, K, A_inv, MakeArmPlantCoefficients());
+}
+
+StateFeedbackPlant<4, 2, 2> MakeArmPlant() {
+ ::std::vector< ::std::unique_ptr<StateFeedbackPlantCoefficients<4, 2, 2>>> plants(1);
+ plants[0] = ::std::unique_ptr<StateFeedbackPlantCoefficients<4, 2, 2>>(new StateFeedbackPlantCoefficients<4, 2, 2>(MakeArmPlantCoefficients()));
+ return StateFeedbackPlant<4, 2, 2>(&plants);
+}
+
+StateFeedbackLoop<4, 2, 2> MakeArmLoop() {
+ ::std::vector< ::std::unique_ptr<StateFeedbackController<4, 2, 2>>> controllers(1);
+ controllers[0] = ::std::unique_ptr<StateFeedbackController<4, 2, 2>>(new StateFeedbackController<4, 2, 2>(MakeArmController()));
+ return StateFeedbackLoop<4, 2, 2>(&controllers);
+}
+
+} // namespace control_loops
+} // namespace frc971
diff --git a/y2015/control_loops/fridge/arm_motor_plant.h b/y2015/control_loops/fridge/arm_motor_plant.h
new file mode 100644
index 0000000..3bf8d09
--- /dev/null
+++ b/y2015/control_loops/fridge/arm_motor_plant.h
@@ -0,0 +1,20 @@
+#ifndef Y2015_CONTROL_LOOPS_FRIDGE_ARM_MOTOR_PLANT_H_
+#define Y2015_CONTROL_LOOPS_FRIDGE_ARM_MOTOR_PLANT_H_
+
+#include "frc971/control_loops/state_feedback_loop.h"
+
+namespace frc971 {
+namespace control_loops {
+
+StateFeedbackPlantCoefficients<4, 2, 2> MakeArmPlantCoefficients();
+
+StateFeedbackController<4, 2, 2> MakeArmController();
+
+StateFeedbackPlant<4, 2, 2> MakeArmPlant();
+
+StateFeedbackLoop<4, 2, 2> MakeArmLoop();
+
+} // namespace control_loops
+} // namespace frc971
+
+#endif // Y2015_CONTROL_LOOPS_FRIDGE_ARM_MOTOR_PLANT_H_
diff --git a/y2015/control_loops/fridge/elevator_motor_plant.cc b/y2015/control_loops/fridge/elevator_motor_plant.cc
new file mode 100644
index 0000000..995d838
--- /dev/null
+++ b/y2015/control_loops/fridge/elevator_motor_plant.cc
@@ -0,0 +1,49 @@
+#include "y2015/control_loops/fridge/elevator_motor_plant.h"
+
+#include <vector>
+
+#include "frc971/control_loops/state_feedback_loop.h"
+
+namespace frc971 {
+namespace control_loops {
+
+StateFeedbackPlantCoefficients<4, 2, 2> MakeElevatorPlantCoefficients() {
+ Eigen::Matrix<double, 4, 4> A;
+ A << 1.0, 0.00435668193669, 0.0, 0.0, 0.0, 0.754212786054, 0.0, 0.0, 0.0, 0.0, 0.997194498569, 0.00435222083164, 0.0, 0.0, -1.07131589702, 0.751658962986;
+ Eigen::Matrix<double, 4, 2> B;
+ B << 3.82580284276e-05, 3.82580284276e-05, 0.0146169286307, 0.0146169286307, 3.82387898839e-05, -3.82387898839e-05, 0.0146019613563, -0.0146019613563;
+ Eigen::Matrix<double, 2, 4> C;
+ C << 1, 0, 1, 0, 1, 0, -1, 0;
+ Eigen::Matrix<double, 2, 2> D;
+ D << 0, 0, 0, 0;
+ Eigen::Matrix<double, 2, 1> U_max;
+ U_max << 12.0, 12.0;
+ Eigen::Matrix<double, 2, 1> U_min;
+ U_min << -12.0, -12.0;
+ return StateFeedbackPlantCoefficients<4, 2, 2>(A, B, C, D, U_max, U_min);
+}
+
+StateFeedbackController<4, 2, 2> MakeElevatorController() {
+ Eigen::Matrix<double, 4, 2> L;
+ L << 0.677106393027, 0.677106393027, 35.5375738607, 35.5375738607, 0.674426730777, -0.674426730777, 34.7138874344, -34.7138874344;
+ Eigen::Matrix<double, 2, 4> K;
+ K << 321.310606763, 11.7674534233, 601.047935717, 12.6977148843, 321.310606764, 11.7674534233, -601.047935716, -12.6977148843;
+ Eigen::Matrix<double, 4, 4> A_inv;
+ A_inv << 1.0, -0.00577646258091, 0.0, 0.0, 0.0, 1.32588576923, 0.0, 0.0, 0.0, 0.0, 0.996613922337, -0.00577054766522, 0.0, 0.0, 1.42044250221, 1.32216599481;
+ return StateFeedbackController<4, 2, 2>(L, K, A_inv, MakeElevatorPlantCoefficients());
+}
+
+StateFeedbackPlant<4, 2, 2> MakeElevatorPlant() {
+ ::std::vector< ::std::unique_ptr<StateFeedbackPlantCoefficients<4, 2, 2>>> plants(1);
+ plants[0] = ::std::unique_ptr<StateFeedbackPlantCoefficients<4, 2, 2>>(new StateFeedbackPlantCoefficients<4, 2, 2>(MakeElevatorPlantCoefficients()));
+ return StateFeedbackPlant<4, 2, 2>(&plants);
+}
+
+StateFeedbackLoop<4, 2, 2> MakeElevatorLoop() {
+ ::std::vector< ::std::unique_ptr<StateFeedbackController<4, 2, 2>>> controllers(1);
+ controllers[0] = ::std::unique_ptr<StateFeedbackController<4, 2, 2>>(new StateFeedbackController<4, 2, 2>(MakeElevatorController()));
+ return StateFeedbackLoop<4, 2, 2>(&controllers);
+}
+
+} // namespace control_loops
+} // namespace frc971
diff --git a/y2015/control_loops/fridge/elevator_motor_plant.h b/y2015/control_loops/fridge/elevator_motor_plant.h
new file mode 100644
index 0000000..e68e6d7
--- /dev/null
+++ b/y2015/control_loops/fridge/elevator_motor_plant.h
@@ -0,0 +1,20 @@
+#ifndef Y2015_CONTROL_LOOPS_FRIDGE_ELEVATOR_MOTOR_PLANT_H_
+#define Y2015_CONTROL_LOOPS_FRIDGE_ELEVATOR_MOTOR_PLANT_H_
+
+#include "frc971/control_loops/state_feedback_loop.h"
+
+namespace frc971 {
+namespace control_loops {
+
+StateFeedbackPlantCoefficients<4, 2, 2> MakeElevatorPlantCoefficients();
+
+StateFeedbackController<4, 2, 2> MakeElevatorController();
+
+StateFeedbackPlant<4, 2, 2> MakeElevatorPlant();
+
+StateFeedbackLoop<4, 2, 2> MakeElevatorLoop();
+
+} // namespace control_loops
+} // namespace frc971
+
+#endif // Y2015_CONTROL_LOOPS_FRIDGE_ELEVATOR_MOTOR_PLANT_H_
diff --git a/y2015/control_loops/fridge/fridge.cc b/y2015/control_loops/fridge/fridge.cc
new file mode 100644
index 0000000..083baf2
--- /dev/null
+++ b/y2015/control_loops/fridge/fridge.cc
@@ -0,0 +1,720 @@
+#include "y2015/control_loops/fridge/fridge.h"
+
+#include <cmath>
+
+#include "aos/common/controls/control_loops.q.h"
+#include "aos/common/logging/logging.h"
+
+#include "y2015/control_loops/fridge/elevator_motor_plant.h"
+#include "y2015/control_loops/fridge/integral_arm_plant.h"
+#include "frc971/control_loops/voltage_cap/voltage_cap.h"
+#include "frc971/zeroing/zeroing.h"
+
+#include "y2015/constants.h"
+
+namespace frc971 {
+namespace control_loops {
+
+namespace {
+constexpr double kZeroingVoltage = 4.0;
+constexpr double kElevatorZeroingVelocity = 0.10;
+// What speed we move to our safe height at.
+constexpr double kElevatorSafeHeightVelocity = 0.3;
+constexpr double kArmZeroingVelocity = 0.20;
+} // namespace
+
+template <int S>
+void CappedStateFeedbackLoop<S>::CapU() {
+ VoltageCap(max_voltage_, this->U(0, 0), this->U(1, 0), &this->mutable_U(0, 0),
+ &this->mutable_U(1, 0));
+}
+
+template <int S>
+Eigen::Matrix<double, 2, 1>
+CappedStateFeedbackLoop<S>::UnsaturateOutputGoalChange() {
+ // Compute the K matrix used to compensate for position errors.
+ Eigen::Matrix<double, 2, 2> Kp;
+ Kp.setZero();
+ Kp.col(0) = this->K().col(0);
+ Kp.col(1) = this->K().col(2);
+
+ Eigen::Matrix<double, 2, 2> Kp_inv = Kp.inverse();
+
+ // Compute how much we need to change R in order to achieve the change in U
+ // that was observed.
+ Eigen::Matrix<double, 2, 1> deltaR =
+ -Kp_inv * (this->U_uncapped() - this->U());
+ return deltaR;
+}
+
+Fridge::Fridge(control_loops::FridgeQueue *fridge)
+ : aos::controls::ControlLoop<control_loops::FridgeQueue>(fridge),
+ arm_loop_(new CappedStateFeedbackLoop<5>(
+ StateFeedbackLoop<5, 2, 2>(MakeIntegralArmLoop()))),
+ elevator_loop_(new CappedStateFeedbackLoop<4>(
+ StateFeedbackLoop<4, 2, 2>(MakeElevatorLoop()))),
+ left_arm_estimator_(constants::GetValues().fridge.left_arm_zeroing),
+ right_arm_estimator_(constants::GetValues().fridge.right_arm_zeroing),
+ left_elevator_estimator_(constants::GetValues().fridge.left_elev_zeroing),
+ right_elevator_estimator_(
+ constants::GetValues().fridge.right_elev_zeroing),
+ last_profiling_type_(ProfilingType::ANGLE_HEIGHT_PROFILING),
+ kinematics_(constants::GetValues().fridge.arm_length,
+ constants::GetValues().fridge.elevator.upper_limit,
+ constants::GetValues().fridge.elevator.lower_limit,
+ constants::GetValues().fridge.arm.upper_limit,
+ constants::GetValues().fridge.arm.lower_limit),
+ arm_profile_(::aos::controls::kLoopFrequency),
+ elevator_profile_(::aos::controls::kLoopFrequency),
+ x_profile_(::aos::controls::kLoopFrequency),
+ y_profile_(::aos::controls::kLoopFrequency) {}
+
+void Fridge::UpdateZeroingState() {
+ if (left_elevator_estimator_.offset_ratio_ready() < 1.0 ||
+ right_elevator_estimator_.offset_ratio_ready() < 1.0 ||
+ left_arm_estimator_.offset_ratio_ready() < 1.0 ||
+ right_arm_estimator_.offset_ratio_ready() < 1.0) {
+ state_ = INITIALIZING;
+ } else if (!left_elevator_estimator_.zeroed() ||
+ !right_elevator_estimator_.zeroed()) {
+ state_ = ZEROING_ELEVATOR;
+ } else if (!left_arm_estimator_.zeroed() || !right_arm_estimator_.zeroed()) {
+ state_ = ZEROING_ARM;
+ } else {
+ state_ = RUNNING;
+ }
+}
+
+void Fridge::Correct() {
+ {
+ Eigen::Matrix<double, 2, 1> Y;
+ Y << left_elevator(), right_elevator();
+ elevator_loop_->Correct(Y);
+ }
+
+ {
+ Eigen::Matrix<double, 2, 1> Y;
+ Y << left_arm(), right_arm();
+ arm_loop_->Correct(Y);
+ }
+}
+
+void Fridge::SetElevatorOffset(double left_offset, double right_offset) {
+ LOG(INFO, "Changing Elevator offset from %f, %f to %f, %f\n",
+ left_elevator_offset_, right_elevator_offset_, left_offset, right_offset);
+ double left_doffset = left_offset - left_elevator_offset_;
+ double right_doffset = right_offset - right_elevator_offset_;
+
+ // Adjust the average height and height difference between the two sides.
+ // The derivatives of both should not need to be updated since the speeds
+ // haven't changed.
+ // The height difference is calculated as left - right, not right - left.
+ elevator_loop_->mutable_X_hat(0, 0) += (left_doffset + right_doffset) / 2;
+ elevator_loop_->mutable_X_hat(2, 0) += (left_doffset - right_doffset) / 2;
+
+ // Modify the zeroing goal.
+ elevator_goal_ += (left_doffset + right_doffset) / 2;
+
+ // Update the cached offset values to the actual values.
+ left_elevator_offset_ = left_offset;
+ right_elevator_offset_ = right_offset;
+}
+
+void Fridge::SetArmOffset(double left_offset, double right_offset) {
+ LOG(INFO, "Changing Arm offset from %f, %f to %f, %f\n", left_arm_offset_,
+ right_arm_offset_, left_offset, right_offset);
+ double left_doffset = left_offset - left_arm_offset_;
+ double right_doffset = right_offset - right_arm_offset_;
+
+ // Adjust the average angle and angle difference between the two sides.
+ // The derivatives of both should not need to be updated since the speeds
+ // haven't changed.
+ arm_loop_->mutable_X_hat(0, 0) += (left_doffset + right_doffset) / 2;
+ arm_loop_->mutable_X_hat(2, 0) += (left_doffset - right_doffset) / 2;
+
+ // Modify the zeroing goal.
+ arm_goal_ += (left_doffset + right_doffset) / 2;
+
+ // Update the cached offset values to the actual values.
+ left_arm_offset_ = left_offset;
+ right_arm_offset_ = right_offset;
+}
+
+double Fridge::estimated_left_elevator() {
+ return current_position_.elevator.left.encoder +
+ left_elevator_estimator_.offset();
+}
+double Fridge::estimated_right_elevator() {
+ return current_position_.elevator.right.encoder +
+ right_elevator_estimator_.offset();
+}
+
+double Fridge::estimated_elevator() {
+ return (estimated_left_elevator() + estimated_right_elevator()) / 2.0;
+}
+
+double Fridge::estimated_left_arm() {
+ return current_position_.arm.left.encoder + left_arm_estimator_.offset();
+}
+double Fridge::estimated_right_arm() {
+ return current_position_.arm.right.encoder + right_arm_estimator_.offset();
+}
+double Fridge::estimated_arm() {
+ return (estimated_left_arm() + estimated_right_arm()) / 2.0;
+}
+
+double Fridge::left_elevator() {
+ return current_position_.elevator.left.encoder + left_elevator_offset_;
+}
+double Fridge::right_elevator() {
+ return current_position_.elevator.right.encoder + right_elevator_offset_;
+}
+
+double Fridge::elevator() { return (left_elevator() + right_elevator()) / 2.0; }
+
+double Fridge::left_arm() {
+ return current_position_.arm.left.encoder + left_arm_offset_;
+}
+double Fridge::right_arm() {
+ return current_position_.arm.right.encoder + right_arm_offset_;
+}
+double Fridge::arm() { return (left_arm() + right_arm()) / 2.0; }
+
+double Fridge::elevator_zeroing_velocity() {
+ double average_elevator =
+ (constants::GetValues().fridge.elevator.lower_limit +
+ constants::GetValues().fridge.elevator.upper_limit) /
+ 2.0;
+
+ const double pulse_width = ::std::max(
+ constants::GetValues().fridge.left_elev_zeroing.index_difference,
+ constants::GetValues().fridge.right_elev_zeroing.index_difference);
+
+ if (elevator_zeroing_velocity_ == 0) {
+ if (estimated_elevator() > average_elevator) {
+ elevator_zeroing_velocity_ = -kElevatorZeroingVelocity;
+ } else {
+ elevator_zeroing_velocity_ = kElevatorZeroingVelocity;
+ }
+ } else if (elevator_zeroing_velocity_ > 0 &&
+ estimated_elevator() > average_elevator + 1.1 * pulse_width) {
+ elevator_zeroing_velocity_ = -kElevatorZeroingVelocity;
+ } else if (elevator_zeroing_velocity_ < 0 &&
+ estimated_elevator() < average_elevator - 1.1 * pulse_width) {
+ elevator_zeroing_velocity_ = kElevatorZeroingVelocity;
+ }
+ return elevator_zeroing_velocity_;
+}
+
+double Fridge::UseUnlessZero(double target_value, double default_value) {
+ if (target_value != 0.0) {
+ return target_value;
+ } else {
+ return default_value;
+ }
+}
+
+double Fridge::arm_zeroing_velocity() {
+ const double average_arm = (constants::GetValues().fridge.arm.lower_limit +
+ constants::GetValues().fridge.arm.upper_limit) /
+ 2.0;
+ const double pulse_width = ::std::max(
+ constants::GetValues().fridge.right_arm_zeroing.index_difference,
+ constants::GetValues().fridge.left_arm_zeroing.index_difference);
+
+ if (arm_zeroing_velocity_ == 0) {
+ if (estimated_arm() > average_arm) {
+ arm_zeroing_velocity_ = -kArmZeroingVelocity;
+ } else {
+ arm_zeroing_velocity_ = kArmZeroingVelocity;
+ }
+ } else if (arm_zeroing_velocity_ > 0.0 &&
+ estimated_arm() > average_arm + 1.1 * pulse_width) {
+ arm_zeroing_velocity_ = -kArmZeroingVelocity;
+ } else if (arm_zeroing_velocity_ < 0.0 && estimated_arm() < average_arm) {
+ arm_zeroing_velocity_ = kArmZeroingVelocity;
+ }
+ return arm_zeroing_velocity_;
+}
+
+void Fridge::RunIteration(const control_loops::FridgeQueue::Goal *unsafe_goal,
+ const control_loops::FridgeQueue::Position *position,
+ control_loops::FridgeQueue::Output *output,
+ control_loops::FridgeQueue::Status *status) {
+ if (WasReset()) {
+ LOG(ERROR, "WPILib reset, restarting\n");
+ left_elevator_estimator_.Reset();
+ right_elevator_estimator_.Reset();
+ left_arm_estimator_.Reset();
+ right_arm_estimator_.Reset();
+ state_ = UNINITIALIZED;
+ }
+
+ // Get a reference to the constants struct since we use it so often in this
+ // code.
+ const auto &values = constants::GetValues();
+
+ // Bool to track if we should turn the motors on or not.
+ bool disable = output == nullptr;
+
+ // Save the current position so it can be used easily in the class.
+ current_position_ = *position;
+
+ left_elevator_estimator_.UpdateEstimate(position->elevator.left);
+ right_elevator_estimator_.UpdateEstimate(position->elevator.right);
+ left_arm_estimator_.UpdateEstimate(position->arm.left);
+ right_arm_estimator_.UpdateEstimate(position->arm.right);
+
+ if (state_ != UNINITIALIZED) {
+ Correct();
+ }
+
+ // Zeroing will work as follows:
+ // At startup, record the offset of the two halves of the two subsystems.
+ // Then, start moving the elevator towards the center until both halves are
+ // zeroed.
+ // Then, start moving the claw towards the center until both halves are
+ // zeroed.
+ // Then, done!
+
+ // We'll then need code to do sanity checking on values.
+
+ // Now, we need to figure out which way to go.
+
+ switch (state_) {
+ case UNINITIALIZED:
+ LOG(DEBUG, "Uninitialized\n");
+ // Startup. Assume that we are at the origin everywhere.
+ // This records the encoder offset between the two sides of the elevator.
+ left_elevator_offset_ = -position->elevator.left.encoder;
+ right_elevator_offset_ = -position->elevator.right.encoder;
+ left_arm_offset_ = -position->arm.left.encoder;
+ right_arm_offset_ = -position->arm.right.encoder;
+ elevator_loop_->mutable_X_hat().setZero();
+ arm_loop_->mutable_X_hat().setZero();
+ LOG(INFO, "Initializing arm offsets to %f, %f\n", left_arm_offset_,
+ right_arm_offset_);
+ LOG(INFO, "Initializing elevator offsets to %f, %f\n",
+ left_elevator_offset_, right_elevator_offset_);
+ Correct();
+ state_ = INITIALIZING;
+ disable = true;
+ break;
+
+ case INITIALIZING:
+ LOG(DEBUG, "Waiting for accurate initial position.\n");
+ disable = true;
+ // Update state_ to accurately represent the state of the zeroing
+ // estimators.
+ UpdateZeroingState();
+ if (state_ != INITIALIZING) {
+ // Set the goals to where we are now.
+ elevator_goal_ = elevator();
+ arm_goal_ = arm();
+ }
+ break;
+
+ case ZEROING_ELEVATOR:
+ LOG(DEBUG, "Zeroing elevator\n");
+
+ // Update state_ to accurately represent the state of the zeroing
+ // estimators.
+ UpdateZeroingState();
+ if (left_elevator_estimator_.zeroed() &&
+ right_elevator_estimator_.zeroed()) {
+ SetElevatorOffset(left_elevator_estimator_.offset(),
+ right_elevator_estimator_.offset());
+ LOG(DEBUG, "Zeroed the elevator!\n");
+
+ if (elevator() < values.fridge.arm_zeroing_height &&
+ state_ != INITIALIZING) {
+ // Move the elevator to a safe height before we start zeroing the arm,
+ // so that we don't crash anything.
+ LOG(DEBUG, "Moving elevator to safe height.\n");
+ if (elevator_goal_ < values.fridge.arm_zeroing_height) {
+ elevator_goal_ += kElevatorSafeHeightVelocity *
+ ::aos::controls::kLoopFrequency.ToSeconds();
+ elevator_goal_velocity_ = kElevatorSafeHeightVelocity;
+ state_ = ZEROING_ELEVATOR;
+ } else {
+ // We want it stopped at whatever height it's currently set to.
+ elevator_goal_velocity_ = 0;
+ }
+ }
+ } else if (!disable) {
+ elevator_goal_velocity_ = elevator_zeroing_velocity();
+ elevator_goal_ += elevator_goal_velocity_ *
+ ::aos::controls::kLoopFrequency.ToSeconds();
+ }
+
+ // Bypass motion profiles while we are zeroing.
+ // This is also an important step right after the elevator is zeroed and
+ // we reach into the elevator's state matrix and change it based on the
+ // newly-obtained offset.
+ {
+ Eigen::Matrix<double, 2, 1> current;
+ current.setZero();
+ current << elevator_goal_, elevator_goal_velocity_;
+ elevator_profile_.MoveCurrentState(current);
+ }
+ break;
+
+ case ZEROING_ARM:
+ LOG(DEBUG, "Zeroing the arm\n");
+
+ if (elevator() < values.fridge.arm_zeroing_height - 0.10 ||
+ elevator_goal_ < values.fridge.arm_zeroing_height) {
+ LOG(INFO,
+ "Going back to ZEROING_ELEVATOR until it gets high enough to "
+ "safely zero the arm\n");
+ state_ = ZEROING_ELEVATOR;
+ break;
+ }
+
+ // Update state_ to accurately represent the state of the zeroing
+ // estimators.
+ UpdateZeroingState();
+ if (left_arm_estimator_.zeroed() && right_arm_estimator_.zeroed()) {
+ SetArmOffset(left_arm_estimator_.offset(),
+ right_arm_estimator_.offset());
+ LOG(DEBUG, "Zeroed the arm!\n");
+ } else if (!disable) {
+ arm_goal_velocity_ = arm_zeroing_velocity();
+ arm_goal_ +=
+ arm_goal_velocity_ * ::aos::controls::kLoopFrequency.ToSeconds();
+ }
+
+ // Bypass motion profiles while we are zeroing.
+ // This is also an important step right after the arm is zeroed and
+ // we reach into the arm's state matrix and change it based on the
+ // newly-obtained offset.
+ {
+ Eigen::Matrix<double, 2, 1> current;
+ current.setZero();
+ current << arm_goal_, arm_goal_velocity_;
+ arm_profile_.MoveCurrentState(current);
+ }
+ break;
+
+ case RUNNING:
+ LOG(DEBUG, "Running!\n");
+ if (unsafe_goal) {
+ // Handle the case where we switch between the types of profiling.
+ ProfilingType new_profiling_type =
+ static_cast<ProfilingType>(unsafe_goal->profiling_type);
+
+ if (last_profiling_type_ != new_profiling_type) {
+ // Reset the height/angle profiles.
+ Eigen::Matrix<double, 2, 1> current;
+ current.setZero();
+ current << arm_goal_, arm_goal_velocity_;
+ arm_profile_.MoveCurrentState(current);
+ current << elevator_goal_, elevator_goal_velocity_;
+ elevator_profile_.MoveCurrentState(current);
+
+ // Reset the x/y profiles.
+ aos::util::ElevatorArmKinematics::KinematicResult x_y_result;
+ kinematics_.ForwardKinematic(elevator_goal_, arm_goal_,
+ elevator_goal_velocity_,
+ arm_goal_velocity_, &x_y_result);
+ current << x_y_result.fridge_x, x_y_result.fridge_x_velocity;
+ x_profile_.MoveCurrentState(current);
+ current << x_y_result.fridge_h, x_y_result.fridge_h_velocity;
+ y_profile_.MoveCurrentState(current);
+
+ last_profiling_type_ = new_profiling_type;
+ }
+
+ if (new_profiling_type == ProfilingType::ANGLE_HEIGHT_PROFILING) {
+ // Pick a set of sane arm defaults if none are specified.
+ arm_profile_.set_maximum_velocity(
+ UseUnlessZero(unsafe_goal->max_angular_velocity, 1.0));
+ arm_profile_.set_maximum_acceleration(
+ UseUnlessZero(unsafe_goal->max_angular_acceleration, 3.0));
+ elevator_profile_.set_maximum_velocity(
+ UseUnlessZero(unsafe_goal->max_velocity, 0.50));
+ elevator_profile_.set_maximum_acceleration(
+ UseUnlessZero(unsafe_goal->max_acceleration, 2.0));
+
+ // Use the profiles to limit the arm's movements.
+ const double unfiltered_arm_goal = ::std::max(
+ ::std::min(unsafe_goal->angle, values.fridge.arm.upper_limit),
+ values.fridge.arm.lower_limit);
+ ::Eigen::Matrix<double, 2, 1> arm_goal_state = arm_profile_.Update(
+ unfiltered_arm_goal, unsafe_goal->angular_velocity);
+ arm_goal_ = arm_goal_state(0, 0);
+ arm_goal_velocity_ = arm_goal_state(1, 0);
+
+ // Use the profiles to limit the elevator's movements.
+ const double unfiltered_elevator_goal =
+ ::std::max(::std::min(unsafe_goal->height,
+ values.fridge.elevator.upper_limit),
+ values.fridge.elevator.lower_limit);
+ ::Eigen::Matrix<double, 2, 1> elevator_goal_state =
+ elevator_profile_.Update(unfiltered_elevator_goal,
+ unsafe_goal->velocity);
+ elevator_goal_ = elevator_goal_state(0, 0);
+ elevator_goal_velocity_ = elevator_goal_state(1, 0);
+ } else if (new_profiling_type == ProfilingType::X_Y_PROFILING) {
+ // Use x/y profiling
+ aos::util::ElevatorArmKinematics::KinematicResult kinematic_result;
+
+ x_profile_.set_maximum_velocity(
+ UseUnlessZero(unsafe_goal->max_x_velocity, 0.5));
+ x_profile_.set_maximum_acceleration(
+ UseUnlessZero(unsafe_goal->max_x_acceleration, 2.0));
+ y_profile_.set_maximum_velocity(
+ UseUnlessZero(unsafe_goal->max_y_velocity, 0.50));
+ y_profile_.set_maximum_acceleration(
+ UseUnlessZero(unsafe_goal->max_y_acceleration, 2.0));
+
+ // Limit the goals before we update the profiles.
+ kinematics_.InverseKinematic(
+ unsafe_goal->x, unsafe_goal->y, unsafe_goal->x_velocity,
+ unsafe_goal->y_velocity, &kinematic_result);
+
+ // Use the profiles to limit the x movements.
+ ::Eigen::Matrix<double, 2, 1> x_goal_state = x_profile_.Update(
+ kinematic_result.fridge_x, kinematic_result.fridge_x_velocity);
+
+ // Use the profiles to limit the y movements.
+ ::Eigen::Matrix<double, 2, 1> y_goal_state = y_profile_.Update(
+ kinematic_result.fridge_h, kinematic_result.fridge_h_velocity);
+
+ // Convert x/y goal states into arm/elevator goals.
+ // The inverse kinematics functions automatically perform range
+ // checking and adjust the results so that they're always valid.
+ kinematics_.InverseKinematic(x_goal_state(0, 0), y_goal_state(0, 0),
+ x_goal_state(1, 0), y_goal_state(1, 0),
+ &kinematic_result);
+
+ // Store the appropriate inverse kinematic results in the
+ // arm/elevator goals.
+ arm_goal_ = kinematic_result.arm_angle;
+ arm_goal_velocity_ = kinematic_result.arm_velocity;
+
+ elevator_goal_ = kinematic_result.elevator_height;
+ elevator_goal_velocity_ = kinematic_result.arm_velocity;
+ } else {
+ LOG(ERROR, "Unknown profiling_type: %d\n",
+ unsafe_goal->profiling_type);
+ }
+ }
+
+ // Update state_ to accurately represent the state of the zeroing
+ // estimators.
+ UpdateZeroingState();
+
+ if (state_ != RUNNING && state_ != ESTOP) {
+ state_ = UNINITIALIZED;
+ }
+ break;
+
+ case ESTOP:
+ LOG(ERROR, "Estop\n");
+ disable = true;
+ break;
+ }
+
+ // Commence death if either left/right tracking error gets too big. This
+ // should run immediately after the SetArmOffset and SetElevatorOffset
+ // functions to double-check that the hardware is in a sane state.
+ if (::std::abs(left_arm() - right_arm()) >=
+ values.max_allowed_left_right_arm_difference) {
+ LOG(ERROR, "The arms are too far apart. |%f - %f| > %f\n", left_arm(),
+ right_arm(), values.max_allowed_left_right_arm_difference);
+
+ // Indicate an ESTOP condition and stop the motors.
+ if (output) {
+ state_ = ESTOP;
+ }
+ disable = true;
+ }
+
+ if (::std::abs(left_elevator() - right_elevator()) >=
+ values.max_allowed_left_right_elevator_difference) {
+ LOG(ERROR, "The elevators are too far apart. |%f - %f| > %f\n",
+ left_elevator(), right_elevator(),
+ values.max_allowed_left_right_elevator_difference);
+
+ // Indicate an ESTOP condition and stop the motors.
+ if (output) {
+ state_ = ESTOP;
+ }
+ disable = true;
+ }
+
+ // Limit the goals so we can't exceed the hardware limits if we are RUNNING.
+ if (state_ == RUNNING) {
+ // Limit the arm goal to min/max allowable angles.
+ if (arm_goal_ >= values.fridge.arm.upper_limit) {
+ LOG(WARNING, "Arm goal above limit, %f > %f\n", arm_goal_,
+ values.fridge.arm.upper_limit);
+ arm_goal_ = values.fridge.arm.upper_limit;
+ }
+ if (arm_goal_ <= values.fridge.arm.lower_limit) {
+ LOG(WARNING, "Arm goal below limit, %f < %f\n", arm_goal_,
+ values.fridge.arm.lower_limit);
+ arm_goal_ = values.fridge.arm.lower_limit;
+ }
+
+ // Limit the elevator goal to min/max allowable heights.
+ if (elevator_goal_ >= values.fridge.elevator.upper_limit) {
+ LOG(WARNING, "Elevator goal above limit, %f > %f\n", elevator_goal_,
+ values.fridge.elevator.upper_limit);
+ elevator_goal_ = values.fridge.elevator.upper_limit;
+ }
+ if (elevator_goal_ <= values.fridge.elevator.lower_limit) {
+ LOG(WARNING, "Elevator goal below limit, %f < %f\n", elevator_goal_,
+ values.fridge.elevator.lower_limit);
+ elevator_goal_ = values.fridge.elevator.lower_limit;
+ }
+ }
+
+ // Check the lower level hardware limit as well.
+ if (state_ == RUNNING) {
+ if (left_arm() >= values.fridge.arm.upper_hard_limit ||
+ left_arm() <= values.fridge.arm.lower_hard_limit) {
+ LOG(ERROR, "Left arm at %f out of bounds [%f, %f], ESTOPing\n",
+ left_arm(), values.fridge.arm.lower_hard_limit,
+ values.fridge.arm.upper_hard_limit);
+ if (output) {
+ state_ = ESTOP;
+ }
+ }
+
+ if (right_arm() >= values.fridge.arm.upper_hard_limit ||
+ right_arm() <= values.fridge.arm.lower_hard_limit) {
+ LOG(ERROR, "Right arm at %f out of bounds [%f, %f], ESTOPing\n",
+ right_arm(), values.fridge.arm.lower_hard_limit,
+ values.fridge.arm.upper_hard_limit);
+ if (output) {
+ state_ = ESTOP;
+ }
+ }
+
+ if (left_elevator() >= values.fridge.elevator.upper_hard_limit) {
+ LOG(ERROR, "Left elevator at %f out of bounds [%f, %f], ESTOPing\n",
+ left_elevator(), values.fridge.elevator.lower_hard_limit,
+ values.fridge.elevator.upper_hard_limit);
+ if (output) {
+ state_ = ESTOP;
+ }
+ }
+
+ if (right_elevator() >= values.fridge.elevator.upper_hard_limit) {
+ LOG(ERROR, "Right elevator at %f out of bounds [%f, %f], ESTOPing\n",
+ right_elevator(), values.fridge.elevator.lower_hard_limit,
+ values.fridge.elevator.upper_hard_limit);
+ if (output) {
+ state_ = ESTOP;
+ }
+ }
+ }
+
+ // Set the goals.
+ arm_loop_->mutable_R() << arm_goal_, arm_goal_velocity_, 0.0, 0.0, 0.0;
+ elevator_loop_->mutable_R() << elevator_goal_, elevator_goal_velocity_, 0.0,
+ 0.0;
+
+ const double max_voltage = state_ == RUNNING ? 12.0 : kZeroingVoltage;
+ arm_loop_->set_max_voltage(max_voltage);
+ elevator_loop_->set_max_voltage(max_voltage);
+
+ if (state_ == ESTOP) {
+ disable = true;
+ }
+ arm_loop_->Update(disable);
+ elevator_loop_->Update(disable);
+
+ if (state_ == INITIALIZING || state_ == ZEROING_ELEVATOR ||
+ state_ == ZEROING_ARM) {
+ if (arm_loop_->U() != arm_loop_->U_uncapped()) {
+ Eigen::Matrix<double, 2, 1> deltaR =
+ arm_loop_->UnsaturateOutputGoalChange();
+
+ // Move the average arm goal by the amount observed.
+ LOG(WARNING, "Moving arm goal by %f to handle saturation\n",
+ deltaR(0, 0));
+ arm_goal_ += deltaR(0, 0);
+ }
+
+ if (elevator_loop_->U() != elevator_loop_->U_uncapped()) {
+ Eigen::Matrix<double, 2, 1> deltaR =
+ elevator_loop_->UnsaturateOutputGoalChange();
+
+ // Move the average elevator goal by the amount observed.
+ LOG(WARNING, "Moving elevator goal by %f to handle saturation\n",
+ deltaR(0, 0));
+ elevator_goal_ += deltaR(0, 0);
+ }
+ }
+
+ if (output) {
+ output->left_arm = arm_loop_->U(0, 0);
+ output->right_arm = arm_loop_->U(1, 0);
+ output->left_elevator = elevator_loop_->U(0, 0);
+ output->right_elevator = elevator_loop_->U(1, 0);
+ if (unsafe_goal) {
+ output->grabbers = unsafe_goal->grabbers;
+ } else {
+ output->grabbers.top_front = false;
+ output->grabbers.top_back = false;
+ output->grabbers.bottom_front = false;
+ output->grabbers.bottom_back = false;
+ }
+ }
+
+ // TODO(austin): Populate these fully.
+ status->zeroed = state_ == RUNNING;
+
+ status->angle = arm_loop_->X_hat(0, 0);
+ status->angular_velocity = arm_loop_->X_hat(1, 0);
+ status->height = elevator_loop_->X_hat(0, 0);
+ status->velocity = elevator_loop_->X_hat(1, 0);
+
+ status->goal_angle = arm_goal_;
+ status->goal_angular_velocity = arm_goal_velocity_;
+ status->goal_height = elevator_goal_;
+ status->goal_velocity = elevator_goal_velocity_;
+
+ // Populate the same status, but in X/Y co-ordinates.
+ aos::util::ElevatorArmKinematics::KinematicResult x_y_status;
+ kinematics_.ForwardKinematic(status->height, status->angle,
+ status->velocity, status->angular_velocity,
+ &x_y_status);
+ status->x = x_y_status.fridge_x;
+ status->y = x_y_status.fridge_h;
+ status->x_velocity = x_y_status.fridge_x_velocity;
+ status->y_velocity = x_y_status.fridge_h_velocity;
+
+ kinematics_.ForwardKinematic(status->goal_height, status->goal_angle,
+ status->goal_velocity, status->goal_angular_velocity,
+ &x_y_status);
+ status->goal_x = x_y_status.fridge_x;
+ status->goal_y = x_y_status.fridge_h;
+ status->goal_x_velocity = x_y_status.fridge_x_velocity;
+ status->goal_y_velocity = x_y_status.fridge_h_velocity;
+
+ if (unsafe_goal) {
+ status->grabbers = unsafe_goal->grabbers;
+ } else {
+ status->grabbers.top_front = false;
+ status->grabbers.top_back = false;
+ status->grabbers.bottom_front = false;
+ status->grabbers.bottom_back = false;
+ }
+ zeroing::PopulateEstimatorState(left_arm_estimator_, &status->left_arm_state);
+ zeroing::PopulateEstimatorState(right_arm_estimator_,
+ &status->right_arm_state);
+ zeroing::PopulateEstimatorState(left_elevator_estimator_,
+ &status->left_elevator_state);
+ zeroing::PopulateEstimatorState(right_elevator_estimator_,
+ &status->right_elevator_state);
+ status->estopped = (state_ == ESTOP);
+ status->state = state_;
+ last_state_ = state_;
+}
+
+} // namespace control_loops
+} // namespace frc971
diff --git a/y2015/control_loops/fridge/fridge.gyp b/y2015/control_loops/fridge/fridge.gyp
new file mode 100644
index 0000000..76844c4
--- /dev/null
+++ b/y2015/control_loops/fridge/fridge.gyp
@@ -0,0 +1,89 @@
+{
+ 'targets': [
+ {
+ 'target_name': 'replay_fridge',
+ 'type': 'executable',
+ 'variables': {
+ 'no_rsync': 1,
+ },
+ 'sources': [
+ 'replay_fridge.cc',
+ ],
+ 'dependencies': [
+ 'fridge_queue',
+ '<(AOS)/common/controls/controls.gyp:replay_control_loop',
+ '<(AOS)/linux_code/linux_code.gyp:init',
+ ],
+ },
+ {
+ 'target_name': 'fridge_queue',
+ 'type': 'static_library',
+ 'sources': ['fridge.q'],
+ 'variables': {
+ 'header_path': 'y2015/control_loops/fridge',
+ },
+ 'dependencies': [
+ '<(AOS)/common/controls/controls.gyp:control_loop_queues',
+ '<(DEPTH)/frc971/control_loops/control_loops.gyp:queues',
+ '<(DEPTH)/frc971/zeroing/zeroing.gyp:zeroing',
+ ],
+ 'export_dependent_settings': [
+ '<(AOS)/common/controls/controls.gyp:control_loop_queues',
+ '<(DEPTH)/frc971/control_loops/control_loops.gyp:queues',
+ '<(DEPTH)/frc971/zeroing/zeroing.gyp:zeroing',
+ ],
+ 'includes': ['../../../aos/build/queues.gypi'],
+ },
+ {
+ 'target_name': 'fridge_lib',
+ 'type': 'static_library',
+ 'sources': [
+ 'fridge.cc',
+ 'integral_arm_plant.cc',
+ 'elevator_motor_plant.cc',
+ ],
+ 'dependencies': [
+ 'fridge_queue',
+ '<(AOS)/common/controls/controls.gyp:control_loop',
+ '<(AOS)/common/util/util.gyp:trapezoid_profile',
+ '<(DEPTH)/y2015/y2015.gyp:constants',
+ '<(DEPTH)/frc971/control_loops/control_loops.gyp:state_feedback_loop',
+ '<(DEPTH)/frc971/control_loops/voltage_cap/voltage_cap.gyp:voltage_cap',
+ ],
+ 'export_dependent_settings': [
+ 'fridge_queue',
+ '<(AOS)/common/controls/controls.gyp:control_loop',
+ '<(AOS)/common/util/util.gyp:trapezoid_profile',
+ '<(DEPTH)/frc971/control_loops/control_loops.gyp:state_feedback_loop',
+ ],
+ },
+ {
+ 'target_name': 'fridge_lib_test',
+ 'type': 'executable',
+ 'sources': [
+ 'fridge_lib_test.cc',
+ 'arm_motor_plant.cc',
+ ],
+ 'dependencies': [
+ '<(EXTERNALS):gtest',
+ 'fridge_lib',
+ '<(DEPTH)/frc971/control_loops/control_loops.gyp:state_feedback_loop',
+ '<(AOS)/common/controls/controls.gyp:control_loop_test',
+ '<(AOS)/common/common.gyp:time',
+ '<(DEPTH)/frc971/control_loops/control_loops.gyp:position_sensor_sim',
+ '<(DEPTH)/frc971/control_loops/control_loops.gyp:team_number_test_environment',
+ ],
+ },
+ {
+ 'target_name': 'fridge',
+ 'type': 'executable',
+ 'sources': [
+ 'fridge_main.cc',
+ ],
+ 'dependencies': [
+ '<(AOS)/linux_code/linux_code.gyp:init',
+ 'fridge_lib',
+ ],
+ },
+ ],
+}
diff --git a/y2015/control_loops/fridge/fridge.h b/y2015/control_loops/fridge/fridge.h
new file mode 100644
index 0000000..0448e09
--- /dev/null
+++ b/y2015/control_loops/fridge/fridge.h
@@ -0,0 +1,172 @@
+#ifndef Y2015_CONTROL_LOOPS_FRIDGE_H_
+#define Y2015_CONTROL_LOOPS_FRIDGE_H_
+
+#include <memory>
+
+#include "aos/common/controls/control_loop.h"
+#include "aos/common/util/trapezoid_profile.h"
+#include "frc971/control_loops/state_feedback_loop.h"
+#include "y2015/control_loops/fridge/fridge.q.h"
+#include "frc971/zeroing/zeroing.h"
+#include "y2015/util/kinematics.h"
+
+namespace frc971 {
+namespace control_loops {
+namespace testing {
+class FridgeTest_DisabledGoalTest_Test;
+class FridgeTest_ArmGoalPositiveWindupTest_Test;
+class FridgeTest_ElevatorGoalPositiveWindupTest_Test;
+class FridgeTest_ArmGoalNegativeWindupTest_Test;
+class FridgeTest_ElevatorGoalNegativeWindupTest_Test;
+class FridgeTest_SafeArmZeroing_Test;
+} // namespace testing
+
+template<int S>
+class CappedStateFeedbackLoop : public StateFeedbackLoop<S, 2, 2> {
+ public:
+ CappedStateFeedbackLoop(StateFeedbackLoop<S, 2, 2> &&loop)
+ : StateFeedbackLoop<S, 2, 2>(::std::move(loop)), max_voltage_(12.0) {}
+
+ void set_max_voltage(double max_voltage) {
+ max_voltage_ = ::std::max(0.0, ::std::min(12.0, max_voltage));
+ }
+
+ void CapU() override;
+
+ // Returns the amount to change the position goals (average and difference) in
+ // order to no longer saturate the controller.
+ Eigen::Matrix<double, 2, 1> UnsaturateOutputGoalChange();
+
+ private:
+ double max_voltage_;
+};
+
+class Fridge
+ : public aos::controls::ControlLoop<control_loops::FridgeQueue> {
+ public:
+ explicit Fridge(
+ control_loops::FridgeQueue *fridge_queue = &control_loops::fridge_queue);
+
+ enum State {
+ // Waiting to receive data before doing anything.
+ UNINITIALIZED = 0,
+ // Estimating the starting location.
+ INITIALIZING = 1,
+ // Moving the elevator to find an index pulse.
+ ZEROING_ELEVATOR = 2,
+ // Moving the arm to find an index pulse.
+ ZEROING_ARM = 3,
+ // All good!
+ RUNNING = 4,
+ // Internal error caused the fridge to abort.
+ ESTOP = 5,
+ };
+
+ enum class ProfilingType : int32_t {
+ // Use angle/height to specify the fridge goal.
+ ANGLE_HEIGHT_PROFILING = 0,
+ // Use x/y co-ordinates to specify the fridge goal.
+ X_Y_PROFILING = 1,
+ };
+
+ State state() const { return state_; }
+
+ protected:
+ void RunIteration(const control_loops::FridgeQueue::Goal *goal,
+ const control_loops::FridgeQueue::Position *position,
+ control_loops::FridgeQueue::Output *output,
+ control_loops::FridgeQueue::Status *status) override;
+
+ private:
+ friend class testing::FridgeTest_DisabledGoalTest_Test;
+ friend class testing::FridgeTest_ElevatorGoalPositiveWindupTest_Test;
+ friend class testing::FridgeTest_ArmGoalPositiveWindupTest_Test;
+ friend class testing::FridgeTest_ElevatorGoalNegativeWindupTest_Test;
+ friend class testing::FridgeTest_ArmGoalNegativeWindupTest_Test;
+ friend class testing::FridgeTest_SafeArmZeroing_Test;
+
+ // Sets state_ to the correct state given the current state of the zeroing
+ // estimators.
+ void UpdateZeroingState();
+
+ void SetElevatorOffset(double left_offset, double right_offset);
+ void SetArmOffset(double left_offset, double right_offset);
+
+ // Getters for the current elevator positions.
+ double left_elevator();
+ double right_elevator();
+ double elevator();
+
+ // Getters for the current arm positions.
+ double left_arm();
+ double right_arm();
+ double arm();
+
+ // Our best guess at the current position of the elevator.
+ double estimated_left_elevator();
+ double estimated_right_elevator();
+ double estimated_elevator();
+
+ // Our best guess at the current position of the arm.
+ double estimated_left_arm();
+ double estimated_right_arm();
+ double estimated_arm();
+
+ // Returns the current zeroing velocity for either subsystem.
+ // If the subsystem is too far away from the center, these will switch
+ // directions.
+ double elevator_zeroing_velocity();
+ double arm_zeroing_velocity();
+
+ // Corrects the Observer with the current position.
+ void Correct();
+
+ double UseUnlessZero(double target_value, double default_value);
+
+ // The state feedback control loop or loops to talk to.
+ ::std::unique_ptr<CappedStateFeedbackLoop<5>> arm_loop_;
+ ::std::unique_ptr<CappedStateFeedbackLoop<4>> elevator_loop_;
+
+ zeroing::ZeroingEstimator left_arm_estimator_;
+ zeroing::ZeroingEstimator right_arm_estimator_;
+ zeroing::ZeroingEstimator left_elevator_estimator_;
+ zeroing::ZeroingEstimator right_elevator_estimator_;
+
+ // Offsets from the encoder position to the absolute position. Add these to
+ // the encoder position to get the absolute position.
+ double left_elevator_offset_ = 0.0;
+ double right_elevator_offset_ = 0.0;
+ double left_arm_offset_ = 0.0;
+ double right_arm_offset_ = 0.0;
+
+ // Current velocity to move at while zeroing.
+ double elevator_zeroing_velocity_ = 0.0;
+ double arm_zeroing_velocity_ = 0.0;
+
+ // The goals for the elevator and arm.
+ double elevator_goal_ = 0.0;
+ double arm_goal_ = 0.0;
+
+ double arm_goal_velocity_ = 0.0;
+ double elevator_goal_velocity_ = 0.0;
+
+ State state_ = UNINITIALIZED;
+ State last_state_ = UNINITIALIZED;
+
+ control_loops::FridgeQueue::Position current_position_;
+
+ ProfilingType last_profiling_type_;
+ aos::util::ElevatorArmKinematics kinematics_;
+
+ aos::util::TrapezoidProfile arm_profile_;
+ aos::util::TrapezoidProfile elevator_profile_;
+
+ aos::util::TrapezoidProfile x_profile_;
+ aos::util::TrapezoidProfile y_profile_;
+};
+
+} // namespace control_loops
+} // namespace frc971
+
+#endif // Y2015_CONTROL_LOOPS_FRIDGE_H_
+
diff --git a/y2015/control_loops/fridge/fridge.q b/y2015/control_loops/fridge/fridge.q
new file mode 100644
index 0000000..257374d
--- /dev/null
+++ b/y2015/control_loops/fridge/fridge.q
@@ -0,0 +1,152 @@
+package frc971.control_loops;
+
+import "aos/common/controls/control_loops.q";
+import "frc971/control_loops/control_loops.q";
+
+// Represents states for all of the box-grabbing pistons.
+// true is grabbed and false is retracted for all of them.
+struct GrabberPistons {
+ bool top_front;
+ bool top_back;
+ bool bottom_front;
+ bool bottom_back;
+};
+
+queue_group FridgeQueue {
+ implements aos.control_loops.ControlLoop;
+
+ // All angles are in radians with 0 sticking straight up.
+ // Rotating up and into the robot (towards the back
+ // where boxes are placed) is positive. Positive output voltage moves all
+ // mechanisms in the direction with positive sensor values.
+
+ // Elevator heights are the vertical distance (in meters) from a defined zero.
+ // In this case, zero is where the portion of the carriage that Spencer
+ // removed lines up with the bolt.
+
+ // X/Y positions are distances (in meters) the fridge is away from its origin
+ // position. Origin is considered at a height of zero and an angle of zero.
+ // Positive X positions are towards the front of the robot and negative X is
+ // towards the back of the robot (which is where we score).
+ // Y is positive going up and negative when it goes below the origin.
+
+ message Goal {
+ // Profile type.
+ // Set to 0 for angle/height profiling.
+ // Set to 1 for x/y profiling.
+ int32_t profiling_type;
+
+ // Angle of the arm.
+ double angle;
+ // Height of the elevator.
+ double height;
+
+ // Angular velocity of the arm.
+ float angular_velocity;
+ // Linear velocity of the elevator.
+ float velocity;
+
+ // Maximum arm profile angular velocity or 0 for the default.
+ float max_angular_velocity;
+ // Maximum elevator profile velocity or 0 for the default.
+ float max_velocity;
+
+ // Maximum arm profile acceleration or 0 for the default.
+ float max_angular_acceleration;
+ // Maximum elevator profile acceleration or 0 for the default.
+ float max_acceleration;
+
+ // X position of the fridge.
+ double x;
+ // Y position of the fridge.
+ double y;
+
+ // Velocity of the x position of the fridge.
+ float x_velocity;
+ // Velocity of the y position of the fridge.
+ float y_velocity;
+
+ // Maximum x profile velocity or 0 for the default.
+ float max_x_velocity;
+ // Maximum y profile velocity or 0 for the default.
+ float max_y_velocity;
+
+ // Maximum x profile acceleration or 0 for the default.
+ float max_x_acceleration;
+ // Maximum y profile acceleration or 0 for the default.
+ float max_y_acceleration;
+
+ // TODO(austin): Do I need acceleration here too?
+
+ GrabberPistons grabbers;
+ };
+
+ message Position {
+ PotAndIndexPair arm;
+ PotAndIndexPair elevator;
+ };
+
+ message Status {
+ // Are both the arm and elevator zeroed?
+ bool zeroed;
+
+ // Estimated angle of the arm.
+ double angle;
+ // Estimated angular velocity of the arm.
+ float angular_velocity;
+ // Estimated height of the elevator.
+ double height;
+ // Estimated velocity of the elvator.
+ float velocity;
+ // state of the grabber pistons
+ GrabberPistons grabbers;
+
+ // Goal angle and velocity of the arm.
+ double goal_angle;
+ float goal_angular_velocity;
+ // Goal height and velocity of the elevator.
+ double goal_height;
+ float goal_velocity;
+
+ // Estimated X/Y position of the fridge.
+ // These are translated directly from the height/angle statuses.
+ double x;
+ double y;
+ float x_velocity;
+ float y_velocity;
+
+ // X/Y goals of the fridge.
+ // These are translated directly from the height/angle goals.
+ double goal_x;
+ double goal_y;
+ float goal_x_velocity;
+ float goal_y_velocity;
+
+ // If true, we have aborted.
+ bool estopped;
+
+ // The internal state of the state machine.
+ int32_t state;
+
+ EstimatorState left_elevator_state;
+ EstimatorState right_elevator_state;
+ EstimatorState left_arm_state;
+ EstimatorState right_arm_state;
+ };
+
+ message Output {
+ double left_arm;
+ double right_arm;
+ double left_elevator;
+ double right_elevator;
+
+ GrabberPistons grabbers;
+ };
+
+ queue Goal goal;
+ queue Position position;
+ queue Output output;
+ queue Status status;
+};
+
+queue_group FridgeQueue fridge_queue;
diff --git a/y2015/control_loops/fridge/fridge_lib_test.cc b/y2015/control_loops/fridge/fridge_lib_test.cc
new file mode 100644
index 0000000..7719fb4
--- /dev/null
+++ b/y2015/control_loops/fridge/fridge_lib_test.cc
@@ -0,0 +1,735 @@
+#include "y2015/control_loops/fridge/fridge.h"
+
+#include <math.h>
+#include <unistd.h>
+
+#include <memory>
+
+#include "gtest/gtest.h"
+#include "aos/common/queue.h"
+#include "aos/common/time.h"
+#include "aos/common/commonmath.h"
+#include "aos/common/controls/control_loop_test.h"
+#include "y2015/util/kinematics.h"
+#include "frc971/control_loops/position_sensor_sim.h"
+#include "y2015/control_loops/fridge/arm_motor_plant.h"
+#include "y2015/control_loops/fridge/elevator_motor_plant.h"
+#include "y2015/control_loops/fridge/fridge.q.h"
+#include "y2015/constants.h"
+#include "frc971/control_loops/team_number_test_environment.h"
+
+using ::aos::time::Time;
+
+namespace frc971 {
+namespace control_loops {
+namespace testing {
+// Class which simulates the fridge and sends out queue messages with the
+// position.
+class FridgeSimulation {
+ public:
+ static constexpr double kNoiseScalar = 0.1;
+ // Constructs a simulation.
+ FridgeSimulation()
+ : arm_plant_(new StateFeedbackPlant<4, 2, 2>(MakeArmPlant())),
+ elevator_plant_(new StateFeedbackPlant<4, 2, 2>(MakeElevatorPlant())),
+ left_arm_pot_encoder_(
+ constants::GetValues().fridge.left_arm_zeroing.index_difference),
+ right_arm_pot_encoder_(
+ constants::GetValues().fridge.right_arm_zeroing.index_difference),
+ left_elevator_pot_encoder_(
+ constants::GetValues().fridge.left_elev_zeroing.index_difference),
+ right_elevator_pot_encoder_(
+ constants::GetValues().fridge.right_elev_zeroing.index_difference),
+ fridge_queue_(".frc971.control_loops.fridge_queue", 0xe4e05855,
+ ".frc971.control_loops.fridge_queue.goal",
+ ".frc971.control_loops.fridge_queue.position",
+ ".frc971.control_loops.fridge_queue.output",
+ ".frc971.control_loops.fridge_queue.status") {
+ // Initialize the elevator.
+ InitializeElevatorPosition(
+ constants::GetValues().fridge.elevator.lower_limit);
+ // Initialize the arm.
+ InitializeArmPosition(0.0);
+ }
+
+ void InitializeElevatorPosition(double start_pos) {
+ InitializeElevatorPosition(start_pos, start_pos);
+ }
+
+ void InitializeElevatorPosition(double left_start_pos,
+ double right_start_pos) {
+ InitializeElevatorPosition(
+ left_start_pos, right_start_pos,
+ kNoiseScalar *
+ constants::GetValues().fridge.right_elev_zeroing.index_difference);
+ }
+
+ void InitializeElevatorPosition(double left_start_pos, double right_start_pos,
+ double pot_noise_stddev) {
+ // Update the internal state of the elevator plant.
+ elevator_plant_->mutable_X(0, 0) = (left_start_pos + right_start_pos) / 2.0;
+ elevator_plant_->mutable_X(1, 0) = 0.0;
+ elevator_plant_->mutable_X(2, 0) = (left_start_pos - right_start_pos) / 2.0;
+ elevator_plant_->mutable_X(3, 0) = 0.0;
+
+ right_elevator_pot_encoder_.Initialize(right_start_pos, pot_noise_stddev);
+ left_elevator_pot_encoder_.Initialize(left_start_pos, pot_noise_stddev);
+ elevator_initial_difference_ = left_start_pos - right_start_pos;
+ }
+
+ void InitializeArmPosition(double start_pos) {
+ InitializeArmPosition(start_pos, start_pos);
+ }
+
+ void InitializeArmPosition(double left_start_pos, double right_start_pos) {
+ InitializeArmPosition(
+ left_start_pos, right_start_pos,
+ kNoiseScalar *
+ constants::GetValues().fridge.right_arm_zeroing.index_difference);
+ }
+ void InitializeArmPosition(double left_start_pos, double right_start_pos,
+ double pot_noise_stddev) {
+ // Update the internal state of the arm plant.
+ arm_plant_->mutable_X(0, 0) = (left_start_pos + right_start_pos) / 2.0;
+ arm_plant_->mutable_X(1, 0) = 0.0;
+ arm_plant_->mutable_X(2, 0) = (left_start_pos - right_start_pos) / 2.0;
+ arm_plant_->mutable_X(3, 0) = 0.0;
+
+ left_arm_pot_encoder_.Initialize(left_start_pos, pot_noise_stddev);
+ right_arm_pot_encoder_.Initialize(right_start_pos, pot_noise_stddev);
+ arm_initial_difference_ = left_start_pos - right_start_pos;
+ }
+
+ // Changes the left-right calculations in the limit checks to measure absolute
+ // differences instead of differences relative to the starting offset.
+ void ErrorOnAbsoluteDifference() {
+ arm_initial_difference_ = 0.0;
+ elevator_initial_difference_ = 0.0;
+ }
+
+ // Sends a queue message with the position.
+ void SendPositionMessage() {
+ ::aos::ScopedMessagePtr<control_loops::FridgeQueue::Position> position =
+ fridge_queue_.position.MakeMessage();
+
+ left_arm_pot_encoder_.GetSensorValues(&position->arm.left);
+ right_arm_pot_encoder_.GetSensorValues(&position->arm.right);
+ left_elevator_pot_encoder_.GetSensorValues(&position->elevator.left);
+ right_elevator_pot_encoder_.GetSensorValues(&position->elevator.right);
+
+ position.Send();
+ }
+
+ // Sets the difference between the commanded and applied power for the arm.
+ // This lets us test that the integrator for the arm works.
+ void set_arm_power_error(double arm_power_error) {
+ arm_power_error_ = arm_power_error;
+ }
+ // Simulates for a single timestep.
+ void Simulate() {
+ EXPECT_TRUE(fridge_queue_.output.FetchLatest());
+
+ // Feed voltages into physics simulation.
+ if (arm_power_error_ != 0.0) {
+ arm_plant_->mutable_U() << ::aos::Clip(
+ fridge_queue_.output->left_arm + arm_power_error_, -12, 12),
+ ::aos::Clip(fridge_queue_.output->right_arm + arm_power_error_, -12,
+ 12);
+ } else {
+ arm_plant_->mutable_U() << fridge_queue_.output->left_arm,
+ fridge_queue_.output->right_arm;
+ }
+ elevator_plant_->mutable_U() << fridge_queue_.output->left_elevator,
+ fridge_queue_.output->right_elevator;
+
+ // Use the plant to generate the next physical state given the voltages to
+ // the motors.
+ arm_plant_->Update();
+ elevator_plant_->Update();
+
+ const double left_arm_angle = arm_plant_->Y(0, 0);
+ const double right_arm_angle = arm_plant_->Y(1, 0);
+ const double left_elevator_height = elevator_plant_->Y(0, 0);
+ const double right_elevator_height = elevator_plant_->Y(1, 0);
+
+ // TODO (phil) Do some sanity tests on the arm angles and the elevator
+ // heights. For example, we need to make sure that both sides are within a
+ // certain distance of each other and they haven't crashed into the top or
+ // the bottom.
+
+ // Use the physical state to simulate sensor readings.
+ left_arm_pot_encoder_.MoveTo(left_arm_angle);
+ right_arm_pot_encoder_.MoveTo(right_arm_angle);
+ left_elevator_pot_encoder_.MoveTo(left_elevator_height);
+ right_elevator_pot_encoder_.MoveTo(right_elevator_height);
+
+ // Verify that the arm and elevator sides don't change much from their
+ // initial difference. Use the initial difference instead of the absolute
+ // difference to handle starting too far apart to test e-stopping.
+ EXPECT_NEAR(left_arm_angle - right_arm_angle, arm_initial_difference_,
+ 5.0 * M_PI / 180.0);
+ EXPECT_NEAR(left_elevator_height - right_elevator_height,
+ elevator_initial_difference_, 0.0254);
+
+ // Validate that the arm is within range.
+ EXPECT_GE(left_arm_angle,
+ constants::GetValues().fridge.arm.lower_hard_limit);
+ EXPECT_GE(right_arm_angle,
+ constants::GetValues().fridge.arm.lower_hard_limit);
+ EXPECT_LE(left_arm_angle,
+ constants::GetValues().fridge.arm.upper_hard_limit);
+ EXPECT_LE(right_arm_angle,
+ constants::GetValues().fridge.arm.upper_hard_limit);
+
+ // Validate that the elevator is within range.
+ EXPECT_GE(left_elevator_height,
+ constants::GetValues().fridge.elevator.lower_hard_limit);
+ EXPECT_GE(right_elevator_height,
+ constants::GetValues().fridge.elevator.lower_hard_limit);
+ EXPECT_LE(left_elevator_height,
+ constants::GetValues().fridge.elevator.upper_hard_limit);
+ EXPECT_LE(right_elevator_height,
+ constants::GetValues().fridge.elevator.upper_hard_limit);
+ }
+
+ private:
+ ::std::unique_ptr<StateFeedbackPlant<4, 2, 2>> arm_plant_;
+ ::std::unique_ptr<StateFeedbackPlant<4, 2, 2>> elevator_plant_;
+
+ PositionSensorSimulator left_arm_pot_encoder_;
+ PositionSensorSimulator right_arm_pot_encoder_;
+ PositionSensorSimulator left_elevator_pot_encoder_;
+ PositionSensorSimulator right_elevator_pot_encoder_;
+
+ FridgeQueue fridge_queue_;
+
+ double elevator_initial_difference_ = 0.0;
+ double arm_initial_difference_ = 0.0;
+ double arm_power_error_ = 0.0;
+};
+
+class FridgeTest : public ::aos::testing::ControlLoopTest {
+ protected:
+ FridgeTest()
+ : fridge_queue_(".frc971.control_loops.fridge_queue", 0xe4e05855,
+ ".frc971.control_loops.fridge_queue.goal",
+ ".frc971.control_loops.fridge_queue.position",
+ ".frc971.control_loops.fridge_queue.output",
+ ".frc971.control_loops.fridge_queue.status"),
+ fridge_(&fridge_queue_),
+ fridge_plant_(),
+ kinematics_(constants::GetValues().fridge.arm_length,
+ constants::GetValues().fridge.elevator.upper_limit,
+ constants::GetValues().fridge.elevator.lower_limit,
+ constants::GetValues().fridge.arm.upper_limit,
+ constants::GetValues().fridge.arm.lower_limit) {
+ set_team_id(kTeamNumber);
+ }
+
+ void VerifyNearGoal() {
+ fridge_queue_.goal.FetchLatest();
+ fridge_queue_.status.FetchLatest();
+ EXPECT_TRUE(fridge_queue_.goal.get() != nullptr);
+ EXPECT_TRUE(fridge_queue_.status.get() != nullptr);
+ if (fridge_queue_.goal->profiling_type == 0) {
+ EXPECT_NEAR(fridge_queue_.goal->angle, fridge_queue_.status->angle,
+ 0.001);
+ EXPECT_NEAR(fridge_queue_.goal->height, fridge_queue_.status->height,
+ 0.001);
+ } else if (fridge_queue_.goal->profiling_type == 1) {
+ aos::util::ElevatorArmKinematics::KinematicResult x_y_status;
+ kinematics_.ForwardKinematic(fridge_queue_.status->height,
+ fridge_queue_.status->angle, 0.0, 0.0, &x_y_status);
+ EXPECT_NEAR(fridge_queue_.goal->x, x_y_status.fridge_x, 0.001);
+ EXPECT_NEAR(fridge_queue_.goal->y, x_y_status.fridge_h, 0.001);
+ } else {
+ // Unhandled profiling type.
+ EXPECT_TRUE(false);
+ }
+ }
+
+ // Runs one iteration of the whole simulation and checks that separation
+ // remains reasonable.
+ void RunIteration(bool enabled = true) {
+ SendMessages(enabled);
+
+ fridge_plant_.SendPositionMessage();
+ fridge_.Iterate();
+ fridge_plant_.Simulate();
+
+ TickTime();
+ }
+
+ // Runs iterations until the specified amount of simulated time has elapsed.
+ void RunForTime(const Time &run_for, bool enabled = true) {
+ const auto start_time = Time::Now();
+ while (Time::Now() < start_time + run_for) {
+ RunIteration(enabled);
+ }
+ }
+
+ // Create a new instance of the test queue so that it invalidates the queue
+ // that it points to. Otherwise, we will have a pointed to
+ // shared memory that is no longer valid.
+ FridgeQueue fridge_queue_;
+
+ // Create a control loop and simulation.
+ Fridge fridge_;
+ FridgeSimulation fridge_plant_;
+
+ aos::util::ElevatorArmKinematics kinematics_;
+};
+
+// Tests that the loop does nothing when the goal is zero.
+TEST_F(FridgeTest, DoesNothing) {
+ // Set the goals to the starting values. This should theoretically guarantee
+ // that the controller does nothing.
+ const auto &values = constants::GetValues();
+ ASSERT_TRUE(fridge_queue_.goal.MakeWithBuilder()
+ .angle(0.0)
+ .height(values.fridge.elevator.lower_limit)
+ .max_velocity(20)
+ .max_acceleration(20)
+ .max_angular_velocity(20)
+ .max_angular_acceleration(20)
+ .Send());
+
+ // Run a few iterations.
+ RunForTime(Time::InSeconds(5));
+
+ VerifyNearGoal();
+}
+
+// Tests that the loop can reach a goal.
+TEST_F(FridgeTest, ReachesXYGoal) {
+ // Set a reasonable goal.
+ const auto &values = constants::GetValues();
+ const double soft_limit = values.fridge.elevator.lower_limit;
+ const double height = soft_limit + 0.4;
+ const double angle = M_PI / 6.0;
+
+ aos::util::ElevatorArmKinematics::KinematicResult x_y_goals;
+ kinematics_.ForwardKinematic(height, angle, 0.0, 0.0, &x_y_goals);
+
+ ASSERT_TRUE(fridge_queue_.goal.MakeWithBuilder()
+ .profiling_type(1)
+ .x(x_y_goals.fridge_x)
+ .y(x_y_goals.fridge_h)
+ .max_x_velocity(20)
+ .max_y_velocity(20)
+ .max_x_acceleration(20)
+ .max_y_acceleration(20)
+ .Send());
+
+ // Give it a lot of time to get there.
+ RunForTime(Time::InSeconds(5));
+
+ VerifyNearGoal();
+}
+
+// Tests that the loop can reach a goal.
+TEST_F(FridgeTest, ReachesGoal) {
+ // Set a reasonable goal.
+ const auto &values = constants::GetValues();
+ const double soft_limit = values.fridge.elevator.lower_limit;
+ ASSERT_TRUE(fridge_queue_.goal.MakeWithBuilder()
+ .angle(M_PI / 4.0)
+ .height(soft_limit + 0.5)
+ .max_velocity(20)
+ .max_acceleration(20)
+ .max_angular_velocity(20)
+ .max_angular_acceleration(20)
+ .Send());
+
+ // Give it a lot of time to get there.
+ RunForTime(Time::InSeconds(5));
+
+ VerifyNearGoal();
+}
+
+// Tests that the loop doesn't try and go beyond the physical range of the
+// mechanisms.
+TEST_F(FridgeTest, RespectsRange) {
+ // Put the arm up to get it out of the way.
+ // We're going to send the elevator to -5, which should be significantly too
+ // low.
+ ASSERT_TRUE(fridge_queue_.goal.MakeWithBuilder()
+ .angle(M_PI)
+ .height(-5.0)
+ .max_velocity(20)
+ .max_acceleration(20)
+ .max_angular_velocity(20)
+ .max_angular_acceleration(20)
+ .Send());
+
+ RunForTime(Time::InSeconds(10));
+
+ // Check that we are near our soft limit.
+ fridge_queue_.status.FetchLatest();
+ const auto &values = constants::GetValues();
+ EXPECT_NEAR(values.fridge.elevator.lower_limit, fridge_queue_.status->height,
+ 0.001);
+ EXPECT_NEAR(values.fridge.arm.upper_limit, fridge_queue_.status->angle,
+ 0.001);
+
+ // Put the arm down to get it out of the way.
+ // We're going to give the elevator some ridiculously high goal.
+ ASSERT_TRUE(fridge_queue_.goal.MakeWithBuilder()
+ .angle(-M_PI)
+ .height(50.0)
+ .max_velocity(20)
+ .max_acceleration(20)
+ .max_angular_velocity(20)
+ .max_angular_acceleration(20)
+ .Send());
+
+ RunForTime(Time::InSeconds(10));
+
+ // Check that we are near our soft limit.
+ fridge_queue_.status.FetchLatest();
+ EXPECT_NEAR(values.fridge.elevator.upper_limit, fridge_queue_.status->height,
+ 0.001);
+ EXPECT_NEAR(values.fridge.arm.lower_limit, fridge_queue_.status->angle,
+ 0.001);
+}
+
+// Tests that the loop zeroes when run for a while.
+TEST_F(FridgeTest, ZeroTest) {
+ fridge_queue_.goal.MakeWithBuilder()
+ .angle(0.0)
+ .height(0.5)
+ .max_velocity(20)
+ .max_acceleration(20)
+ .max_angular_velocity(20)
+ .max_angular_acceleration(20)
+ .Send();
+ RunForTime(Time::InSeconds(5));
+
+ VerifyNearGoal();
+}
+
+// Tests that starting at the lower hardstops doesn't cause an abort.
+TEST_F(FridgeTest, LowerHardstopStartup) {
+ fridge_plant_.InitializeElevatorPosition(
+ constants::GetValues().fridge.elevator.lower_hard_limit,
+ constants::GetValues().fridge.elevator.lower_hard_limit);
+ fridge_plant_.InitializeArmPosition(
+ constants::GetValues().fridge.arm.lower_hard_limit,
+ constants::GetValues().fridge.arm.lower_hard_limit);
+ fridge_queue_.goal.MakeWithBuilder().angle(0.0).height(0.4).Send();
+ // We have to wait for it to put the elevator in a safe position as well.
+ RunForTime(Time::InMS(8000));
+
+ VerifyNearGoal();
+}
+
+// Tests that starting at the upper hardstops doesn't cause an abort.
+TEST_F(FridgeTest, UpperHardstopStartup) {
+ fridge_plant_.InitializeElevatorPosition(
+ constants::GetValues().fridge.elevator.upper_hard_limit,
+ constants::GetValues().fridge.elevator.upper_hard_limit);
+ fridge_plant_.InitializeArmPosition(
+ constants::GetValues().fridge.arm.upper_hard_limit,
+ constants::GetValues().fridge.arm.upper_hard_limit);
+ fridge_queue_.goal.MakeWithBuilder().angle(0.0).height(0.4).Send();
+ RunForTime(Time::InMS(5000));
+
+ VerifyNearGoal();
+}
+
+// Tests that starting with an initial arm difference triggers an ESTOP.
+TEST_F(FridgeTest, ArmFarApartEstop) {
+ fridge_queue_.goal.MakeWithBuilder().angle(0.0).height(0.4).Send();
+
+ do {
+ fridge_plant_.InitializeArmPosition(
+ constants::GetValues().fridge.arm.lower_limit,
+ constants::GetValues().fridge.arm.lower_limit + 0.2);
+ SendMessages(true);
+ fridge_plant_.SendPositionMessage();
+ fridge_.Iterate();
+ fridge_plant_.Simulate();
+ TickTime();
+ } while (fridge_.state() == Fridge::INITIALIZING);
+
+ EXPECT_EQ(Fridge::ZEROING_ELEVATOR, fridge_.state());
+
+ // TODO(austin): We should estop earlier once Phil's code to detect issues
+ // before the index pulse is merged.
+ while (fridge_.state() != Fridge::RUNNING &&
+ fridge_.state() != Fridge::ESTOP) {
+ SendMessages(true);
+ fridge_plant_.SendPositionMessage();
+ fridge_.Iterate();
+ fridge_plant_.Simulate();
+ TickTime();
+ }
+
+ EXPECT_EQ(Fridge::ESTOP, fridge_.state());
+}
+
+// Tests that starting with an initial elevator difference triggers an ESTOP.
+TEST_F(FridgeTest, ElevatorFarApartEstop) {
+ fridge_queue_.goal.MakeWithBuilder().angle(0.0).height(0.4).Send();
+
+ do {
+ fridge_plant_.InitializeElevatorPosition(
+ constants::GetValues().fridge.elevator.lower_limit,
+ constants::GetValues().fridge.elevator.lower_limit + 0.1);
+ SendMessages(true);
+ fridge_plant_.SendPositionMessage();
+ fridge_.Iterate();
+ fridge_plant_.Simulate();
+ TickTime();
+ } while (fridge_.state() == Fridge::INITIALIZING);
+
+ EXPECT_EQ(Fridge::ZEROING_ELEVATOR, fridge_.state());
+
+ // TODO(austin): We should estop earlier once Phil's code to detect issues
+ // before the index pulse is merged.
+ while (fridge_.state() != Fridge::RUNNING &&
+ fridge_.state() != Fridge::ESTOP) {
+ SendMessages(true);
+ fridge_plant_.SendPositionMessage();
+ fridge_.Iterate();
+ fridge_plant_.Simulate();
+ TickTime();
+ }
+
+ EXPECT_EQ(Fridge::ESTOP, fridge_.state());
+}
+
+// Tests that starting with an initial elevator difference converges back to 0
+// error when zeroed.
+TEST_F(FridgeTest, ElevatorFixError) {
+ fridge_queue_.goal.MakeWithBuilder()
+ .angle(0.0)
+ .height(0.2)
+ .max_velocity(20)
+ .max_acceleration(20)
+ .Send();
+
+ do {
+ fridge_plant_.InitializeElevatorPosition(
+ constants::GetValues().fridge.elevator.lower_limit,
+ constants::GetValues().fridge.elevator.lower_limit + 0.01);
+ fridge_plant_.ErrorOnAbsoluteDifference();
+ SendMessages(true);
+ fridge_plant_.SendPositionMessage();
+ fridge_.Iterate();
+ fridge_plant_.Simulate();
+ TickTime();
+ } while (fridge_.state() == Fridge::INITIALIZING);
+
+ EXPECT_EQ(Fridge::ZEROING_ELEVATOR, fridge_.state());
+
+ RunForTime(Time::InSeconds(5));
+ VerifyNearGoal();
+}
+
+// Tests that starting with an initial arm difference converges back to 0
+// error when zeroed.
+TEST_F(FridgeTest, ArmFixError) {
+ fridge_queue_.goal.MakeWithBuilder()
+ .angle(0.0)
+ .height(0.2)
+ .max_angular_velocity(20)
+ .max_angular_acceleration(20)
+ .Send();
+
+ do {
+ fridge_plant_.InitializeArmPosition(0.0, 0.02);
+ fridge_plant_.ErrorOnAbsoluteDifference();
+ SendMessages(true);
+ fridge_plant_.SendPositionMessage();
+ fridge_.Iterate();
+ fridge_plant_.Simulate();
+ TickTime();
+ } while (fridge_.state() == Fridge::INITIALIZING);
+
+ EXPECT_EQ(Fridge::ZEROING_ELEVATOR, fridge_.state());
+
+ RunForTime(Time::InSeconds(5));
+ VerifyNearGoal();
+}
+
+// Tests that resetting WPILib results in a rezero.
+TEST_F(FridgeTest, ResetTest) {
+ fridge_plant_.InitializeElevatorPosition(
+ constants::GetValues().fridge.elevator.upper_hard_limit,
+ constants::GetValues().fridge.elevator.upper_hard_limit);
+ fridge_plant_.InitializeArmPosition(
+ constants::GetValues().fridge.arm.upper_hard_limit,
+ constants::GetValues().fridge.arm.upper_hard_limit);
+ fridge_queue_.goal.MakeWithBuilder().angle(0.03).height(0.45).Send();
+ RunForTime(Time::InMS(5000));
+
+ EXPECT_EQ(Fridge::RUNNING, fridge_.state());
+ VerifyNearGoal();
+ SimulateSensorReset();
+ RunForTime(Time::InMS(100));
+ EXPECT_NE(Fridge::RUNNING, fridge_.state());
+ RunForTime(Time::InMS(6000));
+ EXPECT_EQ(Fridge::RUNNING, fridge_.state());
+ VerifyNearGoal();
+}
+
+// Tests that the internal goals don't change while disabled.
+TEST_F(FridgeTest, DisabledGoalTest) {
+ fridge_queue_.goal.MakeWithBuilder().angle(0.03).height(0.45).Send();
+
+ RunForTime(Time::InMS(100), false);
+ EXPECT_EQ(0.0, fridge_.elevator_goal_);
+ EXPECT_EQ(0.0, fridge_.arm_goal_);
+
+ // Now make sure they move correctly
+ RunForTime(Time::InMS(4000), true);
+ EXPECT_NE(0.0, fridge_.elevator_goal_);
+ EXPECT_NE(0.0, fridge_.arm_goal_);
+}
+
+// Tests that the elevator zeroing goals don't wind up too far.
+TEST_F(FridgeTest, ElevatorGoalPositiveWindupTest) {
+ fridge_queue_.goal.MakeWithBuilder().angle(0.03).height(0.45).Send();
+
+ while (fridge_.state() != Fridge::ZEROING_ELEVATOR) {
+ RunIteration();
+ }
+
+ // Kick it.
+ RunForTime(Time::InMS(50));
+ double orig_fridge_goal = fridge_.elevator_goal_;
+ fridge_.elevator_goal_ += 100.0;
+
+ RunIteration();
+ EXPECT_NEAR(orig_fridge_goal, fridge_.elevator_goal_, 0.05);
+
+ RunIteration();
+
+ EXPECT_EQ(fridge_.elevator_loop_->U(), fridge_.elevator_loop_->U_uncapped());
+}
+
+// Tests that the arm zeroing goals don't wind up too far.
+TEST_F(FridgeTest, ArmGoalPositiveWindupTest) {
+ fridge_queue_.goal.MakeWithBuilder().angle(0.03).height(0.45).Send();
+
+ int i = 0;
+ while (fridge_.state() != Fridge::ZEROING_ARM) {
+ RunIteration();
+ ++i;
+ ASSERT_LE(i, 10000);
+ }
+
+ // Kick it.
+ RunForTime(Time::InMS(50));
+ double orig_fridge_goal = fridge_.arm_goal_;
+ fridge_.arm_goal_ += 100.0;
+
+ RunIteration();
+ EXPECT_NEAR(orig_fridge_goal, fridge_.arm_goal_, 0.05);
+
+ RunIteration();
+
+ EXPECT_EQ(fridge_.arm_loop_->U(), fridge_.arm_loop_->U_uncapped());
+}
+
+// Tests that the elevator zeroing goals don't wind up too far.
+TEST_F(FridgeTest, ElevatorGoalNegativeWindupTest) {
+ fridge_queue_.goal.MakeWithBuilder().angle(0.03).height(0.45).Send();
+
+ while (fridge_.state() != Fridge::ZEROING_ELEVATOR) {
+ RunIteration();
+ }
+
+ // Kick it.
+ RunForTime(Time::InMS(50));
+ double orig_fridge_goal = fridge_.elevator_goal_;
+ fridge_.elevator_goal_ -= 100.0;
+
+ RunIteration();
+ EXPECT_NEAR(orig_fridge_goal, fridge_.elevator_goal_, 0.05);
+
+ RunIteration();
+
+ EXPECT_EQ(fridge_.elevator_loop_->U(), fridge_.elevator_loop_->U_uncapped());
+}
+
+// Tests that the arm zeroing goals don't wind up too far.
+TEST_F(FridgeTest, ArmGoalNegativeWindupTest) {
+ fridge_queue_.goal.MakeWithBuilder().angle(0.03).height(0.45).Send();
+
+ while (fridge_.state() != Fridge::ZEROING_ARM) {
+ RunIteration();
+ }
+
+ // Kick it.
+ RunForTime(Time::InMS(50));
+ double orig_fridge_goal = fridge_.arm_goal_;
+ fridge_.arm_goal_ -= 100.0;
+
+ RunIteration();
+ EXPECT_NEAR(orig_fridge_goal, fridge_.arm_goal_, 0.05);
+
+ RunIteration();
+
+ EXPECT_EQ(fridge_.arm_loop_->U(), fridge_.arm_loop_->U_uncapped());
+}
+
+// Tests that the loop zeroes when run for a while.
+TEST_F(FridgeTest, ZeroNoGoal) {
+ RunForTime(Time::InSeconds(5));
+
+ EXPECT_EQ(Fridge::RUNNING, fridge_.state());
+}
+
+// Tests that if we start at the bottom, the elevator moves to a safe height
+// before zeroing the arm.
+TEST_F(FridgeTest, SafeArmZeroing) {
+ auto &values = constants::GetValues();
+ fridge_plant_.InitializeElevatorPosition(
+ values.fridge.elevator.lower_hard_limit);
+ fridge_plant_.InitializeArmPosition(M_PI / 4.0);
+
+ const auto start_time = Time::Now();
+ double last_arm_goal = fridge_.arm_goal_;
+ while (Time::Now() < start_time + Time::InMS(4000)) {
+ RunIteration();
+
+ if (fridge_.state() != Fridge::ZEROING_ELEVATOR) {
+ // Wait until we are zeroing the elevator.
+ continue;
+ }
+
+ fridge_queue_.status.FetchLatest();
+ ASSERT_TRUE(fridge_queue_.status.get() != nullptr);
+ if (fridge_queue_.status->height > values.fridge.arm_zeroing_height) {
+ // We had better not be trying to zero the arm...
+ EXPECT_EQ(last_arm_goal, fridge_.arm_goal_);
+ last_arm_goal = fridge_.arm_goal_;
+ }
+ }
+}
+
+// Tests that the arm integrator works.
+TEST_F(FridgeTest, ArmIntegratorTest) {
+ fridge_plant_.InitializeArmPosition(
+ (constants::GetValues().fridge.arm.lower_hard_limit +
+ constants::GetValues().fridge.arm.lower_hard_limit) /
+ 2.0);
+ fridge_plant_.set_arm_power_error(1.0);
+ fridge_queue_.goal.MakeWithBuilder().angle(0.0).height(0.4).Send();
+
+ RunForTime(Time::InMS(8000));
+
+ VerifyNearGoal();
+}
+
+// Phil:
+// TODO(austin): Check that we e-stop if encoder index pulse is not n
+// revolutions away from last one. (got extra counts from noise, etc).
+// TODO(austin): Check that we e-stop if pot disagrees too much with encoder
+// after we are zeroed.
+
+} // namespace testing
+} // namespace control_loops
+} // namespace frc971
diff --git a/y2015/control_loops/fridge/fridge_main.cc b/y2015/control_loops/fridge/fridge_main.cc
new file mode 100644
index 0000000..e0fd6ea
--- /dev/null
+++ b/y2015/control_loops/fridge/fridge_main.cc
@@ -0,0 +1,11 @@
+#include "y2015/control_loops/fridge/fridge.h"
+
+#include "aos/linux_code/init.h"
+
+int main() {
+ ::aos::Init();
+ frc971::control_loops::Fridge fridge;
+ fridge.Run();
+ ::aos::Cleanup();
+ return 0;
+}
diff --git a/y2015/control_loops/fridge/integral_arm_plant.cc b/y2015/control_loops/fridge/integral_arm_plant.cc
new file mode 100644
index 0000000..da269d7
--- /dev/null
+++ b/y2015/control_loops/fridge/integral_arm_plant.cc
@@ -0,0 +1,49 @@
+#include "y2015/control_loops/fridge/integral_arm_plant.h"
+
+#include <vector>
+
+#include "frc971/control_loops/state_feedback_loop.h"
+
+namespace frc971 {
+namespace control_loops {
+
+StateFeedbackPlantCoefficients<5, 2, 2> MakeIntegralArmPlantCoefficients() {
+ Eigen::Matrix<double, 5, 5> A;
+ A << 1.0, 0.00479642025454, 0.0, 0.0, 4.92993559969e-05, 0.0, 0.919688585028, 0.0, 0.0, 0.0194484035162, 0.0, 0.0, 0.999539771613, 0.00479566382645, 0.0, 0.0, 0.0, -0.18154390621, 0.919241022297, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0;
+ Eigen::Matrix<double, 5, 2> B;
+ B << 2.46496779984e-05, 2.46496779984e-05, 0.00972420175808, 0.00972420175808, 2.46477449538e-05, -2.46477449538e-05, 0.00972266818532, -0.00972266818532, 0.0, 0.0;
+ Eigen::Matrix<double, 2, 5> C;
+ C << 1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, -1.0, 0.0, 0.0;
+ Eigen::Matrix<double, 2, 2> D;
+ D << 0, 0, 0, 0;
+ Eigen::Matrix<double, 2, 1> U_max;
+ U_max << 12.0, 12.0;
+ Eigen::Matrix<double, 2, 1> U_min;
+ U_min << -12.0, -12.0;
+ return StateFeedbackPlantCoefficients<5, 2, 2>(A, B, C, D, U_max, U_min);
+}
+
+StateFeedbackController<5, 2, 2> MakeIntegralArmController() {
+ Eigen::Matrix<double, 5, 2> L;
+ L << 0.461805946837, 0.461805946837, 5.83483983392, 5.83483983392, 0.429725467802, -0.429725467802, 0.18044816586, -0.18044816586, 31.0623964848, 31.0623964848;
+ Eigen::Matrix<double, 2, 5> K;
+ K << 320.979606093, 21.0129517955, 884.233784759, 36.3637782119, 1.0, 320.979606095, 21.0129517956, -884.233784749, -36.3637782119, 1.0;
+ Eigen::Matrix<double, 5, 5> A_inv;
+ A_inv << 1.0, -0.00521526561559, 0.0, 0.0, 5.21292341391e-05, 0.0, 1.08732457517, 0.0, 0.0, -0.0211467270909, 0.0, 0.0, 0.999513354044, -0.00521444313273, 0.0, 0.0, 0.0, 0.197397150694, 1.08682415753, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0;
+ return StateFeedbackController<5, 2, 2>(L, K, A_inv, MakeIntegralArmPlantCoefficients());
+}
+
+StateFeedbackPlant<5, 2, 2> MakeIntegralArmPlant() {
+ ::std::vector< ::std::unique_ptr<StateFeedbackPlantCoefficients<5, 2, 2>>> plants(1);
+ plants[0] = ::std::unique_ptr<StateFeedbackPlantCoefficients<5, 2, 2>>(new StateFeedbackPlantCoefficients<5, 2, 2>(MakeIntegralArmPlantCoefficients()));
+ return StateFeedbackPlant<5, 2, 2>(&plants);
+}
+
+StateFeedbackLoop<5, 2, 2> MakeIntegralArmLoop() {
+ ::std::vector< ::std::unique_ptr<StateFeedbackController<5, 2, 2>>> controllers(1);
+ controllers[0] = ::std::unique_ptr<StateFeedbackController<5, 2, 2>>(new StateFeedbackController<5, 2, 2>(MakeIntegralArmController()));
+ return StateFeedbackLoop<5, 2, 2>(&controllers);
+}
+
+} // namespace control_loops
+} // namespace frc971
diff --git a/y2015/control_loops/fridge/integral_arm_plant.h b/y2015/control_loops/fridge/integral_arm_plant.h
new file mode 100644
index 0000000..80a4876
--- /dev/null
+++ b/y2015/control_loops/fridge/integral_arm_plant.h
@@ -0,0 +1,20 @@
+#ifndef Y2015_CONTROL_LOOPS_FRIDGE_INTEGRAL_ARM_PLANT_H_
+#define Y2015_CONTROL_LOOPS_FRIDGE_INTEGRAL_ARM_PLANT_H_
+
+#include "frc971/control_loops/state_feedback_loop.h"
+
+namespace frc971 {
+namespace control_loops {
+
+StateFeedbackPlantCoefficients<5, 2, 2> MakeIntegralArmPlantCoefficients();
+
+StateFeedbackController<5, 2, 2> MakeIntegralArmController();
+
+StateFeedbackPlant<5, 2, 2> MakeIntegralArmPlant();
+
+StateFeedbackLoop<5, 2, 2> MakeIntegralArmLoop();
+
+} // namespace control_loops
+} // namespace frc971
+
+#endif // Y2015_CONTROL_LOOPS_FRIDGE_INTEGRAL_ARM_PLANT_H_
diff --git a/y2015/control_loops/fridge/replay_fridge.cc b/y2015/control_loops/fridge/replay_fridge.cc
new file mode 100644
index 0000000..65cc98a
--- /dev/null
+++ b/y2015/control_loops/fridge/replay_fridge.cc
@@ -0,0 +1,24 @@
+#include "aos/common/controls/replay_control_loop.h"
+#include "aos/linux_code/init.h"
+
+#include "y2015/control_loops/fridge/fridge.q.h"
+
+// Reads one or more log files and sends out all the queue messages (in the
+// correct order and at the correct time) to feed a "live" fridge process.
+
+int main(int argc, char **argv) {
+ if (argc <= 1) {
+ fprintf(stderr, "Need at least one file to replay!\n");
+ return EXIT_FAILURE;
+ }
+
+ ::aos::InitNRT();
+
+ ::aos::controls::ControlLoopReplayer<::frc971::control_loops::FridgeQueue>
+ replayer(&::frc971::control_loops::fridge_queue, "fridge");
+ for (int i = 1; i < argc; ++i) {
+ replayer.ProcessFile(argv[i]);
+ }
+
+ ::aos::Cleanup();
+}
diff --git a/y2015/control_loops/python/arm.py b/y2015/control_loops/python/arm.py
new file mode 100755
index 0000000..e17990a
--- /dev/null
+++ b/y2015/control_loops/python/arm.py
@@ -0,0 +1,409 @@
+#!/usr/bin/python
+
+import control_loop
+import controls
+import polytope
+import polydrivetrain
+import numpy
+import math
+import sys
+import matplotlib
+from matplotlib import pylab
+
+
+class Arm(control_loop.ControlLoop):
+ def __init__(self, name="Arm", mass=None):
+ super(Arm, self).__init__(name)
+ # Stall Torque in N m
+ self.stall_torque = 0.476
+ # Stall Current in Amps
+ self.stall_current = 80.730
+ # Free Speed in RPM
+ self.free_speed = 13906.0
+ # Free Current in Amps
+ self.free_current = 5.820
+ # Mass of the arm
+ if mass is None:
+ self.mass = 13.0
+ else:
+ self.mass = mass
+
+ # Resistance of the motor
+ self.R = 12.0 / self.stall_current
+ # Motor velocity constant
+ self.Kv = ((self.free_speed / 60.0 * 2.0 * numpy.pi) /
+ (12.0 - self.R * self.free_current))
+ # Torque constant
+ self.Kt = self.stall_torque / self.stall_current
+ # Gear ratio
+ self.G = (44.0 / 12.0) * (54.0 / 14.0) * (54.0 / 14.0) * (44.0 / 20.0) * (72.0 / 16.0)
+ # Fridge arm length
+ self.r = 32 * 0.0254
+ # Control loop time step
+ self.dt = 0.005
+
+ # Arm moment of inertia
+ self.J = self.r * self.mass
+
+ # Arm left/right spring constant (N*m / radian)
+ self.spring = 100.0
+
+ # State is [average position, average velocity,
+ # position difference/2, velocity difference/2]
+ # Position difference is 1 - 2
+ # Input is [Voltage 1, Voltage 2]
+
+ self.C1 = self.spring / (self.J * 0.5)
+ self.C2 = self.Kt * self.G / (self.J * 0.5 * self.R)
+ self.C3 = self.G * self.G * self.Kt / (self.R * self.J * 0.5 * self.Kv)
+
+ self.A_continuous = numpy.matrix(
+ [[0, 1, 0, 0],
+ [0, -self.C3, 0, 0],
+ [0, 0, 0, 1],
+ [0, 0, -self.C1 * 2.0, -self.C3]])
+
+ print 'Full speed is', self.C2 / self.C3 * 12.0
+
+ print 'Stall arm difference is', 12.0 * self.C2 / self.C1
+ print 'Stall arm difference first principles is', self.stall_torque * self.G / self.spring
+
+ print '5 degrees of arm error is', self.spring / self.r * (math.pi * 5.0 / 180.0)
+
+ # Start with the unmodified input
+ self.B_continuous = numpy.matrix(
+ [[0, 0],
+ [self.C2 / 2.0, self.C2 / 2.0],
+ [0, 0],
+ [self.C2 / 2.0, -self.C2 / 2.0]])
+
+ self.C = numpy.matrix([[1, 0, 1, 0],
+ [1, 0, -1, 0]])
+ self.D = numpy.matrix([[0, 0],
+ [0, 0]])
+
+ self.A, self.B = self.ContinuousToDiscrete(
+ self.A_continuous, self.B_continuous, self.dt)
+
+ controlability = controls.ctrb(self.A, self.B);
+ print 'Rank of augmented controlability matrix.', numpy.linalg.matrix_rank(
+ controlability)
+
+ q_pos = 0.02
+ q_vel = 0.300
+ q_pos_diff = 0.005
+ q_vel_diff = 0.13
+ self.Q = numpy.matrix([[(1.0 / (q_pos ** 2.0)), 0.0, 0.0, 0.0],
+ [0.0, (1.0 / (q_vel ** 2.0)), 0.0, 0.0],
+ [0.0, 0.0, (1.0 / (q_pos_diff ** 2.0)), 0.0],
+ [0.0, 0.0, 0.0, (1.0 / (q_vel_diff ** 2.0))]])
+
+ self.R = numpy.matrix([[(1.0 / (12.0 ** 2.0)), 0.0],
+ [0.0, 1.0 / (12.0 ** 2.0)]])
+ self.K = controls.dlqr(self.A, self.B, self.Q, self.R)
+ print 'Controller'
+ print self.K
+
+ print 'Controller Poles'
+ print numpy.linalg.eig(self.A - self.B * self.K)[0]
+
+ self.rpl = 0.20
+ self.ipl = 0.05
+ self.PlaceObserverPoles([self.rpl + 1j * self.ipl,
+ self.rpl + 1j * self.ipl,
+ self.rpl - 1j * self.ipl,
+ self.rpl - 1j * self.ipl])
+
+ # The box formed by U_min and U_max must encompass all possible values,
+ # or else Austin's code gets angry.
+ self.U_max = numpy.matrix([[12.0], [12.0]])
+ self.U_min = numpy.matrix([[-12.0], [-12.0]])
+
+ print 'Observer (Converted to a KF)', numpy.linalg.inv(self.A) * self.L
+
+ self.InitializeState()
+
+
+class IntegralArm(Arm):
+ def __init__(self, name="IntegralArm", mass=None):
+ super(IntegralArm, self).__init__(name=name, mass=mass)
+
+ self.A_continuous_unaugmented = self.A_continuous
+ self.A_continuous = numpy.matrix(numpy.zeros((5, 5)))
+ self.A_continuous[0:4, 0:4] = self.A_continuous_unaugmented
+ self.A_continuous[1, 4] = self.C2
+
+ # Start with the unmodified input
+ self.B_continuous_unaugmented = self.B_continuous
+ self.B_continuous = numpy.matrix(numpy.zeros((5, 2)))
+ self.B_continuous[0:4, 0:2] = self.B_continuous_unaugmented
+
+ self.C_unaugmented = self.C
+ self.C = numpy.matrix(numpy.zeros((2, 5)))
+ self.C[0:2, 0:4] = self.C_unaugmented
+
+ self.A, self.B = self.ContinuousToDiscrete(
+ self.A_continuous, self.B_continuous, self.dt)
+ print 'A cont', self.A_continuous
+ print 'B cont', self.B_continuous
+ print 'A discrete', self.A
+
+ q_pos = 0.08
+ q_vel = 0.40
+
+ q_pos_diff = 0.08
+ q_vel_diff = 0.40
+ q_voltage = 6.0
+ self.Q = numpy.matrix([[(q_pos ** 2.0), 0.0, 0.0, 0.0, 0.0],
+ [0.0, (q_vel ** 2.0), 0.0, 0.0, 0.0],
+ [0.0, 0.0, (q_pos_diff ** 2.0), 0.0, 0.0],
+ [0.0, 0.0, 0.0, (q_vel_diff ** 2.0), 0.0],
+ [0.0, 0.0, 0.0, 0.0, (q_voltage ** 2.0)]])
+
+ r_volts = 0.05
+ self.R = numpy.matrix([[(r_volts ** 2.0), 0.0],
+ [0.0, (r_volts ** 2.0)]])
+
+ self.KalmanGain, self.Q_steady = controls.kalman(
+ A=self.A, B=self.B, C=self.C, Q=self.Q, R=self.R)
+
+ self.U_max = numpy.matrix([[12.0], [12.0]])
+ self.U_min = numpy.matrix([[-12.0], [-12.0]])
+
+ self.K_unaugmented = self.K
+ self.K = numpy.matrix(numpy.zeros((2, 5)));
+ self.K[0:2, 0:4] = self.K_unaugmented
+ self.K[0, 4] = 1;
+ self.K[1, 4] = 1;
+ print 'Kal', self.KalmanGain
+ self.L = self.A * self.KalmanGain
+
+ self.InitializeState()
+
+
+def CapU(U):
+ if U[0, 0] - U[1, 0] > 24:
+ return numpy.matrix([[12], [-12]])
+ elif U[0, 0] - U[1, 0] < -24:
+ return numpy.matrix([[-12], [12]])
+ else:
+ max_u = max(U[0, 0], U[1, 0])
+ min_u = min(U[0, 0], U[1, 0])
+ if max_u > 12:
+ return U - (max_u - 12)
+ if min_u < -12:
+ return U - (min_u + 12)
+ return U
+
+
+def run_test(arm, initial_X, goal, max_separation_error=0.01,
+ show_graph=True, iterations=200, controller_arm=None,
+ observer_arm=None):
+ """Runs the arm plant with an initial condition and goal.
+
+ The tests themselves are not terribly sophisticated; I just test for
+ whether the goal has been reached and whether the separation goes
+ outside of the initial and goal values by more than max_separation_error.
+ Prints out something for a failure of either condition and returns
+ False if tests fail.
+ Args:
+ arm: arm object to use.
+ initial_X: starting state.
+ goal: goal state.
+ show_graph: Whether or not to display a graph showing the changing
+ states and voltages.
+ iterations: Number of timesteps to run the model for.
+ controller_arm: arm object to get K from, or None if we should
+ use arm.
+ observer_arm: arm object to use for the observer, or None if we should
+ use the actual state.
+ """
+
+ arm.X = initial_X
+
+ if controller_arm is None:
+ controller_arm = arm
+
+ if observer_arm is not None:
+ observer_arm.X_hat = initial_X + 0.01
+ observer_arm.X_hat = initial_X
+
+ # Various lists for graphing things.
+ t = []
+ x_avg = []
+ x_sep = []
+ x_hat_avg = []
+ x_hat_sep = []
+ v_avg = []
+ v_sep = []
+ u_left = []
+ u_right = []
+
+ sep_plot_gain = 100.0
+
+ for i in xrange(iterations):
+ X_hat = arm.X
+ if observer_arm is not None:
+ X_hat = observer_arm.X_hat
+ x_hat_avg.append(observer_arm.X_hat[0, 0])
+ x_hat_sep.append(observer_arm.X_hat[2, 0] * sep_plot_gain)
+ U = controller_arm.K * (goal - X_hat)
+ U = CapU(U)
+ x_avg.append(arm.X[0, 0])
+ v_avg.append(arm.X[1, 0])
+ x_sep.append(arm.X[2, 0] * sep_plot_gain)
+ v_sep.append(arm.X[3, 0])
+ if observer_arm is not None:
+ observer_arm.PredictObserver(U)
+ arm.Update(U)
+ if observer_arm is not None:
+ observer_arm.Y = arm.Y
+ observer_arm.CorrectObserver(U)
+
+ t.append(i * arm.dt)
+ u_left.append(U[0, 0])
+ u_right.append(U[1, 0])
+
+ print numpy.linalg.inv(arm.A)
+ print "delta time is ", arm.dt
+ print "Velocity at t=0 is ", x_avg[0], v_avg[0], x_sep[0], v_sep[0]
+ print "Velocity at t=1+dt is ", x_avg[1], v_avg[1], x_sep[1], v_sep[1]
+
+ if show_graph:
+ pylab.subplot(2, 1, 1)
+ pylab.plot(t, x_avg, label='x avg')
+ pylab.plot(t, x_sep, label='x sep')
+ if observer_arm is not None:
+ pylab.plot(t, x_hat_avg, label='x_hat avg')
+ pylab.plot(t, x_hat_sep, label='x_hat sep')
+ pylab.legend()
+
+ pylab.subplot(2, 1, 2)
+ pylab.plot(t, u_left, label='u left')
+ pylab.plot(t, u_right, label='u right')
+ pylab.legend()
+ pylab.show()
+
+
+def run_integral_test(arm, initial_X, goal, observer_arm, disturbance,
+ max_separation_error=0.01, show_graph=True,
+ iterations=400):
+ """Runs the integral control arm plant with an initial condition and goal.
+
+ The tests themselves are not terribly sophisticated; I just test for
+ whether the goal has been reached and whether the separation goes
+ outside of the initial and goal values by more than max_separation_error.
+ Prints out something for a failure of either condition and returns
+ False if tests fail.
+ Args:
+ arm: arm object to use.
+ initial_X: starting state.
+ goal: goal state.
+ observer_arm: arm object to use for the observer.
+ show_graph: Whether or not to display a graph showing the changing
+ states and voltages.
+ iterations: Number of timesteps to run the model for.
+ disturbance: Voltage missmatch between controller and model.
+ """
+
+ arm.X = initial_X
+
+ # Various lists for graphing things.
+ t = []
+ x_avg = []
+ x_sep = []
+ x_hat_avg = []
+ x_hat_sep = []
+ v_avg = []
+ v_sep = []
+ u_left = []
+ u_right = []
+ u_error = []
+
+ sep_plot_gain = 100.0
+
+ unaugmented_goal = goal
+ goal = numpy.matrix(numpy.zeros((5, 1)))
+ goal[0:4, 0] = unaugmented_goal
+
+ for i in xrange(iterations):
+ X_hat = observer_arm.X_hat[0:4]
+
+ x_hat_avg.append(observer_arm.X_hat[0, 0])
+ x_hat_sep.append(observer_arm.X_hat[2, 0] * sep_plot_gain)
+
+ U = observer_arm.K * (goal - observer_arm.X_hat)
+ u_error.append(observer_arm.X_hat[4,0])
+ U = CapU(U)
+ x_avg.append(arm.X[0, 0])
+ v_avg.append(arm.X[1, 0])
+ x_sep.append(arm.X[2, 0] * sep_plot_gain)
+ v_sep.append(arm.X[3, 0])
+
+ observer_arm.PredictObserver(U)
+
+ arm.Update(U + disturbance)
+ observer_arm.Y = arm.Y
+ observer_arm.CorrectObserver(U)
+
+ t.append(i * arm.dt)
+ u_left.append(U[0, 0])
+ u_right.append(U[1, 0])
+
+ print 'End is', observer_arm.X_hat[4, 0]
+
+ if show_graph:
+ pylab.subplot(2, 1, 1)
+ pylab.plot(t, x_avg, label='x avg')
+ pylab.plot(t, x_sep, label='x sep')
+ if observer_arm is not None:
+ pylab.plot(t, x_hat_avg, label='x_hat avg')
+ pylab.plot(t, x_hat_sep, label='x_hat sep')
+ pylab.legend()
+
+ pylab.subplot(2, 1, 2)
+ pylab.plot(t, u_left, label='u left')
+ pylab.plot(t, u_right, label='u right')
+ pylab.plot(t, u_error, label='u error')
+ pylab.legend()
+ pylab.show()
+
+
+def main(argv):
+ loaded_mass = 25
+ #loaded_mass = 0
+ arm = Arm(mass=13 + loaded_mass)
+ #arm_controller = Arm(mass=13 + 15)
+ #observer_arm = Arm(mass=13 + 15)
+ #observer_arm = None
+
+ integral_arm = IntegralArm(mass=13 + loaded_mass)
+ integral_arm.X_hat[0, 0] += 0.02
+ integral_arm.X_hat[2, 0] += 0.02
+ integral_arm.X_hat[4] = 0
+
+ # Test moving the arm with constant separation.
+ initial_X = numpy.matrix([[0.0], [0.0], [0.0], [0.0]])
+ R = numpy.matrix([[0.0], [0.0], [0.0], [0.0]])
+ run_integral_test(arm, initial_X, R, integral_arm, disturbance=2)
+
+ # Write the generated constants out to a file.
+ if len(argv) != 5:
+ print "Expected .h file name and .cc file name for the arm and augmented arm."
+ else:
+ arm = Arm("Arm", mass=13)
+ loop_writer = control_loop.ControlLoopWriter("Arm", [arm])
+ if argv[1][-3:] == '.cc':
+ loop_writer.Write(argv[2], argv[1])
+ else:
+ loop_writer.Write(argv[1], argv[2])
+
+ integral_arm = IntegralArm("IntegralArm", mass=13)
+ loop_writer = control_loop.ControlLoopWriter("IntegralArm", [integral_arm])
+ if argv[3][-3:] == '.cc':
+ loop_writer.Write(argv[4], argv[3])
+ else:
+ loop_writer.Write(argv[3], argv[4])
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv))
diff --git a/y2015/control_loops/python/claw.py b/y2015/control_loops/python/claw.py
new file mode 100755
index 0000000..4ca50e9
--- /dev/null
+++ b/y2015/control_loops/python/claw.py
@@ -0,0 +1,211 @@
+#!/usr/bin/python
+
+import control_loop
+import controls
+import polytope
+import polydrivetrain
+import numpy
+import sys
+import matplotlib
+from matplotlib import pylab
+
+class Claw(control_loop.ControlLoop):
+ def __init__(self, name="Claw", mass=None):
+ super(Claw, self).__init__(name)
+ # Stall Torque in N m
+ self.stall_torque = 0.476
+ # Stall Current in Amps
+ self.stall_current = 80.730
+ # Free Speed in RPM
+ self.free_speed = 13906.0
+ # Free Current in Amps
+ self.free_current = 5.820
+ # Mass of the claw
+ if mass is None:
+ self.mass = 5.0
+ else:
+ self.mass = mass
+
+ # Resistance of the motor
+ self.R = 12.0 / self.stall_current
+ # Motor velocity constant
+ self.Kv = ((self.free_speed / 60.0 * 2.0 * numpy.pi) /
+ (12.0 - self.R * self.free_current))
+ # Torque constant
+ self.Kt = self.stall_torque / self.stall_current
+ # Gear ratio
+ self.G = (56.0 / 12.0) * (54.0 / 14.0) * (64.0 / 14.0) * (72.0 / 18.0)
+ # Claw length
+ self.r = 18 * 0.0254
+
+ self.J = self.r * self.mass
+
+ # Control loop time step
+ self.dt = 0.005
+
+ # State is [position, velocity]
+ # Input is [Voltage]
+
+ C1 = self.G * self.G * self.Kt / (self.R * self.J * self.Kv)
+ C2 = self.Kt * self.G / (self.J * self.R)
+
+ self.A_continuous = numpy.matrix(
+ [[0, 1],
+ [0, -C1]])
+
+ # Start with the unmodified input
+ self.B_continuous = numpy.matrix(
+ [[0],
+ [C2]])
+
+ self.C = numpy.matrix([[1, 0]])
+ self.D = numpy.matrix([[0]])
+
+ self.A, self.B = self.ContinuousToDiscrete(
+ self.A_continuous, self.B_continuous, self.dt)
+
+ controlability = controls.ctrb(self.A, self.B);
+
+ print "Free speed is", self.free_speed * numpy.pi * 2.0 / 60.0 / self.G
+
+ q_pos = 0.15
+ q_vel = 2.5
+ self.Q = numpy.matrix([[(1.0 / (q_pos ** 2.0)), 0.0],
+ [0.0, (1.0 / (q_vel ** 2.0))]])
+
+ self.R = numpy.matrix([[(1.0 / (12.0 ** 2.0))]])
+ self.K = controls.dlqr(self.A, self.B, self.Q, self.R)
+
+ print 'K', self.K
+ print 'Poles are', numpy.linalg.eig(self.A - self.B * self.K)[0]
+
+ self.rpl = 0.30
+ self.ipl = 0.10
+ self.PlaceObserverPoles([self.rpl + 1j * self.ipl,
+ self.rpl - 1j * self.ipl])
+
+ print 'L is', self.L
+
+ q_pos = 0.05
+ q_vel = 2.65
+ self.Q = numpy.matrix([[(q_pos ** 2.0), 0.0],
+ [0.0, (q_vel ** 2.0)]])
+
+ r_volts = 0.025
+ self.R = numpy.matrix([[(r_volts ** 2.0)]])
+
+ self.KalmanGain, self.Q_steady = controls.kalman(
+ A=self.A, B=self.B, C=self.C, Q=self.Q, R=self.R)
+
+ print 'Kal', self.KalmanGain
+ self.L = self.A * self.KalmanGain
+ print 'KalL is', self.L
+
+ # The box formed by U_min and U_max must encompass all possible values,
+ # or else Austin's code gets angry.
+ self.U_max = numpy.matrix([[12.0]])
+ self.U_min = numpy.matrix([[-12.0]])
+
+ self.InitializeState()
+
+
+def run_test(claw, initial_X, goal, max_separation_error=0.01,
+ show_graph=True, iterations=200, controller_claw=None,
+ observer_claw=None):
+ """Runs the claw plant with an initial condition and goal.
+
+ The tests themselves are not terribly sophisticated; I just test for
+ whether the goal has been reached and whether the separation goes
+ outside of the initial and goal values by more than max_separation_error.
+ Prints out something for a failure of either condition and returns
+ False if tests fail.
+ Args:
+ claw: claw object to use.
+ initial_X: starting state.
+ goal: goal state.
+ show_graph: Whether or not to display a graph showing the changing
+ states and voltages.
+ iterations: Number of timesteps to run the model for.
+ controller_claw: claw object to get K from, or None if we should
+ use claw.
+ observer_claw: claw object to use for the observer, or None if we should
+ use the actual state.
+ """
+
+ claw.X = initial_X
+
+ if controller_claw is None:
+ controller_claw = claw
+
+ if observer_claw is not None:
+ observer_claw.X_hat = initial_X + 0.01
+ observer_claw.X_hat = initial_X
+
+ # Various lists for graphing things.
+ t = []
+ x = []
+ v = []
+ x_hat = []
+ u = []
+
+ sep_plot_gain = 100.0
+
+ for i in xrange(iterations):
+ X_hat = claw.X
+ if observer_claw is not None:
+ X_hat = observer_claw.X_hat
+ x_hat.append(observer_claw.X_hat[0, 0])
+ U = controller_claw.K * (goal - X_hat)
+ U[0, 0] = numpy.clip(U[0, 0], -12, 12)
+ x.append(claw.X[0, 0])
+ v.append(claw.X[1, 0])
+ if observer_claw is not None:
+ observer_claw.PredictObserver(U)
+ claw.Update(U)
+ if observer_claw is not None:
+ observer_claw.Y = claw.Y
+ observer_claw.CorrectObserver(U)
+
+ t.append(i * claw.dt)
+ u.append(U[0, 0])
+
+ if show_graph:
+ pylab.subplot(2, 1, 1)
+ pylab.plot(t, x, label='x')
+ if observer_claw is not None:
+ pylab.plot(t, x_hat, label='x_hat')
+ pylab.legend()
+
+ pylab.subplot(2, 1, 2)
+ pylab.plot(t, u, label='u')
+ pylab.legend()
+ pylab.show()
+
+
+def main(argv):
+ loaded_mass = 0
+ #loaded_mass = 0
+ claw = Claw(mass=4 + loaded_mass)
+ claw_controller = Claw(mass=5 + 0)
+ observer_claw = Claw(mass=5 + 0)
+ #observer_claw = None
+
+ # Test moving the claw with constant separation.
+ initial_X = numpy.matrix([[0.0], [0.0]])
+ R = numpy.matrix([[1.0], [0.0]])
+ run_test(claw, initial_X, R, controller_claw=claw_controller,
+ observer_claw=observer_claw)
+
+ # Write the generated constants out to a file.
+ if len(argv) != 3:
+ print "Expected .h file name and .cc file name for the claw."
+ else:
+ claw = Claw("Claw")
+ loop_writer = control_loop.ControlLoopWriter("Claw", [claw])
+ if argv[1][-3:] == '.cc':
+ loop_writer.Write(argv[2], argv[1])
+ else:
+ loop_writer.Write(argv[1], argv[2])
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv))
diff --git a/y2015/control_loops/python/drivetrain.py b/y2015/control_loops/python/drivetrain.py
new file mode 100755
index 0000000..8ed62d6
--- /dev/null
+++ b/y2015/control_loops/python/drivetrain.py
@@ -0,0 +1,242 @@
+#!/usr/bin/python
+
+import control_loop
+import controls
+import numpy
+import sys
+from matplotlib import pylab
+
+
+class CIM(control_loop.ControlLoop):
+ def __init__(self):
+ super(CIM, self).__init__("CIM")
+ # Stall Torque in N m
+ self.stall_torque = 2.42
+ # Stall Current in Amps
+ self.stall_current = 133
+ # Free Speed in RPM
+ self.free_speed = 4650.0
+ # Free Current in Amps
+ self.free_current = 2.7
+ # Moment of inertia of the CIM in kg m^2
+ self.J = 0.0001
+ # Resistance of the motor, divided by 2 to account for the 2 motors
+ self.R = 12.0 / self.stall_current
+ # Motor velocity constant
+ self.Kv = ((self.free_speed / 60.0 * 2.0 * numpy.pi) /
+ (12.0 - self.R * self.free_current))
+ # Torque constant
+ self.Kt = self.stall_torque / self.stall_current
+ # Control loop time step
+ self.dt = 0.005
+
+ # State feedback matrices
+ self.A_continuous = numpy.matrix(
+ [[-self.Kt / self.Kv / (self.J * self.R)]])
+ self.B_continuous = numpy.matrix(
+ [[self.Kt / (self.J * self.R)]])
+ self.C = numpy.matrix([[1]])
+ self.D = numpy.matrix([[0]])
+
+ self.A, self.B = self.ContinuousToDiscrete(self.A_continuous,
+ self.B_continuous, self.dt)
+
+ self.PlaceControllerPoles([0.01])
+ self.PlaceObserverPoles([0.01])
+
+ self.U_max = numpy.matrix([[12.0]])
+ self.U_min = numpy.matrix([[-12.0]])
+
+ self.InitializeState()
+
+
+class Drivetrain(control_loop.ControlLoop):
+ def __init__(self, name="Drivetrain", left_low=True, right_low=True):
+ super(Drivetrain, self).__init__(name)
+ # Stall Torque in N m
+ self.stall_torque = 2.42
+ # Stall Current in Amps
+ self.stall_current = 133.0
+ # Free Speed in RPM. Used number from last year.
+ self.free_speed = 4650.0
+ # Free Current in Amps
+ self.free_current = 2.7
+ # Moment of inertia of the drivetrain in kg m^2
+ # Just borrowed from last year.
+ self.J = 10
+ # Mass of the robot, in kg.
+ self.m = 68
+ # Radius of the robot, in meters (from last year).
+ self.rb = 0.9603 / 2.0
+ # Radius of the wheels, in meters.
+ self.r = .0515938
+ # Resistance of the motor, divided by the number of motors.
+ self.R = 12.0 / self.stall_current / 2
+ # Motor velocity constant
+ self.Kv = ((self.free_speed / 60.0 * 2.0 * numpy.pi) /
+ (12.0 - self.R * self.free_current))
+ # Torque constant
+ self.Kt = self.stall_torque / self.stall_current
+ # Gear ratios
+ self.G_const = 28.0 / 50.0 * 20.0 / 64.0
+
+ self.G_low = self.G_const
+ self.G_high = self.G_const
+
+ if left_low:
+ self.Gl = self.G_low
+ else:
+ self.Gl = self.G_high
+ if right_low:
+ self.Gr = self.G_low
+ else:
+ self.Gr = self.G_high
+
+ # Control loop time step
+ self.dt = 0.005
+
+ # These describe the way that a given side of a robot will be influenced
+ # by the other side. Units of 1 / kg.
+ self.msp = 1.0 / self.m + self.rb * self.rb / self.J
+ self.msn = 1.0 / self.m - self.rb * self.rb / self.J
+ # The calculations which we will need for A and B.
+ self.tcl = -self.Kt / self.Kv / (self.Gl * self.Gl * self.R * self.r * self.r)
+ self.tcr = -self.Kt / self.Kv / (self.Gr * self.Gr * self.R * self.r * self.r)
+ self.mpl = self.Kt / (self.Gl * self.R * self.r)
+ self.mpr = self.Kt / (self.Gr * self.R * self.r)
+
+ # State feedback matrices
+ # X will be of the format
+ # [[positionl], [velocityl], [positionr], velocityr]]
+ self.A_continuous = numpy.matrix(
+ [[0, 1, 0, 0],
+ [0, self.msp * self.tcl, 0, self.msn * self.tcr],
+ [0, 0, 0, 1],
+ [0, self.msn * self.tcl, 0, self.msp * self.tcr]])
+ self.B_continuous = numpy.matrix(
+ [[0, 0],
+ [self.msp * self.mpl, self.msn * self.mpr],
+ [0, 0],
+ [self.msn * self.mpl, self.msp * self.mpr]])
+ self.C = numpy.matrix([[1, 0, 0, 0],
+ [0, 0, 1, 0]])
+ self.D = numpy.matrix([[0, 0],
+ [0, 0]])
+
+ #print "THE NUMBER I WANT" + str(numpy.linalg.inv(self.A_continuous) * -self.B_continuous * numpy.matrix([[12.0], [12.0]]))
+ self.A, self.B = self.ContinuousToDiscrete(
+ self.A_continuous, self.B_continuous, self.dt)
+
+ # Poles from last year.
+ self.hp = 0.65
+ self.lp = 0.83
+ self.PlaceControllerPoles([self.hp, self.lp, self.hp, self.lp])
+ print self.K
+ q_pos = 0.07
+ q_vel = 1.0
+ self.Q = numpy.matrix([[(1.0 / (q_pos ** 2.0)), 0.0, 0.0, 0.0],
+ [0.0, (1.0 / (q_vel ** 2.0)), 0.0, 0.0],
+ [0.0, 0.0, (1.0 / (q_pos ** 2.0)), 0.0],
+ [0.0, 0.0, 0.0, (1.0 / (q_vel ** 2.0))]])
+
+ self.R = numpy.matrix([[(1.0 / (12.0 ** 2.0)), 0.0],
+ [0.0, (1.0 / (12.0 ** 2.0))]])
+ self.K = controls.dlqr(self.A, self.B, self.Q, self.R)
+ print self.A
+ print self.B
+ print self.K
+ print numpy.linalg.eig(self.A - self.B * self.K)[0]
+
+ self.hlp = 0.3
+ self.llp = 0.4
+ self.PlaceObserverPoles([self.hlp, self.hlp, self.llp, self.llp])
+
+ self.U_max = numpy.matrix([[12.0], [12.0]])
+ self.U_min = numpy.matrix([[-12.0], [-12.0]])
+ self.InitializeState()
+
+def main(argv):
+ # Simulate the response of the system to a step input.
+ drivetrain = Drivetrain()
+ simulated_left = []
+ simulated_right = []
+ for _ in xrange(100):
+ drivetrain.Update(numpy.matrix([[12.0], [12.0]]))
+ simulated_left.append(drivetrain.X[0, 0])
+ simulated_right.append(drivetrain.X[2, 0])
+
+ #pylab.plot(range(100), simulated_left)
+ #pylab.plot(range(100), simulated_right)
+ #pylab.show()
+
+ # Simulate forwards motion.
+ drivetrain = Drivetrain()
+ close_loop_left = []
+ close_loop_right = []
+ R = numpy.matrix([[1.0], [0.0], [1.0], [0.0]])
+ for _ in xrange(100):
+ U = numpy.clip(drivetrain.K * (R - drivetrain.X_hat),
+ drivetrain.U_min, drivetrain.U_max)
+ drivetrain.UpdateObserver(U)
+ drivetrain.Update(U)
+ close_loop_left.append(drivetrain.X[0, 0])
+ close_loop_right.append(drivetrain.X[2, 0])
+
+ #pylab.plot(range(100), close_loop_left)
+ #pylab.plot(range(100), close_loop_right)
+ #pylab.show()
+
+ # Try turning in place
+ drivetrain = Drivetrain()
+ close_loop_left = []
+ close_loop_right = []
+ R = numpy.matrix([[-1.0], [0.0], [1.0], [0.0]])
+ for _ in xrange(100):
+ U = numpy.clip(drivetrain.K * (R - drivetrain.X_hat),
+ drivetrain.U_min, drivetrain.U_max)
+ drivetrain.UpdateObserver(U)
+ drivetrain.Update(U)
+ close_loop_left.append(drivetrain.X[0, 0])
+ close_loop_right.append(drivetrain.X[2, 0])
+
+ #pylab.plot(range(100), close_loop_left)
+ #pylab.plot(range(100), close_loop_right)
+ #pylab.show()
+
+ # Try turning just one side.
+ drivetrain = Drivetrain()
+ close_loop_left = []
+ close_loop_right = []
+ R = numpy.matrix([[0.0], [0.0], [1.0], [0.0]])
+ for _ in xrange(100):
+ U = numpy.clip(drivetrain.K * (R - drivetrain.X_hat),
+ drivetrain.U_min, drivetrain.U_max)
+ drivetrain.UpdateObserver(U)
+ drivetrain.Update(U)
+ close_loop_left.append(drivetrain.X[0, 0])
+ close_loop_right.append(drivetrain.X[2, 0])
+
+ #pylab.plot(range(100), close_loop_left)
+ #pylab.plot(range(100), close_loop_right)
+ #pylab.show()
+
+ # Write the generated constants out to a file.
+ print "Output one"
+ drivetrain_low_low = Drivetrain(name="DrivetrainLowLow", left_low=True, right_low=True)
+ drivetrain_low_high = Drivetrain(name="DrivetrainLowHigh", left_low=True, right_low=False)
+ drivetrain_high_low = Drivetrain(name="DrivetrainHighLow", left_low=False, right_low=True)
+ drivetrain_high_high = Drivetrain(name="DrivetrainHighHigh", left_low=False, right_low=False)
+
+ if len(argv) != 5:
+ print "Expected .h file name and .cc file name"
+ else:
+ dog_loop_writer = control_loop.ControlLoopWriter(
+ "Drivetrain", [drivetrain_low_low, drivetrain_low_high,
+ drivetrain_high_low, drivetrain_high_high])
+ if argv[1][-3:] == '.cc':
+ dog_loop_writer.Write(argv[2], argv[1])
+ else:
+ dog_loop_writer.Write(argv[1], argv[2])
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv))
diff --git a/y2015/control_loops/python/elevator.py b/y2015/control_loops/python/elevator.py
new file mode 100755
index 0000000..fba72c8
--- /dev/null
+++ b/y2015/control_loops/python/elevator.py
@@ -0,0 +1,246 @@
+#!/usr/bin/python
+
+import control_loop
+import controls
+import polytope
+import polydrivetrain
+import numpy
+import sys
+import matplotlib
+from matplotlib import pylab
+
+class Elevator(control_loop.ControlLoop):
+ def __init__(self, name="Elevator", mass=None):
+ super(Elevator, self).__init__(name)
+ # Stall Torque in N m
+ self.stall_torque = 0.476
+ # Stall Current in Amps
+ self.stall_current = 80.730
+ # Free Speed in RPM
+ self.free_speed = 13906.0
+ # Free Current in Amps
+ self.free_current = 5.820
+ # Mass of the elevator
+ if mass is None:
+ self.mass = 13.0
+ else:
+ self.mass = mass
+
+ # Resistance of the motor
+ self.R = 12.0 / self.stall_current
+ # Motor velocity constant
+ self.Kv = ((self.free_speed / 60.0 * 2.0 * numpy.pi) /
+ (12.0 - self.R * self.free_current))
+ # Torque constant
+ self.Kt = self.stall_torque / self.stall_current
+ # Gear ratio
+ self.G = (56.0 / 12.0) * (84.0 / 14.0)
+ # Pulley diameter
+ self.r = 32 * 0.005 / numpy.pi / 2.0
+ # Control loop time step
+ self.dt = 0.005
+
+ # Elevator left/right spring constant (N/m)
+ self.spring = 800.0
+
+ # State is [average position, average velocity,
+ # position difference/2, velocity difference/2]
+ # Input is [V_left, V_right]
+
+ C1 = self.spring / (self.mass * 0.5)
+ C2 = self.Kt * self.G / (self.mass * 0.5 * self.r * self.R)
+ C3 = self.G * self.G * self.Kt / (
+ self.R * self.r * self.r * self.mass * 0.5 * self.Kv)
+
+ self.A_continuous = numpy.matrix(
+ [[0, 1, 0, 0],
+ [0, -C3, 0, 0],
+ [0, 0, 0, 1],
+ [0, 0, -C1 * 2.0, -C3]])
+
+ print "Full speed is", C2 / C3 * 12.0
+
+ # Start with the unmodified input
+ self.B_continuous = numpy.matrix(
+ [[0, 0],
+ [C2 / 2.0, C2 / 2.0],
+ [0, 0],
+ [C2 / 2.0, -C2 / 2.0]])
+
+ self.C = numpy.matrix([[1, 0, 1, 0],
+ [1, 0, -1, 0]])
+ self.D = numpy.matrix([[0, 0],
+ [0, 0]])
+
+ self.A, self.B = self.ContinuousToDiscrete(
+ self.A_continuous, self.B_continuous, self.dt)
+
+ print self.A
+
+ controlability = controls.ctrb(self.A, self.B);
+ print "Rank of augmented controlability matrix.", numpy.linalg.matrix_rank(
+ controlability)
+
+ q_pos = 0.02
+ q_vel = 0.400
+ q_pos_diff = 0.01
+ q_vel_diff = 0.45
+ self.Q = numpy.matrix([[(1.0 / (q_pos ** 2.0)), 0.0, 0.0, 0.0],
+ [0.0, (1.0 / (q_vel ** 2.0)), 0.0, 0.0],
+ [0.0, 0.0, (1.0 / (q_pos_diff ** 2.0)), 0.0],
+ [0.0, 0.0, 0.0, (1.0 / (q_vel_diff ** 2.0))]])
+
+ self.R = numpy.matrix([[(1.0 / (12.0 ** 2.0)), 0.0],
+ [0.0, 1.0 / (12.0 ** 2.0)]])
+ self.K = controls.dlqr(self.A, self.B, self.Q, self.R)
+ print self.K
+
+ print numpy.linalg.eig(self.A - self.B * self.K)[0]
+
+ self.rpl = 0.20
+ self.ipl = 0.05
+ self.PlaceObserverPoles([self.rpl + 1j * self.ipl,
+ self.rpl + 1j * self.ipl,
+ self.rpl - 1j * self.ipl,
+ self.rpl - 1j * self.ipl])
+
+ # The box formed by U_min and U_max must encompass all possible values,
+ # or else Austin's code gets angry.
+ self.U_max = numpy.matrix([[12.0], [12.0]])
+ self.U_min = numpy.matrix([[-12.0], [-12.0]])
+
+ self.InitializeState()
+
+
+def CapU(U):
+ if U[0, 0] - U[1, 0] > 24:
+ return numpy.matrix([[12], [-12]])
+ elif U[0, 0] - U[1, 0] < -24:
+ return numpy.matrix([[-12], [12]])
+ else:
+ max_u = max(U[0, 0], U[1, 0])
+ min_u = min(U[0, 0], U[1, 0])
+ if max_u > 12:
+ return U - (max_u - 12)
+ if min_u < -12:
+ return U - (min_u + 12)
+ return U
+
+
+def run_test(elevator, initial_X, goal, max_separation_error=0.01,
+ show_graph=True, iterations=200, controller_elevator=None,
+ observer_elevator=None):
+ """Runs the elevator plant with an initial condition and goal.
+
+ The tests themselves are not terribly sophisticated; I just test for
+ whether the goal has been reached and whether the separation goes
+ outside of the initial and goal values by more than max_separation_error.
+ Prints out something for a failure of either condition and returns
+ False if tests fail.
+ Args:
+ elevator: elevator object to use.
+ initial_X: starting state.
+ goal: goal state.
+ show_graph: Whether or not to display a graph showing the changing
+ states and voltages.
+ iterations: Number of timesteps to run the model for.
+ controller_elevator: elevator object to get K from, or None if we should
+ use elevator.
+ observer_elevator: elevator object to use for the observer, or None if we
+ should use the actual state.
+ """
+
+ elevator.X = initial_X
+
+ if controller_elevator is None:
+ controller_elevator = elevator
+
+ if observer_elevator is not None:
+ observer_elevator.X_hat = initial_X + 0.01
+ observer_elevator.X_hat = initial_X
+
+ # Various lists for graphing things.
+ t = []
+ x_avg = []
+ x_sep = []
+ x_hat_avg = []
+ x_hat_sep = []
+ v_avg = []
+ v_sep = []
+ u_left = []
+ u_right = []
+
+ sep_plot_gain = 100.0
+
+ for i in xrange(iterations):
+ X_hat = elevator.X
+ if observer_elevator is not None:
+ X_hat = observer_elevator.X_hat
+ x_hat_avg.append(observer_elevator.X_hat[0, 0])
+ x_hat_sep.append(observer_elevator.X_hat[2, 0] * sep_plot_gain)
+ U = controller_elevator.K * (goal - X_hat)
+ U = CapU(U)
+ x_avg.append(elevator.X[0, 0])
+ v_avg.append(elevator.X[1, 0])
+ x_sep.append(elevator.X[2, 0] * sep_plot_gain)
+ v_sep.append(elevator.X[3, 0])
+ if observer_elevator is not None:
+ observer_elevator.PredictObserver(U)
+ elevator.Update(U)
+ if observer_elevator is not None:
+ observer_elevator.Y = elevator.Y
+ observer_elevator.CorrectObserver(U)
+
+ t.append(i * elevator.dt)
+ u_left.append(U[0, 0])
+ u_right.append(U[1, 0])
+
+ print numpy.linalg.inv(elevator.A)
+ print "delta time is ", elevator.dt
+ print "Velocity at t=0 is ", x_avg[0], v_avg[0], x_sep[0], v_sep[0]
+ print "Velocity at t=1+dt is ", x_avg[1], v_avg[1], x_sep[1], v_sep[1]
+
+ if show_graph:
+ pylab.subplot(2, 1, 1)
+ pylab.plot(t, x_avg, label='x avg')
+ pylab.plot(t, x_sep, label='x sep')
+ if observer_elevator is not None:
+ pylab.plot(t, x_hat_avg, label='x_hat avg')
+ pylab.plot(t, x_hat_sep, label='x_hat sep')
+ pylab.legend()
+
+ pylab.subplot(2, 1, 2)
+ pylab.plot(t, u_left, label='u left')
+ pylab.plot(t, u_right, label='u right')
+ pylab.legend()
+ pylab.show()
+
+
+def main(argv):
+ loaded_mass = 25
+ #loaded_mass = 0
+ elevator = Elevator(mass=13 + loaded_mass)
+ elevator_controller = Elevator(mass=13 + 15)
+ observer_elevator = Elevator(mass=13 + 15)
+ #observer_elevator = None
+
+ # Test moving the elevator with constant separation.
+ initial_X = numpy.matrix([[0.0], [0.0], [0.01], [0.0]])
+ #initial_X = numpy.matrix([[0.0], [0.0], [0.00], [0.0]])
+ R = numpy.matrix([[1.0], [0.0], [0.0], [0.0]])
+ run_test(elevator, initial_X, R, controller_elevator=elevator_controller,
+ observer_elevator=observer_elevator)
+
+ # Write the generated constants out to a file.
+ if len(argv) != 3:
+ print "Expected .h file name and .cc file name for the elevator."
+ else:
+ elevator = Elevator("Elevator")
+ loop_writer = control_loop.ControlLoopWriter("Elevator", [elevator])
+ if argv[1][-3:] == '.cc':
+ loop_writer.Write(argv[2], argv[1])
+ else:
+ loop_writer.Write(argv[1], argv[2])
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv))
diff --git a/y2015/control_loops/python/polydrivetrain.py b/y2015/control_loops/python/polydrivetrain.py
new file mode 100755
index 0000000..a62c8c8
--- /dev/null
+++ b/y2015/control_loops/python/polydrivetrain.py
@@ -0,0 +1,504 @@
+#!/usr/bin/python
+
+import numpy
+import sys
+import polytope
+import drivetrain
+import control_loop
+import controls
+from matplotlib import pylab
+
+__author__ = 'Austin Schuh (austin.linux@gmail.com)'
+
+
+def CoerceGoal(region, K, w, R):
+ """Intersects a line with a region, and finds the closest point to R.
+
+ Finds a point that is closest to R inside the region, and on the line
+ defined by K X = w. If it is not possible to find a point on the line,
+ finds a point that is inside the region and closest to the line. This
+ function assumes that
+
+ Args:
+ region: HPolytope, the valid goal region.
+ K: numpy.matrix (2 x 1), the matrix for the equation [K1, K2] [x1; x2] = w
+ w: float, the offset in the equation above.
+ R: numpy.matrix (2 x 1), the point to be closest to.
+
+ Returns:
+ numpy.matrix (2 x 1), the point.
+ """
+ return DoCoerceGoal(region, K, w, R)[0]
+
+def DoCoerceGoal(region, K, w, R):
+ if region.IsInside(R):
+ return (R, True)
+
+ perpendicular_vector = K.T / numpy.linalg.norm(K)
+ parallel_vector = numpy.matrix([[perpendicular_vector[1, 0]],
+ [-perpendicular_vector[0, 0]]])
+
+ # We want to impose the constraint K * X = w on the polytope H * X <= k.
+ # We do this by breaking X up into parallel and perpendicular components to
+ # the half plane. This gives us the following equation.
+ #
+ # parallel * (parallel.T \dot X) + perpendicular * (perpendicular \dot X)) = X
+ #
+ # Then, substitute this into the polytope.
+ #
+ # H * (parallel * (parallel.T \dot X) + perpendicular * (perpendicular \dot X)) <= k
+ #
+ # Substitute K * X = w
+ #
+ # H * parallel * (parallel.T \dot X) + H * perpendicular * w <= k
+ #
+ # Move all the knowns to the right side.
+ #
+ # H * parallel * ([parallel1 parallel2] * X) <= k - H * perpendicular * w
+ #
+ # Let t = parallel.T \dot X, the component parallel to the surface.
+ #
+ # H * parallel * t <= k - H * perpendicular * w
+ #
+ # This is a polytope which we can solve, and use to figure out the range of X
+ # that we care about!
+
+ t_poly = polytope.HPolytope(
+ region.H * parallel_vector,
+ region.k - region.H * perpendicular_vector * w)
+
+ vertices = t_poly.Vertices()
+
+ if vertices.shape[0]:
+ # The region exists!
+ # Find the closest vertex
+ min_distance = numpy.infty
+ closest_point = None
+ for vertex in vertices:
+ point = parallel_vector * vertex + perpendicular_vector * w
+ length = numpy.linalg.norm(R - point)
+ if length < min_distance:
+ min_distance = length
+ closest_point = point
+
+ return (closest_point, True)
+ else:
+ # Find the vertex of the space that is closest to the line.
+ region_vertices = region.Vertices()
+ min_distance = numpy.infty
+ closest_point = None
+ for vertex in region_vertices:
+ point = vertex.T
+ length = numpy.abs((perpendicular_vector.T * point)[0, 0])
+ if length < min_distance:
+ min_distance = length
+ closest_point = point
+
+ return (closest_point, False)
+
+
+class VelocityDrivetrainModel(control_loop.ControlLoop):
+ def __init__(self, left_low=True, right_low=True, name="VelocityDrivetrainModel"):
+ super(VelocityDrivetrainModel, self).__init__(name)
+ self._drivetrain = drivetrain.Drivetrain(left_low=left_low,
+ right_low=right_low)
+ self.dt = 0.01
+ self.A_continuous = numpy.matrix(
+ [[self._drivetrain.A_continuous[1, 1], self._drivetrain.A_continuous[1, 3]],
+ [self._drivetrain.A_continuous[3, 1], self._drivetrain.A_continuous[3, 3]]])
+
+ self.B_continuous = numpy.matrix(
+ [[self._drivetrain.B_continuous[1, 0], self._drivetrain.B_continuous[1, 1]],
+ [self._drivetrain.B_continuous[3, 0], self._drivetrain.B_continuous[3, 1]]])
+ self.C = numpy.matrix(numpy.eye(2));
+ self.D = numpy.matrix(numpy.zeros((2, 2)));
+
+ self.A, self.B = self.ContinuousToDiscrete(self.A_continuous,
+ self.B_continuous, self.dt)
+
+ # FF * X = U (steady state)
+ self.FF = self.B.I * (numpy.eye(2) - self.A)
+
+ self.PlaceControllerPoles([0.6, 0.6])
+ self.PlaceObserverPoles([0.02, 0.02])
+
+ self.G_high = self._drivetrain.G_high
+ self.G_low = self._drivetrain.G_low
+ self.R = self._drivetrain.R
+ self.r = self._drivetrain.r
+ self.Kv = self._drivetrain.Kv
+ self.Kt = self._drivetrain.Kt
+
+ self.U_max = self._drivetrain.U_max
+ self.U_min = self._drivetrain.U_min
+
+
+class VelocityDrivetrain(object):
+ HIGH = 'high'
+ LOW = 'low'
+ SHIFTING_UP = 'up'
+ SHIFTING_DOWN = 'down'
+
+ def __init__(self):
+ self.drivetrain_low_low = VelocityDrivetrainModel(
+ left_low=True, right_low=True, name='VelocityDrivetrainLowLow')
+ self.drivetrain_low_high = VelocityDrivetrainModel(left_low=True, right_low=False, name='VelocityDrivetrainLowHigh')
+ self.drivetrain_high_low = VelocityDrivetrainModel(left_low=False, right_low=True, name = 'VelocityDrivetrainHighLow')
+ self.drivetrain_high_high = VelocityDrivetrainModel(left_low=False, right_low=False, name = 'VelocityDrivetrainHighHigh')
+
+ # X is [lvel, rvel]
+ self.X = numpy.matrix(
+ [[0.0],
+ [0.0]])
+
+ self.U_poly = polytope.HPolytope(
+ numpy.matrix([[1, 0],
+ [-1, 0],
+ [0, 1],
+ [0, -1]]),
+ numpy.matrix([[12],
+ [12],
+ [12],
+ [12]]))
+
+ self.U_max = numpy.matrix(
+ [[12.0],
+ [12.0]])
+ self.U_min = numpy.matrix(
+ [[-12.0000000000],
+ [-12.0000000000]])
+
+ self.dt = 0.01
+
+ self.R = numpy.matrix(
+ [[0.0],
+ [0.0]])
+
+ # ttrust is the comprimise between having full throttle negative inertia,
+ # and having no throttle negative inertia. A value of 0 is full throttle
+ # inertia. A value of 1 is no throttle negative inertia.
+ self.ttrust = 1.1
+
+ self.left_gear = VelocityDrivetrain.LOW
+ self.right_gear = VelocityDrivetrain.LOW
+ self.left_shifter_position = 0.0
+ self.right_shifter_position = 0.0
+ self.left_cim = drivetrain.CIM()
+ self.right_cim = drivetrain.CIM()
+
+ def IsInGear(self, gear):
+ return gear is VelocityDrivetrain.HIGH or gear is VelocityDrivetrain.LOW
+
+ def MotorRPM(self, shifter_position, velocity):
+ if shifter_position > 0.5:
+ return (velocity / self.CurrentDrivetrain().G_high /
+ self.CurrentDrivetrain().r)
+ else:
+ return (velocity / self.CurrentDrivetrain().G_low /
+ self.CurrentDrivetrain().r)
+
+ def CurrentDrivetrain(self):
+ if self.left_shifter_position > 0.5:
+ if self.right_shifter_position > 0.5:
+ return self.drivetrain_high_high
+ else:
+ return self.drivetrain_high_low
+ else:
+ if self.right_shifter_position > 0.5:
+ return self.drivetrain_low_high
+ else:
+ return self.drivetrain_low_low
+
+ def SimShifter(self, gear, shifter_position):
+ if gear is VelocityDrivetrain.HIGH or gear is VelocityDrivetrain.SHIFTING_UP:
+ shifter_position = min(shifter_position + 0.5, 1.0)
+ else:
+ shifter_position = max(shifter_position - 0.5, 0.0)
+
+ if shifter_position == 1.0:
+ gear = VelocityDrivetrain.HIGH
+ elif shifter_position == 0.0:
+ gear = VelocityDrivetrain.LOW
+
+ return gear, shifter_position
+
+ def ComputeGear(self, wheel_velocity, should_print=False, current_gear=False, gear_name=None):
+ high_omega = (wheel_velocity / self.CurrentDrivetrain().G_high /
+ self.CurrentDrivetrain().r)
+ low_omega = (wheel_velocity / self.CurrentDrivetrain().G_low /
+ self.CurrentDrivetrain().r)
+ #print gear_name, "Motor Energy Difference.", 0.5 * 0.000140032647 * (low_omega * low_omega - high_omega * high_omega), "joules"
+ high_torque = ((12.0 - high_omega / self.CurrentDrivetrain().Kv) *
+ self.CurrentDrivetrain().Kt / self.CurrentDrivetrain().R)
+ low_torque = ((12.0 - low_omega / self.CurrentDrivetrain().Kv) *
+ self.CurrentDrivetrain().Kt / self.CurrentDrivetrain().R)
+ high_power = high_torque * high_omega
+ low_power = low_torque * low_omega
+ #if should_print:
+ # print gear_name, "High omega", high_omega, "Low omega", low_omega
+ # print gear_name, "High torque", high_torque, "Low torque", low_torque
+ # print gear_name, "High power", high_power, "Low power", low_power
+
+ # Shift algorithm improvements.
+ # TODO(aschuh):
+ # It takes time to shift. Shifting down for 1 cycle doesn't make sense
+ # because you will end up slower than without shifting. Figure out how
+ # to include that info.
+ # If the driver is still in high gear, but isn't asking for the extra power
+ # from low gear, don't shift until he asks for it.
+ goal_gear_is_high = high_power > low_power
+ #goal_gear_is_high = True
+
+ if not self.IsInGear(current_gear):
+ print gear_name, 'Not in gear.'
+ return current_gear
+ else:
+ is_high = current_gear is VelocityDrivetrain.HIGH
+ if is_high != goal_gear_is_high:
+ if goal_gear_is_high:
+ print gear_name, 'Shifting up.'
+ return VelocityDrivetrain.SHIFTING_UP
+ else:
+ print gear_name, 'Shifting down.'
+ return VelocityDrivetrain.SHIFTING_DOWN
+ else:
+ return current_gear
+
+ def FilterVelocity(self, throttle):
+ # Invert the plant to figure out how the velocity filter would have to work
+ # out in order to filter out the forwards negative inertia.
+ # This math assumes that the left and right power and velocity are equal.
+
+ # The throttle filter should filter such that the motor in the highest gear
+ # should be controlling the time constant.
+ # Do this by finding the index of FF that has the lowest value, and computing
+ # the sums using that index.
+ FF_sum = self.CurrentDrivetrain().FF.sum(axis=1)
+ min_FF_sum_index = numpy.argmin(FF_sum)
+ min_FF_sum = FF_sum[min_FF_sum_index, 0]
+ min_K_sum = self.CurrentDrivetrain().K[min_FF_sum_index, :].sum()
+ # Compute the FF sum for high gear.
+ high_min_FF_sum = self.drivetrain_high_high.FF[0, :].sum()
+
+ # U = self.K[0, :].sum() * (R - x_avg) + self.FF[0, :].sum() * R
+ # throttle * 12.0 = (self.K[0, :].sum() + self.FF[0, :].sum()) * R
+ # - self.K[0, :].sum() * x_avg
+
+ # R = (throttle * 12.0 + self.K[0, :].sum() * x_avg) /
+ # (self.K[0, :].sum() + self.FF[0, :].sum())
+
+ # U = (K + FF) * R - K * X
+ # (K + FF) ^-1 * (U + K * X) = R
+
+ # Scale throttle by min_FF_sum / high_min_FF_sum. This will make low gear
+ # have the same velocity goal as high gear, and so that the robot will hold
+ # the same speed for the same throttle for all gears.
+ adjusted_ff_voltage = numpy.clip(throttle * 12.0 * min_FF_sum / high_min_FF_sum, -12.0, 12.0)
+ return ((adjusted_ff_voltage + self.ttrust * min_K_sum * (self.X[0, 0] + self.X[1, 0]) / 2.0)
+ / (self.ttrust * min_K_sum + min_FF_sum))
+
+ def Update(self, throttle, steering):
+ # Shift into the gear which sends the most power to the floor.
+ # This is the same as sending the most torque down to the floor at the
+ # wheel.
+
+ self.left_gear = self.right_gear = True
+ if False:
+ self.left_gear = self.ComputeGear(self.X[0, 0], should_print=True,
+ current_gear=self.left_gear,
+ gear_name="left")
+ self.right_gear = self.ComputeGear(self.X[1, 0], should_print=True,
+ current_gear=self.right_gear,
+ gear_name="right")
+ if self.IsInGear(self.left_gear):
+ self.left_cim.X[0, 0] = self.MotorRPM(self.left_shifter_position, self.X[0, 0])
+
+ if self.IsInGear(self.right_gear):
+ self.right_cim.X[0, 0] = self.MotorRPM(self.right_shifter_position, self.X[0, 0])
+
+ steering *= 2.3
+ if True or self.IsInGear(self.left_gear) and self.IsInGear(self.right_gear):
+ # Filter the throttle to provide a nicer response.
+ fvel = self.FilterVelocity(throttle)
+
+ # Constant radius means that angualar_velocity / linear_velocity = constant.
+ # Compute the left and right velocities.
+ steering_velocity = numpy.abs(fvel) * steering
+ left_velocity = fvel - steering_velocity
+ right_velocity = fvel + steering_velocity
+
+ # Write this constraint in the form of K * R = w
+ # angular velocity / linear velocity = constant
+ # (left - right) / (left + right) = constant
+ # left - right = constant * left + constant * right
+
+ # (fvel - steering * numpy.abs(fvel) - fvel - steering * numpy.abs(fvel)) /
+ # (fvel - steering * numpy.abs(fvel) + fvel + steering * numpy.abs(fvel)) =
+ # constant
+ # (- 2 * steering * numpy.abs(fvel)) / (2 * fvel) = constant
+ # (-steering * sign(fvel)) = constant
+ # (-steering * sign(fvel)) * (left + right) = left - right
+ # (steering * sign(fvel) + 1) * left + (steering * sign(fvel) - 1) * right = 0
+
+ equality_k = numpy.matrix(
+ [[1 + steering * numpy.sign(fvel), -(1 - steering * numpy.sign(fvel))]])
+ equality_w = 0.0
+
+ self.R[0, 0] = left_velocity
+ self.R[1, 0] = right_velocity
+
+ # Construct a constraint on R by manipulating the constraint on U
+ # Start out with H * U <= k
+ # U = FF * R + K * (R - X)
+ # H * (FF * R + K * R - K * X) <= k
+ # H * (FF + K) * R <= k + H * K * X
+ R_poly = polytope.HPolytope(
+ self.U_poly.H * (self.CurrentDrivetrain().K + self.CurrentDrivetrain().FF),
+ self.U_poly.k + self.U_poly.H * self.CurrentDrivetrain().K * self.X)
+
+ # Limit R back inside the box.
+ self.boxed_R = CoerceGoal(R_poly, equality_k, equality_w, self.R)
+
+ FF_volts = self.CurrentDrivetrain().FF * self.boxed_R
+ self.U_ideal = self.CurrentDrivetrain().K * (self.boxed_R - self.X) + FF_volts
+ else:
+ print 'Not all in gear'
+ if not self.IsInGear(self.left_gear) and not self.IsInGear(self.right_gear):
+ # TODO(austin): Use battery volts here.
+ R_left = self.MotorRPM(self.left_shifter_position, self.X[0, 0])
+ self.U_ideal[0, 0] = numpy.clip(
+ self.left_cim.K * (R_left - self.left_cim.X) + R_left / self.left_cim.Kv,
+ self.left_cim.U_min, self.left_cim.U_max)
+ self.left_cim.Update(self.U_ideal[0, 0])
+
+ R_right = self.MotorRPM(self.right_shifter_position, self.X[1, 0])
+ self.U_ideal[1, 0] = numpy.clip(
+ self.right_cim.K * (R_right - self.right_cim.X) + R_right / self.right_cim.Kv,
+ self.right_cim.U_min, self.right_cim.U_max)
+ self.right_cim.Update(self.U_ideal[1, 0])
+ else:
+ assert False
+
+ self.U = numpy.clip(self.U_ideal, self.U_min, self.U_max)
+
+ # TODO(austin): Model the robot as not accelerating when you shift...
+ # This hack only works when you shift at the same time.
+ if True or self.IsInGear(self.left_gear) and self.IsInGear(self.right_gear):
+ self.X = self.CurrentDrivetrain().A * self.X + self.CurrentDrivetrain().B * self.U
+
+ self.left_gear, self.left_shifter_position = self.SimShifter(
+ self.left_gear, self.left_shifter_position)
+ self.right_gear, self.right_shifter_position = self.SimShifter(
+ self.right_gear, self.right_shifter_position)
+
+ print "U is", self.U[0, 0], self.U[1, 0]
+ print "Left shifter", self.left_gear, self.left_shifter_position, "Right shifter", self.right_gear, self.right_shifter_position
+
+
+def main(argv):
+ vdrivetrain = VelocityDrivetrain()
+
+ if len(argv) != 7:
+ print "Expected .h file name and .cc file name"
+ else:
+ dog_loop_writer = control_loop.ControlLoopWriter(
+ "VelocityDrivetrain", [vdrivetrain.drivetrain_low_low,
+ vdrivetrain.drivetrain_low_high,
+ vdrivetrain.drivetrain_high_low,
+ vdrivetrain.drivetrain_high_high])
+
+ if argv[1][-3:] == '.cc':
+ dog_loop_writer.Write(argv[2], argv[1])
+ else:
+ dog_loop_writer.Write(argv[1], argv[2])
+
+ cim_writer = control_loop.ControlLoopWriter(
+ "CIM", [drivetrain.CIM()])
+
+ if argv[5][-3:] == '.cc':
+ cim_writer.Write(argv[6], argv[5])
+ else:
+ cim_writer.Write(argv[5], argv[6])
+ return
+
+ vl_plot = []
+ vr_plot = []
+ ul_plot = []
+ ur_plot = []
+ radius_plot = []
+ t_plot = []
+ left_gear_plot = []
+ right_gear_plot = []
+ vdrivetrain.left_shifter_position = 0.0
+ vdrivetrain.right_shifter_position = 0.0
+ vdrivetrain.left_gear = VelocityDrivetrain.LOW
+ vdrivetrain.right_gear = VelocityDrivetrain.LOW
+
+ print "K is", vdrivetrain.CurrentDrivetrain().K
+
+ if vdrivetrain.left_gear is VelocityDrivetrain.HIGH:
+ print "Left is high"
+ else:
+ print "Left is low"
+ if vdrivetrain.right_gear is VelocityDrivetrain.HIGH:
+ print "Right is high"
+ else:
+ print "Right is low"
+
+ for t in numpy.arange(0, 1.7, vdrivetrain.dt):
+ if t < 0.5:
+ vdrivetrain.Update(throttle=0.00, steering=1.0)
+ elif t < 1.2:
+ vdrivetrain.Update(throttle=0.5, steering=1.0)
+ else:
+ vdrivetrain.Update(throttle=0.00, steering=1.0)
+ t_plot.append(t)
+ vl_plot.append(vdrivetrain.X[0, 0])
+ vr_plot.append(vdrivetrain.X[1, 0])
+ ul_plot.append(vdrivetrain.U[0, 0])
+ ur_plot.append(vdrivetrain.U[1, 0])
+ left_gear_plot.append((vdrivetrain.left_gear is VelocityDrivetrain.HIGH) * 2.0 - 10.0)
+ right_gear_plot.append((vdrivetrain.right_gear is VelocityDrivetrain.HIGH) * 2.0 - 10.0)
+
+ fwd_velocity = (vdrivetrain.X[1, 0] + vdrivetrain.X[0, 0]) / 2
+ turn_velocity = (vdrivetrain.X[1, 0] - vdrivetrain.X[0, 0])
+ if abs(fwd_velocity) < 0.0000001:
+ radius_plot.append(turn_velocity)
+ else:
+ radius_plot.append(turn_velocity / fwd_velocity)
+
+ cim_velocity_plot = []
+ cim_voltage_plot = []
+ cim_time = []
+ cim = drivetrain.CIM()
+ R = numpy.matrix([[300]])
+ for t in numpy.arange(0, 0.5, cim.dt):
+ U = numpy.clip(cim.K * (R - cim.X) + R / cim.Kv, cim.U_min, cim.U_max)
+ cim.Update(U)
+ cim_velocity_plot.append(cim.X[0, 0])
+ cim_voltage_plot.append(U[0, 0] * 10)
+ cim_time.append(t)
+ pylab.plot(cim_time, cim_velocity_plot, label='cim spinup')
+ pylab.plot(cim_time, cim_voltage_plot, label='cim voltage')
+ pylab.legend()
+ pylab.show()
+
+ # TODO(austin):
+ # Shifting compensation.
+
+ # Tighten the turn.
+ # Closed loop drive.
+
+ pylab.plot(t_plot, vl_plot, label='left velocity')
+ pylab.plot(t_plot, vr_plot, label='right velocity')
+ pylab.plot(t_plot, ul_plot, label='left voltage')
+ pylab.plot(t_plot, ur_plot, label='right voltage')
+ pylab.plot(t_plot, radius_plot, label='radius')
+ pylab.plot(t_plot, left_gear_plot, label='left gear high')
+ pylab.plot(t_plot, right_gear_plot, label='right gear high')
+ pylab.legend()
+ pylab.show()
+ return 0
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv))
diff --git a/y2015/control_loops/python/polydrivetrain_test.py b/y2015/control_loops/python/polydrivetrain_test.py
new file mode 100755
index 0000000..434cdca
--- /dev/null
+++ b/y2015/control_loops/python/polydrivetrain_test.py
@@ -0,0 +1,82 @@
+#!/usr/bin/python
+
+import polydrivetrain
+import numpy
+from numpy.testing import *
+import polytope
+import unittest
+
+__author__ = 'Austin Schuh (austin.linux@gmail.com)'
+
+
+class TestVelocityDrivetrain(unittest.TestCase):
+ def MakeBox(self, x1_min, x1_max, x2_min, x2_max):
+ H = numpy.matrix([[1, 0],
+ [-1, 0],
+ [0, 1],
+ [0, -1]])
+ K = numpy.matrix([[x1_max],
+ [-x1_min],
+ [x2_max],
+ [-x2_min]])
+ return polytope.HPolytope(H, K)
+
+ def test_coerce_inside(self):
+ """Tests coercion when the point is inside the box."""
+ box = self.MakeBox(1, 2, 1, 2)
+
+ # x1 = x2
+ K = numpy.matrix([[1, -1]])
+ w = 0
+
+ assert_array_equal(polydrivetrain.CoerceGoal(box, K, w,
+ numpy.matrix([[1.5], [1.5]])),
+ numpy.matrix([[1.5], [1.5]]))
+
+ def test_coerce_outside_intersect(self):
+ """Tests coercion when the line intersects the box."""
+ box = self.MakeBox(1, 2, 1, 2)
+
+ # x1 = x2
+ K = numpy.matrix([[1, -1]])
+ w = 0
+
+ assert_array_equal(polydrivetrain.CoerceGoal(box, K, w, numpy.matrix([[5], [5]])),
+ numpy.matrix([[2.0], [2.0]]))
+
+ def test_coerce_outside_no_intersect(self):
+ """Tests coercion when the line does not intersect the box."""
+ box = self.MakeBox(3, 4, 1, 2)
+
+ # x1 = x2
+ K = numpy.matrix([[1, -1]])
+ w = 0
+
+ assert_array_equal(polydrivetrain.CoerceGoal(box, K, w, numpy.matrix([[5], [5]])),
+ numpy.matrix([[3.0], [2.0]]))
+
+ def test_coerce_middle_of_edge(self):
+ """Tests coercion when the line intersects the middle of an edge."""
+ box = self.MakeBox(0, 4, 1, 2)
+
+ # x1 = x2
+ K = numpy.matrix([[-1, 1]])
+ w = 0
+
+ assert_array_equal(polydrivetrain.CoerceGoal(box, K, w, numpy.matrix([[5], [5]])),
+ numpy.matrix([[2.0], [2.0]]))
+
+ def test_coerce_perpendicular_line(self):
+ """Tests coercion when the line does not intersect and is in quadrant 2."""
+ box = self.MakeBox(1, 2, 1, 2)
+
+ # x1 = -x2
+ K = numpy.matrix([[1, 1]])
+ w = 0
+
+ assert_array_equal(polydrivetrain.CoerceGoal(box, K, w, numpy.matrix([[5], [5]])),
+ numpy.matrix([[1.0], [1.0]]))
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/y2015/control_loops/python/shooter.py b/y2015/control_loops/python/shooter.py
new file mode 100755
index 0000000..69f2599
--- /dev/null
+++ b/y2015/control_loops/python/shooter.py
@@ -0,0 +1,254 @@
+#!/usr/bin/python
+
+import control_loop
+import numpy
+import sys
+from matplotlib import pylab
+
+class SprungShooter(control_loop.ControlLoop):
+ def __init__(self, name="RawSprungShooter"):
+ super(SprungShooter, self).__init__(name)
+ # Stall Torque in N m
+ self.stall_torque = .4982
+ # Stall Current in Amps
+ self.stall_current = 85
+ # Free Speed in RPM
+ self.free_speed = 19300.0
+ # Free Current in Amps
+ self.free_current = 1.2
+ # Effective mass of the shooter in kg.
+ # This rough estimate should about include the effect of the masses
+ # of the gears. If this number is too low, the eigen values of self.A
+ # will start to become extremely small.
+ self.J = 200
+ # Resistance of the motor, divided by the number of motors.
+ self.R = 12.0 / self.stall_current / 2.0
+ # Motor velocity constant
+ self.Kv = ((self.free_speed / 60.0 * 2.0 * numpy.pi) /
+ (12.0 - self.R * self.free_current))
+ # Torque constant
+ self.Kt = self.stall_torque / self.stall_current
+ # Spring constant for the springs, N/m
+ self.Ks = 2800.0
+ # Maximum extension distance (Distance from the 0 force point on the
+ # spring to the latch position.)
+ self.max_extension = 0.32385
+ # Gear ratio multiplied by radius of final sprocket.
+ self.G = 10.0 / 40.0 * 20.0 / 54.0 * 24.0 / 54.0 * 20.0 / 84.0 * 16.0 * (3.0 / 8.0) / (2.0 * numpy.pi) * 0.0254
+
+ # Control loop time step
+ self.dt = 0.01
+
+ # State feedback matrices
+ self.A_continuous = numpy.matrix(
+ [[0, 1],
+ [-self.Ks / self.J,
+ -self.Kt / self.Kv / (self.J * self.G * self.G * self.R)]])
+ self.B_continuous = numpy.matrix(
+ [[0],
+ [self.Kt / (self.J * self.G * self.R)]])
+ self.C = numpy.matrix([[1, 0]])
+ self.D = numpy.matrix([[0]])
+
+ self.A, self.B = self.ContinuousToDiscrete(
+ self.A_continuous, self.B_continuous, self.dt)
+
+ self.PlaceControllerPoles([0.45, 0.45])
+
+ self.rpl = .05
+ self.ipl = 0.008
+ self.PlaceObserverPoles([self.rpl,
+ self.rpl])
+
+ self.U_max = numpy.matrix([[12.0]])
+ self.U_min = numpy.matrix([[-12.0]])
+
+ self.InitializeState()
+
+
+class Shooter(SprungShooter):
+ def __init__(self, name="RawShooter"):
+ super(Shooter, self).__init__(name)
+
+ # State feedback matrices
+ self.A_continuous = numpy.matrix(
+ [[0, 1],
+ [0, -self.Kt / self.Kv / (self.J * self.G * self.G * self.R)]])
+ self.B_continuous = numpy.matrix(
+ [[0],
+ [self.Kt / (self.J * self.G * self.R)]])
+
+ self.A, self.B = self.ContinuousToDiscrete(
+ self.A_continuous, self.B_continuous, self.dt)
+
+ self.PlaceControllerPoles([0.45, 0.45])
+
+ self.rpl = .05
+ self.ipl = 0.008
+ self.PlaceObserverPoles([self.rpl,
+ self.rpl])
+
+ self.U_max = numpy.matrix([[12.0]])
+ self.U_min = numpy.matrix([[-12.0]])
+
+ self.InitializeState()
+
+
+class SprungShooterDeltaU(SprungShooter):
+ def __init__(self, name="SprungShooter"):
+ super(SprungShooterDeltaU, self).__init__(name)
+ A_unaugmented = self.A
+ B_unaugmented = self.B
+
+ self.A = numpy.matrix([[0.0, 0.0, 0.0],
+ [0.0, 0.0, 0.0],
+ [0.0, 0.0, 1.0]])
+ self.A[0:2, 0:2] = A_unaugmented
+ self.A[0:2, 2] = B_unaugmented
+
+ self.B = numpy.matrix([[0.0],
+ [0.0],
+ [1.0]])
+
+ self.C = numpy.matrix([[1.0, 0.0, 0.0]])
+ self.D = numpy.matrix([[0.0]])
+
+ self.PlaceControllerPoles([0.50, 0.35, 0.80])
+
+ print "K"
+ print self.K
+ print "Placed controller poles are"
+ print numpy.linalg.eig(self.A - self.B * self.K)[0]
+
+ self.rpl = .05
+ self.ipl = 0.008
+ self.PlaceObserverPoles([self.rpl + 1j * self.ipl,
+ self.rpl - 1j * self.ipl, 0.90])
+ print "Placed observer poles are"
+ print numpy.linalg.eig(self.A - self.L * self.C)[0]
+
+ self.U_max = numpy.matrix([[12.0]])
+ self.U_min = numpy.matrix([[-12.0]])
+
+ self.InitializeState()
+
+
+class ShooterDeltaU(Shooter):
+ def __init__(self, name="Shooter"):
+ super(ShooterDeltaU, self).__init__(name)
+ A_unaugmented = self.A
+ B_unaugmented = self.B
+
+ self.A = numpy.matrix([[0.0, 0.0, 0.0],
+ [0.0, 0.0, 0.0],
+ [0.0, 0.0, 1.0]])
+ self.A[0:2, 0:2] = A_unaugmented
+ self.A[0:2, 2] = B_unaugmented
+
+ self.B = numpy.matrix([[0.0],
+ [0.0],
+ [1.0]])
+
+ self.C = numpy.matrix([[1.0, 0.0, 0.0]])
+ self.D = numpy.matrix([[0.0]])
+
+ self.PlaceControllerPoles([0.55, 0.45, 0.80])
+
+ print "K"
+ print self.K
+ print "Placed controller poles are"
+ print numpy.linalg.eig(self.A - self.B * self.K)[0]
+
+ self.rpl = .05
+ self.ipl = 0.008
+ self.PlaceObserverPoles([self.rpl + 1j * self.ipl,
+ self.rpl - 1j * self.ipl, 0.90])
+ print "Placed observer poles are"
+ print numpy.linalg.eig(self.A - self.L * self.C)[0]
+
+ self.U_max = numpy.matrix([[12.0]])
+ self.U_min = numpy.matrix([[-12.0]])
+
+ self.InitializeState()
+
+
+def ClipDeltaU(shooter, old_voltage, delta_u):
+ old_u = old_voltage
+ new_u = numpy.clip(old_u + delta_u, shooter.U_min, shooter.U_max)
+ return new_u - old_u
+
+def main(argv):
+ # Simulate the response of the system to a goal.
+ sprung_shooter = SprungShooterDeltaU()
+ raw_sprung_shooter = SprungShooter()
+ close_loop_x = []
+ close_loop_u = []
+ goal_position = -0.3
+ R = numpy.matrix([[goal_position], [0.0], [-sprung_shooter.A[1, 0] / sprung_shooter.A[1, 2] * goal_position]])
+ voltage = numpy.matrix([[0.0]])
+ for _ in xrange(500):
+ U = sprung_shooter.K * (R - sprung_shooter.X_hat)
+ U = ClipDeltaU(sprung_shooter, voltage, U)
+ sprung_shooter.Y = raw_sprung_shooter.Y + 0.01
+ sprung_shooter.UpdateObserver(U)
+ voltage += U;
+ raw_sprung_shooter.Update(voltage)
+ close_loop_x.append(raw_sprung_shooter.X[0, 0] * 10)
+ close_loop_u.append(voltage[0, 0])
+
+ pylab.plot(range(500), close_loop_x)
+ pylab.plot(range(500), close_loop_u)
+ pylab.show()
+
+ shooter = ShooterDeltaU()
+ raw_shooter = Shooter()
+ close_loop_x = []
+ close_loop_u = []
+ goal_position = -0.3
+ R = numpy.matrix([[goal_position], [0.0], [-shooter.A[1, 0] / shooter.A[1, 2] * goal_position]])
+ voltage = numpy.matrix([[0.0]])
+ for _ in xrange(500):
+ U = shooter.K * (R - shooter.X_hat)
+ U = ClipDeltaU(shooter, voltage, U)
+ shooter.Y = raw_shooter.Y + 0.01
+ shooter.UpdateObserver(U)
+ voltage += U;
+ raw_shooter.Update(voltage)
+ close_loop_x.append(raw_shooter.X[0, 0] * 10)
+ close_loop_u.append(voltage[0, 0])
+
+ pylab.plot(range(500), close_loop_x)
+ pylab.plot(range(500), close_loop_u)
+ pylab.show()
+
+ # Write the generated constants out to a file.
+ if len(argv) != 5:
+ print "Expected .h file name and .cc file name for"
+ print "both the plant and unaugmented plant"
+ else:
+ unaug_sprung_shooter = SprungShooter("RawSprungShooter")
+ unaug_shooter = Shooter("RawShooter")
+ unaug_loop_writer = control_loop.ControlLoopWriter("RawShooter",
+ [unaug_sprung_shooter,
+ unaug_shooter])
+ if argv[3][-3:] == '.cc':
+ unaug_loop_writer.Write(argv[4], argv[3])
+ else:
+ unaug_loop_writer.Write(argv[3], argv[4])
+
+ sprung_shooter = SprungShooterDeltaU()
+ shooter = ShooterDeltaU()
+ loop_writer = control_loop.ControlLoopWriter("Shooter", [sprung_shooter,
+ shooter])
+
+ loop_writer.AddConstant(control_loop.Constant("kMaxExtension", "%f",
+ sprung_shooter.max_extension))
+ loop_writer.AddConstant(control_loop.Constant("kSpringConstant", "%f",
+ sprung_shooter.Ks))
+ if argv[1][-3:] == '.cc':
+ loop_writer.Write(argv[2], argv[1])
+ else:
+ loop_writer.Write(argv[1], argv[2])
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv))
diff --git a/y2015/control_loops/update_arm.sh b/y2015/control_loops/update_arm.sh
new file mode 100755
index 0000000..2a10f93
--- /dev/null
+++ b/y2015/control_loops/update_arm.sh
@@ -0,0 +1,12 @@
+#!/bin/bash
+#
+# Updates the arm controllers (for both robots).
+
+cd $(dirname $0)
+
+export PYTHONPATH=../../frc971/control_loops/python
+
+./python/arm.py fridge/arm_motor_plant.cc \
+ fridge/arm_motor_plant.h \
+ fridge/integral_arm_plant.cc \
+ fridge/integral_arm_plant.h
diff --git a/y2015/control_loops/update_claw.sh b/y2015/control_loops/update_claw.sh
new file mode 100755
index 0000000..d37e737
--- /dev/null
+++ b/y2015/control_loops/update_claw.sh
@@ -0,0 +1,10 @@
+#!/bin/bash
+#
+# Updates the claw controllers (for both robots).
+
+cd $(dirname $0)
+
+export PYTHONPATH=../../frc971/control_loops/python
+
+./python/claw.py claw/claw_motor_plant.cc \
+ claw/claw_motor_plant.h
diff --git a/y2015/control_loops/update_drivetrain.sh b/y2015/control_loops/update_drivetrain.sh
new file mode 100755
index 0000000..ecd1c41
--- /dev/null
+++ b/y2015/control_loops/update_drivetrain.sh
@@ -0,0 +1,12 @@
+#!/bin/bash
+#
+# Updates the drivetrain controllers (for both robots).
+
+cd $(dirname $0)
+
+export PYTHONPATH=../../frc971/control_loops/python
+
+./python/drivetrain.py drivetrain/drivetrain_dog_motor_plant.h \
+ drivetrain/drivetrain_dog_motor_plant.cc \
+ drivetrain/drivetrain_clutch_motor_plant.h \
+ drivetrain/drivetrain_clutch_motor_plant.cc
diff --git a/y2015/control_loops/update_elevator.sh b/y2015/control_loops/update_elevator.sh
new file mode 100755
index 0000000..4a7edb4
--- /dev/null
+++ b/y2015/control_loops/update_elevator.sh
@@ -0,0 +1,10 @@
+#!/bin/bash
+#
+# Updates the elevator controllers (for both robots).
+
+cd $(dirname $0)
+
+export PYTHONPATH=../../frc971/control_loops/python
+
+./python/elevator.py fridge/elevator_motor_plant.cc \
+ fridge/elevator_motor_plant.h
diff --git a/y2015/control_loops/update_polydrivetrain.sh b/y2015/control_loops/update_polydrivetrain.sh
new file mode 100755
index 0000000..338e40e
--- /dev/null
+++ b/y2015/control_loops/update_polydrivetrain.sh
@@ -0,0 +1,14 @@
+#!/bin/bash
+#
+# Updates the polydrivetrain controllers (for both robots) and CIM models.
+
+cd $(dirname $0)
+
+export PYTHONPATH=../../frc971/control_loops/python
+
+./python/polydrivetrain.py drivetrain/polydrivetrain_dog_motor_plant.h \
+ drivetrain/polydrivetrain_dog_motor_plant.cc \
+ drivetrain/polydrivetrain_clutch_motor_plant.h \
+ drivetrain/polydrivetrain_clutch_motor_plant.cc \
+ drivetrain/polydrivetrain_cim_plant.h \
+ drivetrain/polydrivetrain_cim_plant.cc
diff --git a/y2015/http_status/http_status.cc b/y2015/http_status/http_status.cc
new file mode 100644
index 0000000..9c64314
--- /dev/null
+++ b/y2015/http_status/http_status.cc
@@ -0,0 +1,296 @@
+#include "y2015/http_status/http_status.h"
+
+#include <iostream>
+#include <sstream>
+#include <string>
+#include <thread>
+#include <vector>
+
+#include "seasocks/Server.h"
+
+#include "aos/linux_code/init.h"
+#include "aos/common/logging/logging.h"
+#include "aos/common/time.h"
+#include "aos/common/util/phased_loop.h"
+#include "aos/common/mutex.h"
+
+#include "y2015/control_loops/claw/claw.q.h"
+#include "y2015/control_loops/fridge/fridge.q.h"
+
+#include "y2015/http_status/embedded.h"
+
+namespace frc971 {
+namespace http_status {
+
+// TODO(comran): Make some of these separate libraries & document them better.
+
+HTTPStatusMessage::HTTPStatusMessage()
+ : sample_id_(0),
+ measure_index_(0),
+ overflow_id_(200),
+ num_samples_per_packet_(50) {}
+
+void HTTPStatusMessage::NextSample() {
+ int32_t adjusted_index = GetIndex(sample_id_);
+
+ ::aos::time::Time time_now = ::aos::time::Time::Now();
+
+ if (sample_id_ < overflow_id_) {
+ sample_times_.emplace_back(time_now);
+ data_values_.emplace_back(::std::vector<double>());
+ } else {
+ sample_times_[adjusted_index] = time_now;
+ }
+
+ sample_id_++;
+ measure_index_ = 0;
+
+ CHECK(!mutex_.Lock()); // Lock the mutex so measures can be added.
+}
+
+void HTTPStatusMessage::EndSample() { mutex_.Unlock(); }
+
+int32_t HTTPStatusMessage::GetIndex(int32_t sample_id) {
+ return sample_id % overflow_id_;
+}
+
+void HTTPStatusMessage::AddMeasure(::std::string name, double value) {
+ // Mutex should be locked when this method is called to synchronize packets.
+ assert(mutex_.OwnedBySelf());
+
+ int32_t index = GetIndex(sample_id_ - 1);
+
+ if (measure_index_ >= static_cast<int32_t>(data_names_.size())) {
+ data_names_.emplace_back(name);
+ }
+
+ if (measure_index_ >= static_cast<int32_t>(data_values_.at(index).size())) {
+ data_values_.at(index).emplace_back(value);
+ } else {
+ data_values_.at(index).at(measure_index_) = value;
+ }
+ measure_index_++;
+}
+
+::std::string HTTPStatusMessage::Fetch(size_t from_sample) {
+ ::aos::MutexLocker locker(&mutex_);
+
+ ::std::stringstream message;
+ message.precision(10); // Cap how precise the time/measurement data is.
+
+ // To save space, data is being sent with a custom protocol over to the
+ // client.
+ // Initially, a message containing all the names of the measurements is sent
+ // and is preceeded with a *.
+ // Names begin with a star and are split with commas.
+
+ // Example: *test,test2
+ if (static_cast<int32_t>(from_sample) == -1) {
+ message << "*";
+ for (int32_t cur_data_name = 0;
+ cur_data_name < static_cast<int32_t>(data_names_.size());
+ cur_data_name++) {
+ if (cur_data_name > 0) {
+ message << ",";
+ }
+ message << data_names_.at(cur_data_name);
+ }
+ return message.str();
+ }
+
+ // TODO(comran): Use from_sample to determine the speed packets should be sent
+ // out to avoid skipping packets.
+ from_sample = sample_id_ - num_samples_per_packet_;
+
+ // Data packets are sent, with raw data being placed at the same index as the
+ // original index of the measurement name sent in the initial packet.
+ // Samples are split with dollar signs, info with percent signs, and
+ // measurements with commas.
+ // This special format system is helpful for debugging issues and looping
+ // through the data on the client side.
+
+ // Example of two samples that correspond with the initialized example:
+ // 289%2803.135127%10,67$290%2803.140109%12,68
+ for (int32_t cur_sample = from_sample;
+ cur_sample <
+ static_cast<int32_t>(from_sample + num_samples_per_packet_) &&
+ GetIndex(cur_sample) < static_cast<int32_t>(data_values_.size());
+ cur_sample++) {
+ if (cur_sample != static_cast<int32_t>(from_sample)) {
+ message << "$";
+ }
+
+ int32_t adjusted_index = GetIndex(cur_sample);
+
+ message << cur_sample << "%" << sample_times_.at(adjusted_index).ToSeconds()
+ << "%";
+ for (int32_t cur_measure = 0;
+ cur_measure < static_cast<int32_t>(data_names_.size());
+ cur_measure++) {
+ if (cur_measure > 0) {
+ message << ",";
+ }
+ message << data_values_.at(adjusted_index).at(cur_measure);
+ }
+ }
+ return message.str();
+}
+
+DataCollector::DataCollector() : cur_raw_data_("no data") {}
+
+void DataCollector::RunIteration() {
+ auto& fridge_queue = control_loops::fridge_queue;
+ auto& claw_queue = control_loops::claw_queue;
+
+ fridge_queue.status.FetchAnother();
+ claw_queue.status.FetchAnother();
+
+ message_.NextSample();
+ // Add recorded data here. /////
+ // NOTE: Try to use fewer than 30 measures, or the whole thing will lag.
+ // Abbreviate names if long, otherwise just use the command to get the value
+ // from the queue.
+
+ // TODO(comran): Make it so that the name doesn't have to be copied as a
+ // string.
+
+ // //// Fridge
+ // Positions
+ message_.AddMeasure("(fridge position left arm encoder)",
+ fridge_queue.position->arm.left.encoder);
+ message_.AddMeasure("(fridge position right arm encoder)",
+ fridge_queue.position->arm.right.encoder);
+ message_.AddMeasure("(fridge position left elev encoder)",
+ fridge_queue.position->elevator.left.encoder);
+ message_.AddMeasure("(fridge position right elev encoder)",
+ fridge_queue.position->elevator.right.encoder);
+ // Goals
+ message_.AddMeasure("fridge_queue.goal->profiling_type",
+ fridge_queue.goal->profiling_type);
+ message_.AddMeasure("fridge_queue.goal->angle", fridge_queue.goal->angle);
+ message_.AddMeasure("fridge_queue.goal->angular_velocity",
+ fridge_queue.goal->angular_velocity);
+ message_.AddMeasure("fridge_queue.goal->height", fridge_queue.goal->height);
+ message_.AddMeasure("fridge_queue.goal->velocity",
+ fridge_queue.goal->velocity);
+ message_.AddMeasure("fridge_queue.x", fridge_queue.goal->x);
+ message_.AddMeasure("fridge_queue.x_velocity", fridge_queue.goal->x_velocity);
+ message_.AddMeasure("fridge_queue.y", fridge_queue.goal->y);
+ message_.AddMeasure("fridge_queue.y_velocity", fridge_queue.goal->y_velocity);
+ // Statuses
+ message_.AddMeasure("fridge_queue.status->height",
+ fridge_queue.status->height);
+ message_.AddMeasure("fridge_queue.status->velocity",
+ fridge_queue.status->velocity);
+ message_.AddMeasure("fridge_queue.status->angle", fridge_queue.status->angle);
+ message_.AddMeasure("fridge_queue.status->angular_velocity",
+ fridge_queue.status->angular_velocity);
+ message_.AddMeasure("fridge_queue.status->x", fridge_queue.status->x);
+ message_.AddMeasure("fridge_queue.status->x_velocity",
+ fridge_queue.status->x_velocity);
+ message_.AddMeasure("fridge_queue.status->y", fridge_queue.status->y);
+ message_.AddMeasure("fridge_queue.status->y_velocity",
+ fridge_queue.status->y_velocity);
+ message_.AddMeasure("fridge_queue.status->state", fridge_queue.status->state);
+ message_.AddMeasure("fridge_queue.status->zeroed",
+ fridge_queue.status->zeroed);
+ message_.AddMeasure("fridge_queue.status->estopped",
+ fridge_queue.status->estopped);
+ // Outputs
+ message_.AddMeasure("fridge_queue.output->left_arm",
+ fridge_queue.output->left_arm);
+ message_.AddMeasure("fridge_queue.output->right_arm",
+ fridge_queue.output->right_arm);
+ message_.AddMeasure("fridge_queue.output->left_elevator",
+ fridge_queue.output->left_elevator);
+ message_.AddMeasure("fridge_queue.output->right_elevator",
+ fridge_queue.output->right_elevator);
+ // End recorded data. /////
+ message_.EndSample();
+}
+
+::std::string DataCollector::GetData(int32_t from_sample) {
+ return message_.Fetch(from_sample);
+}
+
+void DataCollector::operator()() {
+ ::aos::SetCurrentThreadName("HTTPStatusData");
+
+ while (run_) {
+ ::aos::time::PhasedLoopXMS(5, 0);
+ RunIteration();
+ }
+}
+
+SocketHandler::SocketHandler()
+ : data_collector_thread_(::std::ref(data_collector_)) {}
+
+void SocketHandler::onConnect(seasocks::WebSocket* connection) {
+ connections_.insert(connection);
+ LOG(INFO, "Connected: %s : %s\n", connection->getRequestUri().c_str(),
+ seasocks::formatAddress(connection->getRemoteAddress()).c_str());
+}
+
+void SocketHandler::onData(seasocks::WebSocket* connection, const char* data) {
+ int32_t from_sample = atoi(data);
+
+ ::std::string send_data = data_collector_.GetData(from_sample);
+ connection->send(send_data.c_str());
+}
+
+void SocketHandler::onDisconnect(seasocks::WebSocket* connection) {
+ connections_.erase(connection);
+ LOG(INFO, "Disconnected: %s : %s\n", connection->getRequestUri().c_str(),
+ seasocks::formatAddress(connection->getRemoteAddress()).c_str());
+}
+
+void SocketHandler::Quit() {
+ data_collector_.Quit();
+ data_collector_thread_.join();
+}
+
+SeasocksLogger::SeasocksLogger(Level min_level_to_log)
+ : PrintfLogger(min_level_to_log) {}
+
+void SeasocksLogger::log(Level level, const char* message) {
+ log_level aos_level;
+ switch (level) {
+ case seasocks::Logger::INFO:
+ aos_level = INFO;
+ break;
+ case seasocks::Logger::WARNING:
+ aos_level = WARNING;
+ break;
+ case seasocks::Logger::ERROR:
+ case seasocks::Logger::SEVERE:
+ aos_level = ERROR;
+ break;
+ case seasocks::Logger::DEBUG:
+ case seasocks::Logger::ACCESS:
+ default:
+ aos_level = DEBUG;
+ break;
+ }
+ LOG(aos_level, "Seasocks: %s\n", message);
+}
+
+} // namespace http_status
+} // namespace frc971
+
+int main(int, char* []) {
+ ::aos::InitNRT();
+
+ seasocks::Server server(::std::shared_ptr<seasocks::Logger>(
+ new frc971::http_status::SeasocksLogger(seasocks::Logger::INFO)));
+ frc971::http_status::SocketHandler socket_handler;
+
+ server.addWebSocketHandler(
+ "/ws",
+ ::std::shared_ptr<frc971::http_status::SocketHandler>(&socket_handler));
+ server.serve("www", 8080);
+
+ socket_handler.Quit();
+
+ ::aos::Cleanup();
+ return 0;
+}
diff --git a/y2015/http_status/http_status.gyp b/y2015/http_status/http_status.gyp
new file mode 100644
index 0000000..c78a1fc
--- /dev/null
+++ b/y2015/http_status/http_status.gyp
@@ -0,0 +1,46 @@
+{
+ 'targets': [
+ {
+ 'target_name': 'http_status',
+ 'type': 'executable',
+ 'sources': [
+ 'http_status.cc',
+ ],
+ 'actions': [
+ {
+ 'action_name': 'http_status_gen_embedded',
+ 'inputs': [
+ '<!@(find ./www_defaults)',
+ '<(AOS)/externals/seasocks/gen_embedded.py',
+ ],
+ 'outputs': [
+ '<(SHARED_INTERMEDIATE_DIR)/http_status/y2015/http_status/embedded.h',
+ ],
+ 'action': [
+ 'python', '<(AOS)/externals/seasocks/gen_embedded.py', '<(_outputs)',
+ ],
+ },
+ ],
+ 'include_dirs': [
+ '<(SHARED_INTERMEDIATE_DIR)/http_status/'
+ ],
+ 'dependencies': [
+ '<(AOS)/linux_code/linux_code.gyp:init',
+ '<(AOS)/build/aos.gyp:logging',
+ '<(EXTERNALS):seasocks',
+ '<(DEPTH)/y2015/control_loops/claw/claw.gyp:claw_queue',
+ '<(DEPTH)/y2015/control_loops/fridge/fridge.gyp:fridge_queue',
+ '<(AOS)/common/util/util.gyp:phased_loop',
+ '<(AOS)/common/common.gyp:time',
+ ],
+ 'copies': [
+ {
+ 'destination': '<(rsync_dir)',
+ 'files': [
+ 'www',
+ ],
+ },
+ ],
+ },
+ ],
+}
diff --git a/y2015/http_status/http_status.h b/y2015/http_status/http_status.h
new file mode 100644
index 0000000..2f7497d
--- /dev/null
+++ b/y2015/http_status/http_status.h
@@ -0,0 +1,106 @@
+#include <iostream>
+#include <memory>
+#include <sstream>
+#include <string>
+#include <thread>
+#include <atomic>
+#include <vector>
+
+#include "seasocks/PageHandler.h"
+#include "seasocks/PrintfLogger.h"
+#include "seasocks/StringUtil.h"
+#include "seasocks/WebSocket.h"
+
+#include "aos/linux_code/init.h"
+#include "aos/common/time.h"
+#include "aos/common/util/phased_loop.h"
+#include "aos/common/mutex.h"
+
+namespace frc971 {
+namespace http_status {
+
+// A class for storing data from DataCollector and packaging it as a custom
+// message for the websocket.
+// Samples are stored in a vector that wraps around at a certain point to avoid
+// clogging up memory. These samples should be already on all clients before
+// they are overwritten. To avoid losing samples, there must be a balance
+// between the rate samples are being recorded at and the speed of the link
+// between the robot and client.
+
+class HTTPStatusMessage {
+ public:
+ HTTPStatusMessage();
+
+ // Stores an individual measurement in the current sample.
+ void AddMeasure(::std::string name, double value);
+
+ // Starts a new sample that contains measurements for all the states at a
+ // timestep, and lock mutex to synchronize measures.
+ void NextSample();
+
+ // Unlock mutex.
+ void EndSample();
+
+ // Method called by the websocket to get a JSON-packaged string containing,
+ // at most, a constant number of samples, starting at "from_sample".
+ // "from_sample" is a specific index for a sample that is not wrapped.
+ ::std::string Fetch(size_t from_sample);
+
+ private:
+ // Returns the vector index of the sample given.
+ // Since the vectors wrap, multiple sample_ids may refer to the same vector
+ // index.
+ int32_t GetIndex(int32_t sample_id);
+
+ // Vectors of vectors to store samples at indexes determined by GetIndex.
+ ::std::vector<::std::string> data_names_;
+ ::std::vector<::std::vector<double>> data_values_;
+ ::std::vector<::aos::time::Time> sample_times_;
+
+ int32_t sample_id_; // Last sample id used.
+ int32_t measure_index_; // Last measure index used.
+ const int32_t overflow_id_; // Vector wrapping size.
+ // Number of samples to include in each JSON packet.
+ const int32_t num_samples_per_packet_;
+
+ // Mutex used to synchronize data.
+ ::aos::Mutex mutex_;
+};
+
+class DataCollector {
+ public:
+ DataCollector();
+ void RunIteration();
+ ::std::string GetData(int32_t from);
+
+ void operator()(); // Will be called by ::std::thread internally.
+ void Quit() { run_ = false; }
+
+ private:
+ ::std::string cur_raw_data_;
+ HTTPStatusMessage message_;
+ ::std::atomic<bool> run_{true};
+};
+
+class SocketHandler : public seasocks::WebSocket::Handler {
+ public:
+ SocketHandler();
+ void onConnect(seasocks::WebSocket* connection) override;
+ void onData(seasocks::WebSocket* connection, const char* data) override;
+ void onDisconnect(seasocks::WebSocket* connection) override;
+ void Quit();
+
+ private:
+ ::std::set<seasocks::WebSocket*> connections_;
+ DataCollector data_collector_;
+ ::std::thread data_collector_thread_;
+};
+
+class SeasocksLogger : public seasocks::PrintfLogger {
+ public:
+ SeasocksLogger(Level min_level_to_log);
+ void log(Level level, const char* message) override;
+};
+
+} // namespace http_status
+} // namespace frc971
diff --git a/y2015/http_status/www/index.html b/y2015/http_status/www/index.html
new file mode 100644
index 0000000..0fe83b4
--- /dev/null
+++ b/y2015/http_status/www/index.html
@@ -0,0 +1,160 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+<title>Hello, world</title>
+<script type="text/javascript">
+var escapable =
+ /[\x00-\x1f\ud800-\udfff\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufff0-\uffff]/g;
+var ws;
+var intervalTime = 50;
+var safetyTimeout;
+var safetyIntervalTime = 10;
+var selected = 0;
+
+// Filter out junky JSON packets that will otherwise cause nasty decoding errors.
+function filterUnicode(quoted) {
+ escapable.lastIndex = 0;
+ if (!escapable.test(quoted)) return quoted;
+
+ return quoted.replace(escapable, function(a) {
+ return '';
+ });
+}
+
+// Change the current data index to plot on the graph.
+function changeSelected(select) {
+ selected = select;
+ document.getElementById("selected").innerHTML = "Selected: " + selected;
+}
+
+// Get a new JSON packet from the websocket on the robot.
+function refresh() {
+ ws.send(lastSampleID);
+ safetyTimeout = setTimeout(safetyRefresh, safetyIntervalTime);
+}
+
+function safetyRefresh(){
+ console.log("Safety timeout exceeded. Performing additional refresh...");
+ refresh();
+}
+
+window.onload = function() {
+ var dps = [];
+ var numDatas = 0;
+ var chart = new CanvasJS.Chart("chartContainer", {
+ title: {
+ text: "Live Data"
+ },
+ axisX: {
+ title: "Time (sec)"
+ },
+ axisY: {
+ title: "Units"
+ },
+ zoomEnabled: true,
+ panEnabled: true,
+ data: [{
+ color: 'rgba(119, 152, 191, 1.0)',
+ type: "scatter",
+ dataPoints: dps
+ }],
+ });
+
+ chart.render();
+
+ $(function() {
+ ws = new WebSocket('ws://' + document.location.host + '/ws');
+ run = true;
+ xVal = 1;
+ lastSampleID = -1;
+
+ // Socket created & first opened.
+ ws.onopen = function() {
+ run = true;
+ refresh();
+ };
+
+ // Socket disconnected.
+ ws.onclose = function() {
+ console.log('onclose');
+ run = false;
+ $('#message').text('Lost connection.');
+ };
+
+ // Received message over the socket, so parse and chart it.
+ ws.onmessage = function(message) {
+ console.log(message);
+ clearTimeout(safetyTimeout);
+ message = message.data;
+ //$('#data').html(message);
+ if(message.charAt(0) == "*"){
+ message = message.substring(1);
+ var names = message.split(",");
+ for (var i = numDatas; i < names.length; i++) {
+ $('#dataTable').append('<tr onClick="changeSelected(' + i +
+ ')"><td>' + names[i] + '</td><td></td></tr>');
+ numDatas++;
+ }
+ lastSampleID = 0;
+ }else{
+ var samples = message.split("$");
+ for(var x = 0;x < samples.length;x++){
+ var info = samples[x].split("%");
+ lastSampleID = info[0];
+
+ if(!(typeof info[2] === "undefined")){
+ var values = info[2].split(",");
+ for(var y = 0;y < values.length;y++){
+ if(!(typeof info[1] === "undefined"
+ || typeof values[y] === "undefined")){
+ $('#dataTable').find('tr').eq(y).find('td').eq(1)
+ .text(values[y]);
+ if(y == selected){
+ dps.push({
+ x: parseFloat(info[1]),
+ y: parseFloat(values[y])
+ });
+ if(dps.length > 10000){
+ dps.shift();
+ }
+ }
+ }
+ }
+ }
+ }
+
+ chart.render();
+ }
+
+ if(run){
+ setTimeout(refresh, intervalTime);
+ }
+ };
+
+ // Socket error, most likely due to a server-side error.
+ ws.onerror = function(error) {
+ console.log('onerror ' + error);
+ run = false;
+ };
+ });
+}
+</script>
+<script type="text/javascript" src='/lib/jquery-1.4.4.js'></script>
+<script type="text/javascript" src='/lib/canvasjs.min.js'></script>
+<script type="text/javascript" src='/lib/reconnecting-websocket.min.js'></script>
+</head>
+<body>
+<div style="width: 1200px;margin-left: auto;margin-right:auto">
+ <table id="dataTable" style="width: 200px;cell-spacing:0;cell-padding:0;
+ text-align:left">
+ </table>
+ <div id="chartContainer" style="height:600px; width:100%;"></div>
+ <div style="width: 1000px;float: right">
+ <p id="message" style="color: #FF0000"></p>
+ <p id="selected">Selected: 0</p>
+ <p id="data"></p>
+ </div>
+</div>
+</body>
+</html>
diff --git a/y2015/http_status/www/lib/canvasjs.min.js b/y2015/http_status/www/lib/canvasjs.min.js
new file mode 100644
index 0000000..67fc83b
--- /dev/null
+++ b/y2015/http_status/www/lib/canvasjs.min.js
@@ -0,0 +1,429 @@
+/*
+ CanvasJS HTML5 & JavaScript Charts - v1.6.1 GA- http://canvasjs.com/
+ Copyright 2013 fenopix
+*/
+(function(){function O(a,b){a.prototype=Aa(b.prototype);a.prototype.constructor=a;a.parent=b.prototype}function Aa(a){function b(){}b.prototype=a;return new b}function qa(a,b,c){"millisecond"===c?a.setMilliseconds(a.getMilliseconds()+1*b):"second"===c?a.setSeconds(a.getSeconds()+1*b):"minute"===c?a.setMinutes(a.getMinutes()+1*b):"hour"===c?a.setHours(a.getHours()+1*b):"day"===c?a.setDate(a.getDate()+1*b):"week"===c?a.setDate(a.getDate()+7*b):"month"===c?a.setMonth(a.getMonth()+1*b):"year"===c&&a.setFullYear(a.getFullYear()+
+1*b);return a}function Y(a,b){return z[b+"Duration"]*a}function K(a,b){var c=!1;0>a&&(c=!0,a*=-1);a=""+a;for(b=b?b:1;a.length<b;)a="0"+a;return c?"-"+a:a}function Z(a){if(!a)return a;a=a.replace(/^\s\s*/,"");for(var b=/\s/,c=a.length;b.test(a.charAt(--c)););return a.slice(0,c+1)}function Ba(a){a.roundRect=function(a,c,d,e,f,g,k,p){k&&(this.fillStyle=k);p&&(this.strokeStyle=p);"undefined"===typeof f&&(f=5);this.lineWidth=g;this.beginPath();this.moveTo(a+f,c);this.lineTo(a+d-f,c);this.quadraticCurveTo(a+
+d,c,a+d,c+f);this.lineTo(a+d,c+e-f);this.quadraticCurveTo(a+d,c+e,a+d-f,c+e);this.lineTo(a+f,c+e);this.quadraticCurveTo(a,c+e,a,c+e-f);this.lineTo(a,c+f);this.quadraticCurveTo(a,c,a+f,c);this.closePath();k&&this.fill();p&&0<g&&this.stroke()}}function ra(a,b){return a-b}function Ca(a,b){return a.x-b.x}function C(a){var b=((a&16711680)>>16).toString(16),c=((a&65280)>>8).toString(16);a=((a&255)>>0).toString(16);b=2>b.length?"0"+b:b;c=2>c.length?"0"+c:c;a=2>a.length?"0"+a:a;return"#"+b+c+a}function ca(a,
+b,c){c=c||"normal";var d=a+"_"+b+"_"+c,e=sa[d];if(isNaN(e)){try{a="position:absolute; left:0px; top:-20000px; padding:0px;margin:0px;border:none;white-space:pre;line-height:normal;font-family:"+a+"; font-size:"+b+"px; font-weight:"+c+";";if(!R){var f=document.body;R=document.createElement("span");R.innerHTML="";var g=document.createTextNode("Mpgyi");R.appendChild(g);f.appendChild(R)}R.style.display="";R.setAttribute("style",a);e=Math.round(R.offsetHeight);R.style.display="none"}catch(k){e=Math.ceil(1.1*
+b)}e=Math.max(e,b);sa[d]=e}return e}function E(a,b,c,d){if(a.addEventListener)a.addEventListener(b,c,d||!1);else if(a.attachEvent)a.attachEvent("on"+b,function(b){b=b||window.event;b.preventDefault=b.preventDefault||function(){b.returnValue=!1};b.stopPropagation=b.stopPropagation||function(){b.cancelBubble=!0};c.call(a,b)});else return!1}function ta(a,b,c){a*=H;b*=H;a=c.getImageData(a,b,2,2).data;b=!0;for(c=0;4>c;c++)if(a[c]!==a[c+4]|a[c]!==a[c+8]|a[c]!==a[c+12]){b=!1;break}return b?a[0]<<16|a[1]<<
+8|a[2]:0}function ua(a,b,c){var d;d=a?a+"FontStyle":"fontStyle";var e=a?a+"FontWeight":"fontWeight",f=a?a+"FontSize":"fontSize";a=a?a+"FontFamily":"fontFamily";d=""+(b[d]?b[d]+" ":c&&c[d]?c[d]+" ":"");d+=b[e]?b[e]+" ":c&&c[e]?c[e]+" ":"";d+=b[f]?b[f]+"px ":c&&c[f]?c[f]+"px ":"";b=b[a]?b[a]+"":c&&c[a]?c[a]+"":"";!t&&b&&(b=b.split(",")[0],"'"!==b[0]&&'"'!==b[0]&&(b="'"+b+"'"));return d+=b}function T(a,b,c){return a in b?b[a]:c[a]}function da(a,b,c){if(t&&va){var d=a.getContext("2d");ea=d.webkitBackingStorePixelRatio||
+d.mozBackingStorePixelRatio||d.msBackingStorePixelRatio||d.oBackingStorePixelRatio||d.backingStorePixelRatio||1;H=ja/ea;a.width=b*H;a.height=c*H;ja!==ea&&(a.style.width=b+"px",a.style.height=c+"px",d.scale(H,H))}else a.width=b,a.height=c}function U(a,b){var c=document.createElement("canvas");c.setAttribute("class","canvasjs-chart-canvas");da(c,a,b);t||"undefined"===typeof G_vmlCanvasManager||G_vmlCanvasManager.initElement(c);return c}function wa(a,b,c){if(a&&b&&c){c=c+"."+("jpeg"===b?"jpg":b);var d=
+"image/"+b;a=a.toDataURL(d);var e=!1,f=document.createElement("a");f.download=c;f.href=a;f.target="_blank";if("undefined"!==typeof Blob&&new Blob){for(var g=a.replace(/^data:[a-z/]*;base64,/,""),g=atob(g),k=new ArrayBuffer(g.length),p=new Uint8Array(k),h=0;h<g.length;h++)p[h]=g.charCodeAt(h);b=new Blob([k],{type:"image/"+b});try{window.navigator.msSaveBlob(b,c),e=!0}catch(l){f.dataset.downloadurl=[d,f.download,f.href].join(":"),f.href=window.URL.createObjectURL(b)}}if(!e)try{event=document.createEvent("MouseEvents"),
+event.initMouseEvent("click",!0,!1,window,0,0,0,0,0,!1,!1,!1,!1,0,null),f.dispatchEvent?f.dispatchEvent(event):f.fireEvent&&f.fireEvent("onclick")}catch(r){b=window.open(),b.document.write("<img src='"+a+"'></img><div>Please right click on the image and save it to your device</div>"),b.document.close()}}}function M(a,b,c){b.getAttribute("state")!==c&&(b.setAttribute("state",c),b.setAttribute("type","button"),b.style.position="relative",b.style.margin="0px 0px 0px 0px",b.style.padding="3px 4px 0px 4px",
+b.style.cssFloat="left",b.setAttribute("title",a._cultureInfo[c+"Text"]),b.innerHTML="<img style='height:16px;' src='"+Da[c].image+"' alt='"+a._cultureInfo[c+"Text"]+"' />")}function ka(){for(var a=null,b=0;b<arguments.length;b++)a=arguments[b],a.style&&(a.style.display="inline")}function S(){for(var a=null,b=0;b<arguments.length;b++)(a=arguments[b])&&a.style&&(a.style.display="none")}function L(a,b,c){this._defaultsKey=a;var d={};c&&(W[c]&&W[c][a])&&(d=W[c][a]);this._options=b?b:{};this.setOptions(this._options,
+d)}function v(a,b,c){this._publicChartReference=c;b=b||{};v.parent.constructor.call(this,"Chart",b,b.theme?b.theme:"theme1");var d=this;this._containerId=a;this._objectsInitialized=!1;this.overlaidCanvasCtx=this.ctx=null;this._indexLabels=[];this._panTimerId=0;this._lastTouchEventType="";this._lastTouchData=null;this.isAnimating=!1;this.renderCount=0;this.panEnabled=this.disableToolTip=this.animatedRender=!1;this._defaultCursor="default";this.plotArea={canvas:null,ctx:null,x1:0,y1:0,x2:0,y2:0,width:0,
+height:0};this._dataInRenderedOrder=[];(this._container="string"===typeof this._containerId?document.getElementById(this._containerId):this._containerId)?(this._container.innerHTML="",b=a=0,a=this._options.width?this.width:0<this._container.clientWidth?this._container.clientWidth:this.width,b=this._options.height?this.height:0<this._container.clientHeight?this._container.clientHeight:this.height,this.width=a,this.height=b,this._selectedColorSet="undefined"!==typeof V[this.colorSet]?V[this.colorSet]:
+V.colorSet1,this._canvasJSContainer=document.createElement("div"),this._canvasJSContainer.setAttribute("class","canvasjs-chart-container"),this._canvasJSContainer.style.position="relative",this._canvasJSContainer.style.textAlign="left",this._canvasJSContainer.style.cursor="auto",t||(this._canvasJSContainer.style.height="0px"),this._container.appendChild(this._canvasJSContainer),this.canvas=U(a,b),this.canvas.style.position="absolute",this.canvas.getContext&&(this._canvasJSContainer.appendChild(this.canvas),
+this.ctx=this.canvas.getContext("2d"),this.ctx.textBaseline="top",Ba(this.ctx),t?this.plotArea.ctx=this.ctx:(this.plotArea.canvas=U(a,b),this.plotArea.canvas.style.position="absolute",this.plotArea.canvas.setAttribute("class","plotAreaCanvas"),this._canvasJSContainer.appendChild(this.plotArea.canvas),this.plotArea.ctx=this.plotArea.canvas.getContext("2d")),this.overlaidCanvas=U(a,b),this.overlaidCanvas.style.position="absolute",this._canvasJSContainer.appendChild(this.overlaidCanvas),this.overlaidCanvasCtx=
+this.overlaidCanvas.getContext("2d"),this.overlaidCanvasCtx.textBaseline="top",this._eventManager=new $(this),E(window,"resize",function(){d._updateSize()&&d.render()}),this._toolBar=document.createElement("div"),this._toolBar.setAttribute("class","canvasjs-chart-toolbar"),this._toolBar.style.cssText="position: absolute; right: 2px; top: 0px;",this._canvasJSContainer.appendChild(this._toolBar),this.bounds={x1:0,y1:0,x2:this.width,y2:this.height},E(this.overlaidCanvas,"click",function(a){d._mouseEventHandler(a)}),
+E(this.overlaidCanvas,"mousemove",function(a){d._mouseEventHandler(a)}),E(this.overlaidCanvas,"mouseup",function(a){d._mouseEventHandler(a)}),E(this.overlaidCanvas,"mousedown",function(a){d._mouseEventHandler(a);S(d._dropdownMenu)}),E(this.overlaidCanvas,"mouseout",function(a){d._mouseEventHandler(a)}),E(this.overlaidCanvas,window.navigator.msPointerEnabled?"MSPointerDown":"touchstart",function(a){d._touchEventHandler(a)}),E(this.overlaidCanvas,window.navigator.msPointerEnabled?"MSPointerMove":"touchmove",
+function(a){d._touchEventHandler(a)}),E(this.overlaidCanvas,window.navigator.msPointerEnabled?"MSPointerUp":"touchend",function(a){d._touchEventHandler(a)}),E(this.overlaidCanvas,window.navigator.msPointerEnabled?"MSPointerCancel":"touchcancel",function(a){d._touchEventHandler(a)}),this._creditLink||(this._creditLink=document.createElement("a"),this._creditLink.setAttribute("class","canvasjs-chart-credit"),this._creditLink.setAttribute("style","outline:none;margin:0px;position:absolute;right:3px;top:"+
+(this.height-14)+"px;color:dimgrey;text-decoration:none;font-size:10px;font-family:Lucida Grande, Lucida Sans Unicode, Arial, sans-serif"),this._creditLink.setAttribute("tabIndex",-1),this._creditLink.setAttribute("target","_blank")),this._toolTip=new N(this,this._options.toolTip,this.theme),this.layoutManager=new aa(this),this.axisY2=this.axisY=this.axisX=this.data=null,this.sessionVariables={axisX:{internalMinimum:null,internalMaximum:null},axisY:{internalMinimum:null,internalMaximum:null},axisY2:{internalMinimum:null,
+internalMaximum:null}})):window.console&&window.console.log('CanvasJS Error: Chart Container with id "'+this._containerId+'" was not found')}function fa(a,b){for(var c=[],d=0;d<a.length;d++)if(0==d)c.push(a[0]);else{var e,f,g;g=d-1;e=0===g?0:g-1;f=g===a.length-1?g:g+1;c[c.length]={x:a[g].x+(a[f].x-a[e].x)/b/3,y:a[g].y+(a[f].y-a[e].y)/b/3};g=d;e=0===g?0:g-1;f=g===a.length-1?g:g+1;c[c.length]={x:a[g].x-(a[f].x-a[e].x)/b/3,y:a[g].y-(a[f].y-a[e].y)/b/3};c[c.length]=a[d]}return c}function aa(a){this._rightOccupied=
+this._leftOccupied=this._bottomOccupied=this._topOccupied=0;this.chart=a}function I(a,b){I.parent.constructor.call(this,"TextBlock",b);this.ctx=a;this._isDirty=!0;this._wrappedText=null;this._lineHeight=ca(this.fontFamily,this.fontSize,this.fontWeight)}function ba(a,b){ba.parent.constructor.call(this,"Title",b,a.theme);this.chart=a;this.canvas=a.canvas;this.ctx=this.chart.ctx;"undefined"===typeof this._options.fontSize&&(this.fontSize=this.chart.getAutoFontSize(this.fontSize));this.height=this.width=
+null;this.bounds={x1:null,y1:null,x2:null,y2:null}}function ga(a,b,c){ga.parent.constructor.call(this,"Legend",b,c);this.chart=a;this.canvas=a.canvas;this.ctx=this.chart.ctx;this.ghostCtx=this.chart._eventManager.ghostCtx;this.items=[];this.height=this.width=0;this.orientation=null;this.horizontalSpacing=10;this.dataSeries=[];this.bounds={x1:null,y1:null,x2:null,y2:null};"undefined"===typeof this._options.fontSize&&(this.fontSize=this.chart.getAutoFontSize(this.fontSize));this.lineHeight=ca(this.fontFamily,
+this.fontSize,this.fontWeight)}function la(a,b){la.parent.constructor.call(this,b);this.chart=a;this.canvas=a.canvas;this.ctx=this.chart.ctx}function P(a,b,c,d,e){P.parent.constructor.call(this,"DataSeries",b,c);this.chart=a;this.canvas=a.canvas;this._ctx=a.canvas.ctx;this.index=d;this.noDataPointsInPlotArea=0;this.id=e;this.chart._eventManager.objectMap[e]={id:e,objectType:"dataSeries",dataSeriesIndex:d};this.dataPointIds=[];this.plotUnit=[];this.axisY=this.axisX=null;null===this.fillOpacity&&(this.type.match(/area/i)?
+this.fillOpacity=0.7:this.fillOpacity=1);this.axisPlacement=this.getDefaultAxisPlacement();"undefined"===typeof this._options.indexLabelFontSize&&(this.indexLabelFontSize=this.chart.getAutoFontSize(this.indexLabelFontSize))}function A(a,b,c,d){A.parent.constructor.call(this,"Axis",b,a.theme);this.chart=a;this.canvas=a.canvas;this.ctx=a.ctx;this.intervalstartTimePercent=this.maxHeight=this.maxWidth=0;this.labels=[];this._labels=null;this.dataInfo={min:Infinity,max:-Infinity,viewPortMin:Infinity,viewPortMax:-Infinity,
+minDiff:Infinity};"axisX"===c?(this.sessionVariables=this.chart.sessionVariables[c],this._options.interval||(this.intervalType=null)):this.sessionVariables="left"===d||"top"===d?this.chart.sessionVariables.axisY:this.chart.sessionVariables.axisY2;"undefined"===typeof this._options.titleFontSize&&(this.titleFontSize=this.chart.getAutoFontSize(this.titleFontSize));"undefined"===typeof this._options.labelFontSize&&(this.labelFontSize=this.chart.getAutoFontSize(this.labelFontSize));this.type=c;"axisX"!==
+c||b&&"undefined"!==typeof b.gridThickness||(this.gridThickness=0);this._position=d;this.lineCoordinates={x1:null,y1:null,x2:null,y2:null,width:null};this.labelAngle=(this.labelAngle%360+360)%360;90<this.labelAngle&&270>=this.labelAngle?this.labelAngle-=180:180<this.labelAngle&&270>=this.labelAngle?this.labelAngle-=180:270<this.labelAngle&&360>=this.labelAngle&&(this.labelAngle-=360);if(this._options.stripLines&&0<this._options.stripLines.length)for(this.stripLines=[],b=0;b<this._options.stripLines.length;b++)this.stripLines.push(new ma(this.chart,
+this._options.stripLines[b],a.theme,++this.chart._eventManager.lastObjectId));this._absoluteMaximum=this._absoluteMinimum=this._titleTextBlock=null;this.hasOptionChanged("minimum")&&(this.sessionVariables.internalMinimum=this.minimum);this.hasOptionChanged("maximum")&&(this.sessionVariables.internalMaximum=this.maximum);this.trackChanges("minimum");this.trackChanges("maximum")}function ma(a,b,c,d){ma.parent.constructor.call(this,"StripLine",b,c);this._thicknessType="pixel";this.id=d;null!==this.startValue&&
+null!==this.endValue&&(this.value=((this.startValue.getTime?this.startValue.getTime():this.startValue)+(this.endValue.getTime?this.endValue.getTime():this.endValue))/2,this.thickness=Math.max(this.endValue-this.startValue),this._thicknessType="value")}function N(a,b,c){N.parent.constructor.call(this,"ToolTip",b,c);this.chart=a;this.canvas=a.canvas;this.ctx=this.chart.ctx;this.currentDataPointIndex=this.currentSeriesIndex=-1;this._timerId=0;this._prevY=this._prevX=NaN;this._initialize()}function $(a){this.chart=
+a;this.lastObjectId=0;this.objectMap=[];this.rectangularRegionEventSubscriptions=[];this.previousDataPointEventObject=null;this.ghostCanvas=U(this.chart.width,this.chart.height);this.ghostCtx=this.ghostCanvas.getContext("2d");this.mouseoveredObjectMaps=[]}function xa(a,b){var c;b&&na[b]&&(c=na[b]);ba.parent.constructor.call(this,"CultureInfo",c,a.theme);this.chart=a;this.canvas=a.canvas;this.ctx=this.chart.ctx}function oa(a){this.chart=a;this.ctx=this.chart.plotArea.ctx;this.animations=[];this.animationRequestId=
+null}var t=!!document.createElement("canvas").getContext,ha={Chart:{width:500,height:400,zoomEnabled:!1,backgroundColor:"white",theme:"theme1",animationEnabled:!1,animationDuration:1200,colorSet:"colorSet1",culture:"en",creditText:"CanvasJS.com",interactivityEnabled:!0,exportEnabled:!1,exportFileName:"Chart"},Title:{padding:0,text:null,verticalAlign:"top",horizontalAlign:"center",fontSize:20,fontFamily:"Calibri",fontWeight:"normal",fontColor:"black",fontStyle:"normal",borderThickness:0,borderColor:"black",
+cornerRadius:0,backgroundColor:null,margin:5},Legend:{name:null,verticalAlign:"center",horizontalAlign:"right",fontSize:14,fontFamily:"calibri",fontWeight:"normal",fontColor:"black",fontStyle:"normal",cursor:null,itemmouseover:null,itemmouseout:null,itemmousemove:null,itemclick:null},ToolTip:{enabled:!0,borderColor:null,shared:!1,animationEnabled:!0,content:null},Axis:{minimum:null,maximum:null,interval:null,intervalType:null,title:null,titleFontColor:"black",titleFontSize:20,titleFontFamily:"arial",
+titleFontWeight:"normal",titleFontStyle:"normal",labelAngle:0,labelFontFamily:"arial",labelFontColor:"black",labelFontSize:12,labelFontWeight:"normal",labelFontStyle:"normal",labelAutoFit:!1,labelWrap:!0,labelMaxWidth:null,prefix:"",suffix:"",includeZero:!0,tickLength:5,tickColor:"black",tickThickness:1,lineColor:"black",lineThickness:1,gridColor:"A0A0A0",gridThickness:0,interlacedColor:null,valueFormatString:null,margin:2,stripLines:[]},StripLine:{value:null,startValue:null,endValue:null,color:"orange",
+thickness:2,label:"",labelBackgroundColor:"#EEEEEE",labelFontFamily:"arial",labelFontColor:"orange",labelFontSize:12,labelFontWeight:"normal",labelFontStyle:"normal"},DataSeries:{name:null,dataPoints:null,label:"",bevelEnabled:!1,cursor:null,indexLabel:"",indexLabelPlacement:"auto",indexLabelOrientation:"horizontal",indexLabelFontColor:"black",indexLabelFontSize:12,indexLabelFontStyle:"normal",indexLabelFontFamily:"Arial",indexLabelFontWeight:"normal",indexLabelBackgroundColor:null,indexLabelLineColor:null,
+indexLabelLineThickness:1,indexLabelMaxWidth:null,indexLabelWrap:!0,lineThickness:2,color:null,risingColor:"white",fillOpacity:null,startAngle:0,type:"column",xValueType:"number",axisYType:"primary",xValueFormatString:null,yValueFormatString:null,zValueFormatString:null,percentFormatString:null,showInLegend:null,legendMarkerType:null,legendMarkerColor:null,legendText:null,legendMarkerBorderColor:null,legendMarkerBorderThickness:null,markerType:"circle",markerColor:null,markerSize:null,markerBorderColor:null,
+markerBorderThickness:null,mouseover:null,mouseout:null,mousemove:null,click:null,toolTipContent:null,visible:!0},CultureInfo:{decimalSeparator:".",digitGroupSeparator:",",zoomText:"Zoom",panText:"Pan",resetText:"Reset",menuText:"More Options",saveJPGText:"Save as JPG",savePNGText:"Save as PNG",days:"Sunday Monday Tuesday Wednesday Thursday Friday Saturday".split(" "),shortDays:"Sun Mon Tue Wed Thu Fri Sat".split(" "),months:"January February March April May June July August September October November December".split(" "),
+shortMonths:"Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec".split(" ")},TextBlock:{x:0,y:0,width:null,height:null,maxWidth:null,maxHeight:null,padding:0,angle:0,text:"",horizontalAlign:"center",fontSize:12,fontFamily:"calibri",fontWeight:"normal",fontColor:"black",fontStyle:"normal",borderThickness:0,borderColor:"black",cornerRadius:0,backgroundColor:null,textBaseline:"top"}},na={en:{}},V={colorSet1:"#369EAD #C24642 #7F6084 #86B402 #A2D1CF #C8B631 #6DBCEB #52514E #4F81BC #A064A1 #F79647".split(" "),
+colorSet2:"#4F81BC #C0504E #9BBB58 #23BFAA #8064A1 #4AACC5 #F79647 #33558B".split(" "),colorSet3:"#8CA1BC #36845C #017E82 #8CB9D0 #708C98 #94838D #F08891 #0366A7 #008276 #EE7757 #E5BA3A #F2990B #03557B #782970".split(" ")},W={theme1:{Chart:{colorSet:"colorSet1"},Title:{fontFamily:t?"Calibri, Optima, Candara, Verdana, Geneva, sans-serif":"calibri",fontSize:33,fontColor:"#3A3A3A",fontWeight:"bold",verticalAlign:"top",margin:10},Axis:{titleFontSize:26,titleFontColor:"#666666",titleFontFamily:t?"Calibri, Optima, Candara, Verdana, Geneva, sans-serif":
+"calibri",labelFontFamily:t?"Calibri, Optima, Candara, Verdana, Geneva, sans-serif":"calibri",labelFontSize:18,labelFontColor:"grey",tickColor:"#BBBBBB",tickThickness:2,gridThickness:2,gridColor:"#BBBBBB",lineThickness:2,lineColor:"#BBBBBB"},Legend:{verticalAlign:"bottom",horizontalAlign:"center",fontFamily:t?"monospace, sans-serif,arial black":"calibri"},DataSeries:{indexLabelFontColor:"grey",indexLabelFontFamily:t?"Calibri, Optima, Candara, Verdana, Geneva, sans-serif":"calibri",indexLabelFontSize:18,
+indexLabelLineThickness:1}},theme2:{Chart:{colorSet:"colorSet2"},Title:{fontFamily:"impact, charcoal, arial black, sans-serif",fontSize:32,fontColor:"#333333",verticalAlign:"top",margin:10},Axis:{titleFontSize:22,titleFontColor:"rgb(98,98,98)",titleFontFamily:t?"monospace, sans-serif,arial black":"arial",titleFontWeight:"bold",labelFontFamily:t?"monospace, Courier New, Courier":"arial",labelFontSize:16,labelFontColor:"grey",labelFontWeight:"bold",tickColor:"grey",tickThickness:2,gridThickness:2,gridColor:"grey",
+lineThickness:0},Legend:{verticalAlign:"bottom",horizontalAlign:"center",fontFamily:t?"monospace, sans-serif,arial black":"arial"},DataSeries:{indexLabelFontColor:"grey",indexLabelFontFamily:t?"Courier New, Courier, monospace":"arial",indexLabelFontWeight:"bold",indexLabelFontSize:18,indexLabelLineThickness:1}},theme3:{Chart:{colorSet:"colorSet1"},Title:{fontFamily:t?"Candara, Optima, Trebuchet MS, Helvetica Neue, Helvetica, Trebuchet MS, serif":"calibri",fontSize:32,fontColor:"#3A3A3A",fontWeight:"bold",
+verticalAlign:"top",margin:10},Axis:{titleFontSize:22,titleFontColor:"rgb(98,98,98)",titleFontFamily:t?"Verdana, Geneva, Calibri, sans-serif":"calibri",labelFontFamily:t?"Calibri, Optima, Candara, Verdana, Geneva, sans-serif":"calibri",labelFontSize:18,labelFontColor:"grey",tickColor:"grey",tickThickness:2,gridThickness:2,gridColor:"grey",lineThickness:2,lineColor:"grey"},Legend:{verticalAlign:"bottom",horizontalAlign:"center",fontFamily:t?"monospace, sans-serif,arial black":"calibri"},DataSeries:{bevelEnabled:!0,
+indexLabelFontColor:"grey",indexLabelFontFamily:t?"Candara, Optima, Calibri, Verdana, Geneva, sans-serif":"calibri",indexLabelFontSize:18,indexLabelLineColor:"lightgrey",indexLabelLineThickness:2}}},z={numberDuration:1,yearDuration:314496E5,monthDuration:2592E6,weekDuration:6048E5,dayDuration:864E5,hourDuration:36E5,minuteDuration:6E4,secondDuration:1E3,millisecondDuration:1,dayOfWeekFromInt:"Sunday Monday Tuesday Wednesday Thursday Friday Saturday".split(" ")},sa={},R=null,ya=function(){var a=/D{1,4}|M{1,4}|Y{1,4}|h{1,2}|H{1,2}|m{1,2}|s{1,2}|f{1,3}|t{1,2}|T{1,2}|K|z{1,3}|"[^"]*"|'[^']*'/g,
+b="Sunday Monday Tuesday Wednesday Thursday Friday Saturday".split(" "),c="Sun Mon Tue Wed Thu Fri Sat".split(" "),d="January February March April May June July August September October November December".split(" "),e="Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec".split(" "),f=/\b(?:[PMCEA][SDP]T|(?:Pacific|Mountain|Central|Eastern|Atlantic) (?:Standard|Daylight|Prevailing) Time|(?:GMT|UTC)(?:[-+]\d{4})?)\b/g,g=/[^-+\dA-Z]/g;return function(k,p,h){var l=h?h.days:b,r=h?h.months:d,m=h?h.shortDays:
+c,q=h?h.shortMonths:e;h="";var n=!1;k=k&&k.getTime?k:k?new Date(k):new Date;if(isNaN(k))throw SyntaxError("invalid date");"UTC:"===p.slice(0,4)&&(p=p.slice(4),n=!0);h=n?"getUTC":"get";var s=k[h+"Date"](),t=k[h+"Day"](),u=k[h+"Month"](),x=k[h+"FullYear"](),w=k[h+"Hours"](),Q=k[h+"Minutes"](),G=k[h+"Seconds"](),v=k[h+"Milliseconds"](),y=n?0:k.getTimezoneOffset();return h=p.replace(a,function(a){switch(a){case "D":return s;case "DD":return K(s,2);case "DDD":return m[t];case "DDDD":return l[t];case "M":return u+
+1;case "MM":return K(u+1,2);case "MMM":return q[u];case "MMMM":return r[u];case "Y":return parseInt(String(x).slice(-2));case "YY":return K(String(x).slice(-2),2);case "YYY":return K(String(x).slice(-3),3);case "YYYY":return K(x,4);case "h":return w%12||12;case "hh":return K(w%12||12,2);case "H":return w;case "HH":return K(w,2);case "m":return Q;case "mm":return K(Q,2);case "s":return G;case "ss":return K(G,2);case "f":return String(v).slice(0,1);case "ff":return K(String(v).slice(0,2),2);case "fff":return K(String(v).slice(0,
+3),3);case "t":return 12>w?"a":"p";case "tt":return 12>w?"am":"pm";case "T":return 12>w?"A":"P";case "TT":return 12>w?"AM":"PM";case "K":return n?"UTC":(String(k).match(f)||[""]).pop().replace(g,"");case "z":return(0<y?"-":"+")+Math.floor(Math.abs(y)/60);case "zz":return(0<y?"-":"+")+K(Math.floor(Math.abs(y)/60),2);case "zzz":return(0<y?"-":"+")+K(Math.floor(Math.abs(y)/60),2)+K(Math.abs(y)%60,2);default:return a.slice(1,a.length-1)}})}}(),X=function(a,b,c){if(null===a)return"";a=Number(a);var d=
+0>a?!0:!1;d&&(a*=-1);var e=c?c.decimalSeparator:".",f=c?c.digitGroupSeparator:",",g="";b=String(b);var g=1,k=c="",p=-1,h=[],l=[],r=0,m=0,q=0,n=!1,s=0,k=b.match(/"[^"]*"|'[^']*'|[eE][+-]*[0]+|[,]+[.]|\u2030|./g);b=null;for(var t=0;k&&t<k.length;t++)if(b=k[t],"."===b&&0>p)p=t;else{if("%"===b)g*=100;else if("\u2030"===b){g*=1E3;continue}else if(","===b[0]&&"."===b[b.length-1]){g/=Math.pow(1E3,b.length-1);p=t+b.length-1;continue}else"E"!==b[0]&&"e"!==b[0]||"0"!==b[b.length-1]||(n=!0);0>p?(h.push(b),"#"===
+b||"0"===b?r++:","===b&&q++):(l.push(b),"#"!==b&&"0"!==b||m++)}n&&(b=Math.floor(a),s=(0===b?"":String(b)).length-r,g/=Math.pow(10,s));0>p&&(p=t);g=(a*g).toFixed(m);b=g.split(".");g=(b[0]+"").split("");a=(b[1]+"").split("");g&&"0"===g[0]&&g.shift();for(t=n=k=m=p=0;0<h.length;)if(b=h.pop(),"#"===b||"0"===b)if(p++,p===r){var u=g,g=[];if("0"===b)for(b=r-m-(u?u.length:0);0<b;)u.unshift("0"),b--;for(;0<u.length;)c=u.pop()+c,t++,0===t%n&&(k===q&&0<u.length)&&(c=f+c);d&&(c="-"+c)}else 0<g.length?(c=g.pop()+
+c,m++,t++):"0"===b&&(c="0"+c,m++,t++),0===t%n&&(k===q&&0<g.length)&&(c=f+c);else"E"!==b[0]&&"e"!==b[0]||"0"!==b[b.length-1]||!/[eE][+-]*[0]+/.test(b)?","===b?(k++,n=t,t=0,0<g.length&&(c=f+c)):c=1<b.length&&('"'===b[0]&&'"'===b[b.length-1]||"'"===b[0]&&"'"===b[b.length-1])?b.slice(1,b.length-1)+c:b+c:(b=0>s?b.replace("+","").replace("-",""):b.replace("-",""),c+=b.replace(/[0]+/,function(a){return K(s,a.length)}));d="";for(f=!1;0<l.length;)b=l.shift(),"#"===b||"0"===b?0<a.length&&0!==Number(a.join(""))?
+(d+=a.shift(),f=!0):"0"===b&&(d+="0",f=!0):1<b.length&&('"'===b[0]&&'"'===b[b.length-1]||"'"===b[0]&&"'"===b[b.length-1])?d+=b.slice(1,b.length-1):"E"!==b[0]&&"e"!==b[0]||"0"!==b[b.length-1]||!/[eE][+-]*[0]+/.test(b)?d+=b:(b=0>s?b.replace("+","").replace("-",""):b.replace("-",""),d+=b.replace(/[0]+/,function(a){return K(s,a.length)}));return c+((f?e:"")+d)},ia=function(a){var b=0,c=0;a=a||window.event;a.offsetX||0===a.offsetX?(b=a.offsetX,c=a.offsetY):a.layerX||0==a.layerX?(b=a.layerX,c=a.layerY):
+(b=a.pageX-a.target.offsetLeft,c=a.pageY-a.target.offsetTop);return{x:b,y:c}},va=!0,ja=window.devicePixelRatio||1,ea=1,H=va?ja/ea:1,Da={reset:{image:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAcCAYAAAAAwr0iAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAKRSURBVEiJrdY/iF1FFMfxzwnZrGISUSR/JLGIhoh/QiRNBLWxMLIWEkwbgiAoFgoW2mhlY6dgpY2IlRBRxBSKhSAKIklWJRYuMZKAhiyopAiaTY7FvRtmZ+/ed9/zHRjezLw5v/O9d86cuZGZpmURAfdn5o9DfdZNLXpjz+LziPgyIl6MiG0jPTJzZBuyDrP4BVm0P/AKbljTb4ToY/gGewYA7KyCl+1b3DUYANvwbiHw0gCAGRzBOzjTAXEOu0cC4Ch+r5x/HrpdrcZmvIDFSucMtnYCYC++6HmNDw8FKDT34ETrf639/azOr5vwRk/g5fbeuABtgC04XWk9VQLciMP4EH/3AFzErRNC7MXlQmsesSoHsGPE23hmEoBW+61K66HMXFmIMvN8myilXS36R01ub+KfYvw43ZXwYDX+AHP4BAci4pFJomfmr/ihmNofESsBImJGk7mlncrM45n5JPbhz0kAWpsv+juxaX21YIPmVJS2uNzJMS6ZNexC0d+I7fUWXLFyz2kSZlpWPvASlmqAf/FXNXf3FAF2F/1LuFifAlionB6dRuSI2IwHi6lzmXmp6xR8XY0fiIh7psAwh+3FuDkRHQVjl+a8lkXjo0kLUKH7XaV5oO86PmZ1FTzyP4K/XGl9v/zwfbW7BriiuETGCP5ch9bc9f97HF/vcFzCa5gdEPgWq+t/4v0V63oE1uF4h0DiFJ7HnSWMppDdh1dxtsPvJ2wcBNAKbsJXa0Ck5opdaBPsRNu/usba09i1KsaAVzmLt3sghrRjuK1Tf4xkegInxwy8gKf7dKMVH2QRsV5zXR/Cftyu+aKaKbbkQrsdH+PTzLzcqzkOQAVzM+7FHdiqqe2/YT4zF/t8S/sPmawyvC974vcAAAAASUVORK5CYII="},
+pan:{image:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAJVSURBVFiFvZe7a1RBGMV/x2hWI4JpfKCIiSBKOoOCkID/wP4BFqIIFkE02ChIiC8QDKlSiI3YqRBsBVGwUNAUdiIEUgjiAzQIIsuKJsfizsXr5t7d+8jmwLDfzHz3nLOzc7+ZxTZlGyDgZiWOCuJ9wH2gCUyuqQFgF/AGcKJNrYkBYBj40CIet+muGQi/96kM4WS7C/Tm5VUg7whJg8BkEGkCR4BDYfodsADUgP6wErO5iCtswsuJb32hdbXy8qzL5TIdmzJinHdZoZIBZcSFkGlAKs1Z3YCketZcBtouuaQNkrblMiBpBrhme7mAgU4wMCvpcFsDkq4C54DFVRTH9h+i6vlE0r5UA5ImgCuh28jB28iIs7BIVCOeStoZD64P4uPAjUTygKSx2FsK2TIwkugfk9Qkfd/E+yMWHQCeSRqx/R3gOp3LazfaS2C4B5gHDgD7U9x3E3uAH7KNpC3AHHAwTL4FHgM9GQ8vAaPA0dB/Abxqk2/gBLA9MXba9r1k/d4LfA3JtwueBeM58ucS+edXnAW23wP10N3advEi9CXizTnyN4bPS7Zn4sH/dq3t18AY4e1YLYSy3g/csj2VnFshZPuOpOeSKHCodUINuGj7YetE6je1PV9QoNPJ9StNHKodx7nRbiWrGHBGXAi5DUiqtQwtpcWK0Jubt8CltA5MEV1IfwO7+VffPwGfia5m34CT4bXujIIX0Qna1/cGMNqV/wUJE2czxD8CQ4X5Sl7Jz7SILwCDpbjKPBRMHAd+EtX4HWV5Spdc2w8kDQGPbH8py/MXMygM69/FKz4AAAAASUVORK5CYII="},
+zoom:{image:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAK6wAACusBgosNWgAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAMqSURBVFiFvdfbj91TFMDxz57U6GUEMS1aYzyMtCSSDhWjCZMInpAI3khE/QHtgzdRkXgSCS8SES9epKLi0oRKNETjRahREq2KS1stdRujtDPtbA97n5zdn9+5zJxTK9k5v3POXmt991p7r71+IcaoGwkhTOIebMRqzOBTvIG3Y4zTXRmqSoyx5cAKbMJOHMFJnMZ8/jyFaXyMR7G6nb1aH22cP4BvcBxziG3GKfyTIR9D6BYg1KUghPBCDveFlb/24Av8iuUYw41YVsz5G7uxKcZ4aMEpwGt5NY3V/YbHsQ6rcAHOw/kYxigewr5CZw4fYGxBKcCLOFEYehXrMdRhr5yLETxVScsOLOkKAPfn1TYMPIvLFrShUlS2FDZm8XRHACzFAWl3R2xbqPMCYhmeLCAOYEMngAczbcTvuHYxzguIy/FesR9e6gSwU/OoPYHBHgHgviIKX2Flq7k34KhmcVnbi/PC8JX4MgMcxb118wZwdz5aISscqx7VRcox7MrPQ7i+btIAJrAkf9+bI9EPmZY2IAxiTSuAldLq4Y9+AcSUh78KP0tbAcwU35cXMD1JCIFUoGiehlqAz6TNB1f1C0DK+0h+nsNPrQC2a4bqGmlD9kOGcWt+Po6pVgDvSxfJaSkFd4UQBvoAsBYbCoB3a2flM7slA0R8iyt6rAFDeDPbm8eOTpVwGD9qVq7nLbIaZnmksPU1JtsCZMXNmpdRxFasWITzh6Xj3LCzra1OxcD2QjHiGVzdpfORnMqZio2PcF23ABdJF1Np4BPptlyPi6WzPYBzpJZtHe7A6xW9cnyP8TqA//SEIYRL8Bxul7rihvwgtVn78WcGGZXa9HGd5TDujDHuOePXNiHdKjWgZX/YbsxLx/ktqbjVzTlcjUSnvI5JrdlUVp6WesZZ6R1hRrpq9+EVTGS9jTjYAuKIouGpbcurEkIYxC051KNSamazsc+xK8b4S0VnEi/j0hqTP+M27O258egQwZuzs7pI7Mf4WQXIEDc5s9sux+5+1Py2EmP8UOq6GvWhIScxfdYjUERiAt9Jd84J6a16zf8JEKT3yCm8g1UxRv8CC4pyRhzR1uUAAAAASUVORK5CYII="},
+menu:{image:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAgCAYAAAAbifjMAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAK6wAACusBgosNWgAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAAWdEVYdENyZWF0aW9uIFRpbWUAMDcvMTUvMTTPsvU0AAAAP0lEQVRIie2SMQoAIBDDUvH/X667g8sJJ9KOhYYOkW0qGaU1MPdC0vGSbV19EACo3YMPAFH5BUBUjsqfAPpVXtNgGDfxEDCtAAAAAElFTkSuQmCC"}};L.prototype.setOptions=function(a,b){if(ha[this._defaultsKey]){var c=ha[this._defaultsKey],d;for(d in c)this[d]=a&&d in a?a[d]:b&&d in b?b[d]:c[d]}};L.prototype.updateOption=
+function(a){var b=ha[this._defaultsKey],c=this._options.theme?this._options.theme:this.chart&&this.chart._options.theme?this.chart._options.theme:"theme1",d={},e=this[a];c&&(W[c]&&W[c][this._defaultsKey])&&(d=W[c][this._defaultsKey]);a in b&&(e=a in this._options?this._options[a]:d&&a in d?d[a]:b[a]);if(e===this[a])return!1;this[a]=e;return!0};L.prototype.trackChanges=function(a){this._options._oldOptions||(this._options._oldOptions={});this._options._oldOptions[a]=this._options[a]};L.prototype.isBeingTracked=
+function(a){this._options._oldOptions||(this._options._oldOptions={});return this._options._oldOptions[a]?!0:!1};L.prototype.hasOptionChanged=function(a){this._options._oldOptions||(this._options._oldOptions={});return this._options._oldOptions[a]!==this._options[a]};O(v,L);v.prototype._updateOptions=function(){var a=this;this.updateOption("width");this.updateOption("height");this.updateOption("theme");this.updateOption("colorSet")&&(this._selectedColorSet="undefined"!==typeof V[this.colorSet]?V[this.colorSet]:
+V.colorSet1);this.updateOption("backgroundColor");this.backgroundColor||(this.backgroundColor="rgba(0,0,0,0)");this.updateOption("culture");this._cultureInfo=new xa(this,this._options.culture);this.updateOption("animationEnabled");this.animationEnabled=this.animationEnabled&&t;this._options.zoomEnabled?(this._zoomButton||(S(this._zoomButton=document.createElement("button")),M(this,this._zoomButton,"pan"),this._toolBar.appendChild(this._zoomButton),E(this._zoomButton,"click",function(){a.zoomEnabled?
+(a.zoomEnabled=!1,a.panEnabled=!0,M(a,a._zoomButton,"zoom")):(a.zoomEnabled=!0,a.panEnabled=!1,M(a,a._zoomButton,"pan"));a.render()})),this._resetButton||(S(this._resetButton=document.createElement("button")),M(this,this._resetButton,"reset"),this._toolBar.appendChild(this._resetButton),E(this._resetButton,"click",function(){a._toolTip.hide();a.zoomEnabled||a.panEnabled?(a.zoomEnabled=!0,a.panEnabled=!1,M(a,a._zoomButton,"pan"),a._defaultCursor="default",a.overlaidCanvas.style.cursor=a._defaultCursor):
+(a.zoomEnabled=!1,a.panEnabled=!1);a.sessionVariables.axisX.internalMinimum=a._options.axisX&&a._options.axisX.minimum?a._options.axisX.minimum:null;a.sessionVariables.axisX.internalMaximum=a._options.axisX&&a._options.axisX.maximum?a._options.axisX.maximum:null;a.resetOverlayedCanvas();S(a._zoomButton,a._resetButton);a.render()}),this.overlaidCanvas.style.cursor=a._defaultCursor),this.zoomEnabled||this.panEnabled||(this._zoomButton?(a._zoomButton.getAttribute("state")===a._cultureInfo.zoomText?(this.panEnabled=
+!0,this.zoomEnabled=!1):(this.zoomEnabled=!0,this.panEnabled=!1),ka(a._zoomButton,a._resetButton)):(this.zoomEnabled=!0,this.panEnabled=!1))):this.panEnabled=this.zoomEnabled=!1;"undefined"!==typeof this._options.exportFileName&&(this.exportFileName=this._options.exportFileName);"undefined"!==typeof this._options.exportEnabled&&(this.exportEnabled=this._options.exportEnabled);this._menuButton?this.exportEnabled?ka(this._menuButton):S(this._menuButton):this.exportEnabled&&t&&(this._menuButton=document.createElement("button"),
+M(this,this._menuButton,"menu"),this._toolBar.appendChild(this._menuButton),E(this._menuButton,"click",function(){"none"!==a._dropdownMenu.style.display||a._dropDownCloseTime&&500>=(new Date).getTime()-a._dropDownCloseTime.getTime()||(a._dropdownMenu.style.display="block",a._menuButton.blur(),a._dropdownMenu.focus())},!0));if(!this._dropdownMenu&&this.exportEnabled&&t){this._dropdownMenu=document.createElement("div");this._dropdownMenu.setAttribute("tabindex",-1);this._dropdownMenu.style.cssText=
+"position: absolute; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; cursor: pointer;right: 1px;top: 25px;min-width: 120px;outline: 0;border: 1px solid silver;font-size: 14px;font-family: Calibri, Verdana, sans-serif;padding: 5px 0px 5px 0px;text-align: left;background-color: #fff;line-height: 20px;box-shadow: 2px 2px 10px #888888;";a._dropdownMenu.style.display="none";this._toolBar.appendChild(this._dropdownMenu);E(this._dropdownMenu,"blur",function(){S(a._dropdownMenu);
+a._dropDownCloseTime=new Date},!0);var b=document.createElement("div");b.style.cssText="padding: 2px 15px 2px 10px";b.innerHTML=this._cultureInfo.saveJPGText;this._dropdownMenu.appendChild(b);E(b,"mouseover",function(){this.style.backgroundColor="#EEEEEE"},!0);E(b,"mouseout",function(){this.style.backgroundColor="transparent"},!0);E(b,"click",function(){wa(a.canvas,"jpg",a.exportFileName);S(a._dropdownMenu)},!0);b=document.createElement("div");b.style.cssText="padding: 2px 15px 2px 10px";b.innerHTML=
+this._cultureInfo.savePNGText;this._dropdownMenu.appendChild(b);E(b,"mouseover",function(){this.style.backgroundColor="#EEEEEE"},!0);E(b,"mouseout",function(){this.style.backgroundColor="transparent"},!0);E(b,"click",function(){wa(a.canvas,"png",a.exportFileName);S(a._dropdownMenu)},!0)}"none"!==this._toolBar.style.display&&this._zoomButton&&(this.panEnabled?M(a,a._zoomButton,"zoom"):M(a,a._zoomButton,"pan"),a._resetButton.getAttribute("state")!==a._cultureInfo.resetText&&M(a,a._resetButton,"reset"));
+if("undefined"===typeof ha.Chart.creditHref)this.creditHref="http://canvasjs.com/",this.creditText="CanvasJS.com";else var c=this.updateOption("creditText"),d=this.updateOption("creditHref");if(0===this.renderCount||c||d)this._creditLink.setAttribute("href",this.creditHref),this._creditLink.innerHTML=this.creditText;this.creditHref&&this.creditText?this._creditLink.parentElement||this._canvasJSContainer.appendChild(this._creditLink):this._creditLink.parentElement&&this._canvasJSContainer.removeChild(this._creditLink);
+this._options.toolTip&&this._toolTip._options!==this._options.toolTip&&(this._toolTip._options=this._options.toolTip);this._toolTip.updateOption("enabled");this._toolTip.updateOption("shared");this._toolTip.updateOption("animationEnabled");this._toolTip.updateOption("borderColor");this._toolTip.updateOption("content")};v.prototype._updateSize=function(){var a=0,b=0;this._options.width?a=this.width:this.width=a=0<this._container.clientWidth?this._container.clientWidth:this.width;this._options.height?
+b=this.height:this.height=b=0<this._container.clientHeight?this._container.clientHeight:this.height;return this.canvas.width!==a*H||this.canvas.height!==b*H?(da(this.canvas,a,b),da(this.overlaidCanvas,a,b),da(this._eventManager.ghostCanvas,a,b),!0):!1};v.prototype._initialize=function(){this._animator?this._animator.cancelAllAnimations():this._animator=new oa(this);this.disableToolTip=!1;this.pieDoughnutClickHandler=null;this.animationRequestId&&this.cancelRequestAnimFrame.call(window,this.animationRequestId);
+this._updateOptions();this.animatedRender=t&&this.animationEnabled&&0===this.renderCount;this._updateSize();this.clearCanvas();this.ctx.beginPath();this.axisY2=this.axisY=this.axisX=null;this._indexLabels=[];this._dataInRenderedOrder=[];this._events=[];this._eventManager&&this._eventManager.reset();this.plotInfo={axisPlacement:null,axisXValueType:null,plotTypes:[]};this.layoutManager.reset();this.data=[];for(var a=0,b=0;b<this._options.data.length;b++)if(a++,!this._options.data[b].type||0<=v._supportedChartTypes.indexOf(this._options.data[b].type)){var c=
+new P(this,this._options.data[b],this.theme,a-1,++this._eventManager.lastObjectId);null===c.name&&(c.name="DataSeries "+a);null===c.color?1<this._options.data.length?(c._colorSet=[this._selectedColorSet[c.index%this._selectedColorSet.length]],c.color=this._selectedColorSet[c.index%this._selectedColorSet.length]):c._colorSet="line"===c.type||"stepLine"===c.type||"spline"===c.type||"area"===c.type||"stepArea"===c.type||"splineArea"===c.type||"stackedArea"===c.type||"stackedArea100"===c.type||"rangeArea"===
+c.type||"rangeSplineArea"===c.type||"candlestick"===c.type||"ohlc"===c.type?[this._selectedColorSet[0]]:this._selectedColorSet:c._colorSet=[c.color];null===c.markerSize&&(("line"===c.type||"stepLine"===c.type||"spline"===c.type)&&c.dataPoints&&c.dataPoints.length<this.width/16||"scatter"===c.type)&&(c.markerSize=8);"bubble"!==c.type&&"scatter"!==c.type||!c.dataPoints||c.dataPoints.sort(Ca);this.data.push(c);var d=c.axisPlacement,e;"normal"===d?"xySwapped"===this.plotInfo.axisPlacement?e='You cannot combine "'+
+c.type+'" with bar chart':"none"===this.plotInfo.axisPlacement?e='You cannot combine "'+c.type+'" with pie chart':null===this.plotInfo.axisPlacement&&(this.plotInfo.axisPlacement="normal"):"xySwapped"===d?"normal"===this.plotInfo.axisPlacement?e='You cannot combine "'+c.type+'" with line, area, column or pie chart':"none"===this.plotInfo.axisPlacement?e='You cannot combine "'+c.type+'" with pie chart':null===this.plotInfo.axisPlacement&&(this.plotInfo.axisPlacement="xySwapped"):"none"==d&&("normal"===
+this.plotInfo.axisPlacement?e='You cannot combine "'+c.type+'" with line, area, column or bar chart':"xySwapped"===this.plotInfo.axisPlacement?e='You cannot combine "'+c.type+'" with bar chart':null===this.plotInfo.axisPlacement&&(this.plotInfo.axisPlacement="none"));if(e&&window.console){window.console.log(e);return}}this._objectsInitialized=!0};v._supportedChartTypes="line stepLine spline column area stepArea splineArea bar bubble scatter stackedColumn stackedColumn100 stackedBar stackedBar100 stackedArea stackedArea100 candlestick ohlc rangeColumn rangeBar rangeArea rangeSplineArea pie doughnut funnel".split(" ");
+v._supportedChartTypes.indexOf||(v._supportedChartTypes.indexOf=function(a,b){var c=this.length>>>0,d=Number(b)||0,d=0>d?Math.ceil(d):Math.floor(d);for(0>d&&(d+=c);d<c;d++)if(d in this&&this[d]===a)return d;return-1});v.prototype.render=function(a){a&&(this._options=a);this._initialize();for(a=0;a<this.data.length;a++)if("normal"===this.plotInfo.axisPlacement||"xySwapped"===this.plotInfo.axisPlacement)this.data[a].axisYType&&"primary"!==this.data[a].axisYType?"secondary"===this.data[a].axisYType&&
+(this.axisY2||("normal"===this.plotInfo.axisPlacement?this.axisY2=new A(this,this._options.axisY2,"axisY","right"):"xySwapped"===this.plotInfo.axisPlacement&&(this.axisY2=new A(this,this._options.axisY2,"axisY","top"))),this.data[a].axisY=this.axisY2):(this.axisY||("normal"===this.plotInfo.axisPlacement?this.axisY=new A(this,this._options.axisY,"axisY","left"):"xySwapped"===this.plotInfo.axisPlacement&&(this.axisY=new A(this,this._options.axisY,"axisY","bottom"))),this.data[a].axisY=this.axisY),this.axisX||
+("normal"===this.plotInfo.axisPlacement?this.axisX=new A(this,this._options.axisX,"axisX","bottom"):"xySwapped"===this.plotInfo.axisPlacement&&(this.axisX=new A(this,this._options.axisX,"axisX","left"))),this.data[a].axisX=this.axisX;this._processData();this._options.title&&(this._title=new ba(this,this._options.title),this._title.render());this.legend=new ga(this,this._options.legend,this.theme);for(a=0;a<this.data.length;a++)this.data[a].showInLegend&&this.legend.dataSeries.push(this.data[a]);this.legend.render();
+if("normal"===this.plotInfo.axisPlacement||"xySwapped"===this.plotInfo.axisPlacement)this.layoutManager.getFreeSpace(),A.setLayoutAndRender(this.axisX,this.axisY,this.axisY2,this.plotInfo.axisPlacement,this.layoutManager.getFreeSpace());else if("none"===this.plotInfo.axisPlacement)this.preparePlotArea();else return;var b=[];if(this.animatedRender){var c=U(this.width,this.height);c.getContext("2d").drawImage(this.canvas,0,0,this.width,this.height)}for(a=0;a<this.plotInfo.plotTypes.length;a++)for(var d=
+this.plotInfo.plotTypes[a],e=0;e<d.plotUnits.length;e++){var f=d.plotUnits[e],g=null;f.targetCanvas=null;this.animatedRender&&(f.targetCanvas=U(this.width,this.height),f.targetCanvasCtx=f.targetCanvas.getContext("2d"));"line"===f.type?g=this.renderLine(f):"stepLine"===f.type?g=this.renderStepLine(f):"spline"===f.type?g=this.renderSpline(f):"column"===f.type?g=this.renderColumn(f):"bar"===f.type?g=this.renderBar(f):"area"===f.type?g=this.renderArea(f):"stepArea"===f.type?g=this.renderStepArea(f):"splineArea"===
+f.type?g=this.renderSplineArea(f):"stackedColumn"===f.type?g=this.renderStackedColumn(f):"stackedColumn100"===f.type?g=this.renderStackedColumn100(f):"stackedBar"===f.type?g=this.renderStackedBar(f):"stackedBar100"===f.type?g=this.renderStackedBar100(f):"stackedArea"===f.type?g=this.renderStackedArea(f):"stackedArea100"===f.type?g=this.renderStackedArea100(f):"bubble"===f.type?g=g=this.renderBubble(f):"scatter"===f.type?g=this.renderScatter(f):"pie"===f.type?this.renderPie(f):"doughnut"===f.type?
+this.renderPie(f):"candlestick"===f.type?g=this.renderCandlestick(f):"ohlc"===f.type?g=this.renderCandlestick(f):"rangeColumn"===f.type?g=this.renderRangeColumn(f):"rangeBar"===f.type?g=this.renderRangeBar(f):"rangeArea"===f.type?g=this.renderRangeArea(f):"rangeSplineArea"===f.type&&(g=this.renderRangeSplineArea(f));for(var k=0;k<f.dataSeriesIndexes.length;k++)this._dataInRenderedOrder.push(this.data[f.dataSeriesIndexes[k]]);this.animatedRender&&g&&b.push(g)}this.animatedRender&&0<this._indexLabels.length&&
+(a=U(this.width,this.height).getContext("2d"),b.push(this.renderIndexLabels(a)));var p=this;0<b.length?(p.disableToolTip=!0,p._animator.animate(200,p.animationDuration,function(a){p.ctx.clearRect(0,0,p.width,p.height);p.ctx.drawImage(c,0,0,p.width*H,p.height*H,0,0,p.width,p.height);for(var d=0;d<b.length;d++)g=b[d],1>a&&"undefined"!==typeof g.startTimePercent?a>=g.startTimePercent&&g.animationCallback(g.easingFunction(a-g.startTimePercent,0,1,1-g.startTimePercent),g):g.animationCallback(g.easingFunction(a,
+0,1,1),g)},function(){b=[];for(var a=0;a<p.plotInfo.plotTypes.length;a++)for(var d=p.plotInfo.plotTypes[a],e=0;e<d.plotUnits.length;e++)d.plotUnits[e].targetCanvas=null;c=null;p.disableToolTip=!1})):0<p._indexLabels.length&&p.renderIndexLabels();this.attachPlotAreaEventHandlers();this.zoomEnabled||(this.panEnabled||!this._zoomButton||"none"===this._zoomButton.style.display)||S(this._zoomButton,this._resetButton);this._toolTip._updateToolTip();this.renderCount++};v.prototype.attachPlotAreaEventHandlers=
+function(){this.attachEvent({context:this,chart:this,mousedown:this._plotAreaMouseDown,mouseup:this._plotAreaMouseUp,mousemove:this._plotAreaMouseMove,cursor:this.zoomEnabled?"col-resize":"move",cursor:this.panEnabled?"move":"default",capture:!0,bounds:this.plotArea})};v.prototype.categoriseDataSeries=function(){for(var a="",b=0;b<this.data.length;b++)if(a=this.data[b],a.dataPoints&&(0!==a.dataPoints.length&&a.visible)&&0<=v._supportedChartTypes.indexOf(a.type)){for(var c=null,d=!1,e=null,f=!1,g=
+0;g<this.plotInfo.plotTypes.length;g++)if(this.plotInfo.plotTypes[g].type===a.type){d=!0;c=this.plotInfo.plotTypes[g];break}d||(c={type:a.type,totalDataSeries:0,plotUnits:[]},this.plotInfo.plotTypes.push(c));for(g=0;g<c.plotUnits.length;g++)if(c.plotUnits[g].axisYType===a.axisYType){f=!0;e=c.plotUnits[g];break}f||(e={type:a.type,previousDataSeriesCount:0,index:c.plotUnits.length,plotType:c,axisYType:a.axisYType,axisY:"primary"===a.axisYType?this.axisY:this.axisY2,axisX:this.axisX,dataSeriesIndexes:[],
+yTotals:[]},c.plotUnits.push(e));c.totalDataSeries++;e.dataSeriesIndexes.push(b);a.plotUnit=e}for(b=0;b<this.plotInfo.plotTypes.length;b++)for(c=this.plotInfo.plotTypes[b],g=a=0;g<c.plotUnits.length;g++)c.plotUnits[g].previousDataSeriesCount=a,a+=c.plotUnits[g].dataSeriesIndexes.length};v.prototype.assignIdToDataPoints=function(){for(var a=0;a<this.data.length;a++){var b=this.data[a];if(b.dataPoints)for(var c=b.dataPoints.length,d=0;d<c;d++)b.dataPointIds[d]=++this._eventManager.lastObjectId}};v.prototype._processData=
+function(){this.assignIdToDataPoints();this.categoriseDataSeries();for(var a=0;a<this.plotInfo.plotTypes.length;a++)for(var b=this.plotInfo.plotTypes[a],c=0;c<b.plotUnits.length;c++){var d=b.plotUnits[c];"line"===d.type||"stepLine"===d.type||"spline"===d.type||"column"===d.type||"area"===d.type||"stepArea"===d.type||"splineArea"===d.type||"bar"===d.type||"bubble"===d.type||"scatter"===d.type?this._processMultiseriesPlotUnit(d):"stackedColumn"===d.type||"stackedBar"===d.type||"stackedArea"===d.type?
+this._processStackedPlotUnit(d):"stackedColumn100"===d.type||"stackedBar100"===d.type||"stackedArea100"===d.type?this._processStacked100PlotUnit(d):"candlestick"!==d.type&&"ohlc"!==d.type&&"rangeColumn"!==d.type&&"rangeBar"!==d.type&&"rangeArea"!==d.type&&"rangeSplineArea"!==d.type||this._processMultiYPlotUnit(d)}};v.prototype._processMultiseriesPlotUnit=function(a){if(a.dataSeriesIndexes&&!(1>a.dataSeriesIndexes.length))for(var b=a.axisY.dataInfo,c=a.axisX.dataInfo,d,e,f=!1,g=0;g<a.dataSeriesIndexes.length;g++){var k=
+this.data[a.dataSeriesIndexes[g]],p=0,h=!1,l=!1;if("normal"===k.axisPlacement||"xySwapped"===k.axisPlacement)var r=this.sessionVariables.axisX.internalMinimum?this.sessionVariables.axisX.internalMinimum:this._options.axisX&&this._options.axisX.minimum?this._options.axisX.minimum:-Infinity,m=this.sessionVariables.axisX.internalMaximum?this.sessionVariables.axisX.internalMaximum:this._options.axisX&&this._options.axisX.maximum?this._options.axisX.maximum:Infinity;if(k.dataPoints[p].x&&k.dataPoints[p].x.getTime||
+"dateTime"===k.xValueType)f=!0;for(p=0;p<k.dataPoints.length;p++){"undefined"===typeof k.dataPoints[p].x&&(k.dataPoints[p].x=p);k.dataPoints[p].x.getTime?(f=!0,d=k.dataPoints[p].x.getTime()):d=k.dataPoints[p].x;e=k.dataPoints[p].y;d<c.min&&(c.min=d);d>c.max&&(c.max=d);e<b.min&&(b.min=e);e>b.max&&(b.max=e);if(0<p){var q=d-k.dataPoints[p-1].x;0>q&&(q*=-1);c.minDiff>q&&0!==q&&(c.minDiff=q)}if(!(d<r)||h){if(!h&&(h=!0,0<p)){p-=2;continue}if(d>m&&!l)l=!0;else if(d>m&&l)continue;k.dataPoints[p].label&&(a.axisX.labels[d]=
+k.dataPoints[p].label);d<c.viewPortMin&&(c.viewPortMin=d);d>c.viewPortMax&&(c.viewPortMax=d);null!==e&&(e<b.viewPortMin&&(b.viewPortMin=e),e>b.viewPortMax&&(b.viewPortMax=e))}}this.plotInfo.axisXValueType=k.xValueType=f?"dateTime":"number"}};v.prototype._processStackedPlotUnit=function(a){if(a.dataSeriesIndexes&&!(1>a.dataSeriesIndexes.length)){for(var b=a.axisY.dataInfo,c=a.axisX.dataInfo,d,e,f=!1,g=[],k=[],p=0;p<a.dataSeriesIndexes.length;p++){var h=this.data[a.dataSeriesIndexes[p]],l=0,r=!1,m=
+!1;if("normal"===h.axisPlacement||"xySwapped"===h.axisPlacement)var q=this.sessionVariables.axisX.internalMinimum?this.sessionVariables.axisX.internalMinimum:this._options.axisX&&this._options.axisX.minimum?this._options.axisX.minimum:-Infinity,n=this.sessionVariables.axisX.internalMaximum?this.sessionVariables.axisX.internalMaximum:this._options.axisX&&this._options.axisX.maximum?this._options.axisX.maximum:Infinity;if(h.dataPoints[l].x&&h.dataPoints[l].x.getTime||"dateTime"===h.xValueType)f=!0;
+for(l=0;l<h.dataPoints.length;l++){"undefined"===typeof h.dataPoints[l].x&&(h.dataPoints[l].x=l);h.dataPoints[l].x.getTime?(f=!0,d=h.dataPoints[l].x.getTime()):d=h.dataPoints[l].x;e=h.dataPoints[l].y;d<c.min&&(c.min=d);d>c.max&&(c.max=d);if(0<l){var s=d-h.dataPoints[l-1].x;0>s&&(s*=-1);c.minDiff>s&&0!==s&&(c.minDiff=s)}if(!(d<q)||r){if(!r&&(r=!0,0<l)){l-=2;continue}if(d>n&&!m)m=!0;else if(d>n&&m)continue;h.dataPoints[l].label&&(a.axisX.labels[d]=h.dataPoints[l].label);d<c.viewPortMin&&(c.viewPortMin=
+d);d>c.viewPortMax&&(c.viewPortMax=d);null!==e&&(a.yTotals[d]=(a.yTotals[d]?a.yTotals[d]:0)+Math.abs(e),0<=e?g[d]=g[d]?g[d]+e:e:k[d]=k[d]?k[d]+e:e)}}this.plotInfo.axisXValueType=h.xValueType=f?"dateTime":"number"}for(l in g)isNaN(l)||(a=g[l],a<b.min&&(b.min=a),a>b.max&&(b.max=a),l<c.viewPortMin||l>c.viewPortMax||(a<b.viewPortMin&&(b.viewPortMin=a),a>b.viewPortMax&&(b.viewPortMax=a)));for(l in k)isNaN(l)||(a=k[l],a<b.min&&(b.min=a),a>b.max&&(b.max=a),l<c.viewPortMin||l>c.viewPortMax||(a<b.viewPortMin&&
+(b.viewPortMin=a),a>b.viewPortMax&&(b.viewPortMax=a)))}};v.prototype._processStacked100PlotUnit=function(a){if(a.dataSeriesIndexes&&!(1>a.dataSeriesIndexes.length)){for(var b=a.axisY.dataInfo,c=a.axisX.dataInfo,d,e,f=!1,g=!1,k=!1,p=[],h=0;h<a.dataSeriesIndexes.length;h++){var l=this.data[a.dataSeriesIndexes[h]],r=0,m=!1,q=!1;if("normal"===l.axisPlacement||"xySwapped"===l.axisPlacement)var n=this.sessionVariables.axisX.internalMinimum?this.sessionVariables.axisX.internalMinimum:this._options.axisX&&
+this._options.axisX.minimum?this._options.axisX.minimum:-Infinity,s=this.sessionVariables.axisX.internalMaximum?this.sessionVariables.axisX.internalMaximum:this._options.axisX&&this._options.axisX.maximum?this._options.axisX.maximum:Infinity;if(l.dataPoints[r].x&&l.dataPoints[r].x.getTime||"dateTime"===l.xValueType)f=!0;for(r=0;r<l.dataPoints.length;r++){"undefined"===typeof l.dataPoints[r].x&&(l.dataPoints[r].x=r);l.dataPoints[r].x.getTime?(f=!0,d=l.dataPoints[r].x.getTime()):d=l.dataPoints[r].x;
+e=l.dataPoints[r].y;d<c.min&&(c.min=d);d>c.max&&(c.max=d);if(0<r){var t=d-l.dataPoints[r-1].x;0>t&&(t*=-1);c.minDiff>t&&0!==t&&(c.minDiff=t)}if(!(d<n)||m){if(!m&&(m=!0,0<r)){r-=2;continue}if(d>s&&!q)q=!0;else if(d>s&&q)continue;l.dataPoints[r].label&&(a.axisX.labels[d]=l.dataPoints[r].label);d<c.viewPortMin&&(c.viewPortMin=d);d>c.viewPortMax&&(c.viewPortMax=d);null!==e&&(a.yTotals[d]=(a.yTotals[d]?a.yTotals[d]:0)+Math.abs(e),0<=e?g=!0:k=!0,p[d]=p[d]?p[d]+Math.abs(e):Math.abs(e))}}this.plotInfo.axisXValueType=
+l.xValueType=f?"dateTime":"number"}g&&!k?(b.max=99,b.min=1):g&&k?(b.max=99,b.min=-99):!g&&k&&(b.max=-1,b.min=-99);b.viewPortMin=b.min;b.viewPortMax=b.max;a.dataPointYSums=p}};v.prototype._processMultiYPlotUnit=function(a){if(a.dataSeriesIndexes&&!(1>a.dataSeriesIndexes.length))for(var b=a.axisY.dataInfo,c=a.axisX.dataInfo,d,e,f,g,k=!1,p=0;p<a.dataSeriesIndexes.length;p++){var h=this.data[a.dataSeriesIndexes[p]],l=0,r=!1,m=!1;if("normal"===h.axisPlacement||"xySwapped"===h.axisPlacement)var q=this.sessionVariables.axisX.internalMinimum?
+this.sessionVariables.axisX.internalMinimum:this._options.axisX&&this._options.axisX.minimum?this._options.axisX.minimum:-Infinity,n=this.sessionVariables.axisX.internalMaximum?this.sessionVariables.axisX.internalMaximum:this._options.axisX&&this._options.axisX.maximum?this._options.axisX.maximum:Infinity;if(h.dataPoints[l].x&&h.dataPoints[l].x.getTime||"dateTime"===h.xValueType)k=!0;for(l=0;l<h.dataPoints.length;l++){"undefined"===typeof h.dataPoints[l].x&&(h.dataPoints[l].x=l);h.dataPoints[l].x.getTime?
+(k=!0,d=h.dataPoints[l].x.getTime()):d=h.dataPoints[l].x;(e=h.dataPoints[l].y)&&e.length&&(f=Math.min.apply(null,e),g=Math.max.apply(null,e));d<c.min&&(c.min=d);d>c.max&&(c.max=d);f<b.min&&(b.min=f);g>b.max&&(b.max=g);if(0<l){var s=d-h.dataPoints[l-1].x;0>s&&(s*=-1);c.minDiff>s&&0!==s&&(c.minDiff=s)}if(!(d<q)||r){if(!r&&(r=!0,0<l)){l-=2;continue}if(d>n&&!m)m=!0;else if(d>n&&m)continue;h.dataPoints[l].label&&(a.axisX.labels[d]=h.dataPoints[l].label);d<c.viewPortMin&&(c.viewPortMin=d);d>c.viewPortMax&&
+(c.viewPortMax=d);null!==e&&(f<b.viewPortMin&&(b.viewPortMin=f),g>b.viewPortMax&&(b.viewPortMax=g))}}this.plotInfo.axisXValueType=h.xValueType=k?"dateTime":"number"}};v.prototype.getDataPointAtXY=function(a,b,c){c=c||!1;for(var d=[],e=this._dataInRenderedOrder.length-1;0<=e;e--){var f=null;(f=this._dataInRenderedOrder[e].getDataPointAtXY(a,b,c))&&d.push(f)}a=null;b=!1;for(c=0;c<d.length;c++)if("line"===d[c].dataSeries.type||"stepLine"===d[c].dataSeries.type||"area"===d[c].dataSeries.type||"stepArea"===
+d[c].dataSeries.type)if(e=T("markerSize",d[c].dataPoint,d[c].dataSeries)||8,d[c].distance<=e/2){b=!0;break}for(c=0;c<d.length;c++)b&&"line"!==d[c].dataSeries.type&&"stepLine"!==d[c].dataSeries.type&&"area"!==d[c].dataSeries.type&&"stepArea"!==d[c].dataSeries.type||(a?d[c].distance<=a.distance&&(a=d[c]):a=d[c]);return a};v.prototype.getObjectAtXY=function(a,b,c){var d=null;if(c=this.getDataPointAtXY(a,b,c||!1))d=c.dataSeries.dataPointIds[c.dataPointIndex];else if(t)d=ta(a,b,this._eventManager.ghostCtx);
+else for(c=0;c<this.legend.items.length;c++){var e=this.legend.items[c];a>=e.x1&&(a<=e.x2&&b>=e.y1&&b<=e.y2)&&(d=e.id)}return d};v.prototype.getAutoFontSize=function(a,b,c){a/=400;return Math.round(Math.min(this.width,this.height)*a)};v.prototype.resetOverlayedCanvas=function(){this.overlaidCanvasCtx.clearRect(0,0,this.width,this.height)};v.prototype.clearCanvas=function(){this.ctx.clearRect(0,0,this.width,this.height);this.backgroundColor&&(this.ctx.fillStyle=this.backgroundColor,this.ctx.fillRect(0,
+0,this.width,this.height))};v.prototype.attachEvent=function(a){this._events.push(a)};v.prototype._touchEventHandler=function(a){if(a.changedTouches&&this.interactivityEnabled){var b=[],c=a.changedTouches,d=c?c[0]:a,e=null;switch(a.type){case "touchstart":case "MSPointerDown":b=["mousemove","mousedown"];this._lastTouchData=ia(d);this._lastTouchData.time=new Date;break;case "touchmove":case "MSPointerMove":b=["mousemove"];break;case "touchend":case "MSPointerUp":b="touchstart"===this._lastTouchEventType||
+"MSPointerDown"===this._lastTouchEventType?["mouseup","click"]:["mouseup"];break;default:return}if(!(c&&1<c.length)){e=ia(d);e.time=new Date;try{var f=e.y-this._lastTouchData.y,g=e.time-this._lastTouchData.time;if(15<Math.abs(f)&&(this._lastTouchData.scroll||200>g)){this._lastTouchData.scroll=!0;var k=window.parent||window;k&&k.scrollBy&&k.scrollBy(0,-f)}}catch(p){}this._lastTouchEventType=a.type;if(this._lastTouchData.scroll&&this.zoomEnabled)this.isDrag&&this.resetOverlayedCanvas(),this.isDrag=
+!1;else for(c=0;c<b.length;c++)e=b[c],f=document.createEvent("MouseEvent"),f.initMouseEvent(e,!0,!0,window,1,d.screenX,d.screenY,d.clientX,d.clientY,!1,!1,!1,!1,0,null),d.target.dispatchEvent(f),a.preventManipulation&&a.preventManipulation(),a.preventDefault&&a.preventDefault()}}};v.prototype._mouseEventHandler=function(a){if(this.interactivityEnabled)if(this._ignoreNextEvent)this._ignoreNextEvent=!1;else{a.preventManipulation&&a.preventManipulation();a.preventDefault&&a.preventDefault();"undefined"===
+typeof a.target&&a.srcElement&&(a.target=a.srcElement);var b=ia(a),c=a.type,d,e;a.which?e=3==a.which:a.button&&(e=2==a.button);if(!e){if(v.capturedEventParam)d=v.capturedEventParam,"mouseup"===c&&(v.capturedEventParam=null,d.chart.overlaidCanvas.releaseCapture?d.chart.overlaidCanvas.releaseCapture():document.body.removeEventListener("mouseup",d.chart._mouseEventHandler,!1)),d.hasOwnProperty(c)&&d[c].call(d.context,b.x,b.y);else if(this._events){for(e=0;e<this._events.length;e++)if(this._events[e].hasOwnProperty(c)){d=
+this._events[e];var f=d.bounds;if(b.x>=f.x1&&b.x<=f.x2&&b.y>=f.y1&&b.y<=f.y2){d[c].call(d.context,b.x,b.y);"mousedown"===c&&!0===d.capture?(v.capturedEventParam=d,this.overlaidCanvas.setCapture?this.overlaidCanvas.setCapture():document.body.addEventListener("mouseup",this._mouseEventHandler,!1)):"mouseup"===c&&(d.chart.overlaidCanvas.releaseCapture?d.chart.overlaidCanvas.releaseCapture():document.body.removeEventListener("mouseup",this._mouseEventHandler,!1));break}else d=null}a.target.style.cursor=
+d&&d.cursor?d.cursor:this._defaultCursor}this._toolTip&&this._toolTip.enabled&&(c=this.plotArea,(b.x<c.x1||b.x>c.x2||b.y<c.y1||b.y>c.y2)&&this._toolTip.hide());this.isDrag&&this.zoomEnabled||!this._eventManager||this._eventManager.mouseEventHandler(a)}}};v.prototype._plotAreaMouseDown=function(a,b){this.isDrag=!0;this.dragStartPoint="none"!==this.plotInfo.axisPlacement?{x:a,y:b,xMinimum:this.axisX.minimum,xMaximum:this.axisX.maximum}:{x:a,y:b}};v.prototype._plotAreaMouseUp=function(a,b){var c,d;if(("normal"===
+this.plotInfo.axisPlacement||"xySwapped"===this.plotInfo.axisPlacement)&&this.isDrag){var e=0,e=this.axisX.lineCoordinates,e="xySwapped"===this.plotInfo.axisPlacement?b-this.dragStartPoint.y:this.dragStartPoint.x-a;Math.abs(this.axisX.maximum-this.axisX.minimum);if(2<Math.abs(e)){if(this.panEnabled)e=!1,d=0,this.axisX.sessionVariables.internalMinimum<this.axisX._absoluteMinimum?(d=this.axisX._absoluteMinimum-this.axisX.sessionVariables.internalMinimum,this.axisX.sessionVariables.internalMinimum+=
+d,this.axisX.sessionVariables.internalMaximum+=d,e=!0):this.axisX.sessionVariables.internalMaximum>this.axisX._absoluteMaximum&&(d=this.axisX.sessionVariables.internalMaximum-this.axisX._absoluteMaximum,this.axisX.sessionVariables.internalMaximum-=d,this.axisX.sessionVariables.internalMinimum-=d,e=!0),e&&this.render();else if(this.zoomEnabled){this.resetOverlayedCanvas();if(!this.dragStartPoint)return;"xySwapped"===this.plotInfo.axisPlacement?(c=Math.min(this.dragStartPoint.y,b),d=Math.max(this.dragStartPoint.y,
+b),1<Math.abs(c-d)&&(e=this.axisX.lineCoordinates,d=this.axisX.maximum-(this.axisX.maximum-this.axisX.minimum)/e.height*(d-e.y1),e=this.axisX.maximum-(this.axisX.maximum-this.axisX.minimum)/e.height*(c-e.y1),d=Math.max(d,this.axisX.dataInfo.min),e=Math.min(e,this.axisX.dataInfo.max),Math.abs(e-d)>2*Math.abs(this.axisX.dataInfo.minDiff)&&(this.axisX.sessionVariables.internalMinimum=d,this.axisX.sessionVariables.internalMaximum=e,this.render()))):"normal"===this.plotInfo.axisPlacement&&(d=Math.min(this.dragStartPoint.x,
+a),c=Math.max(this.dragStartPoint.x,a),1<Math.abs(d-c)&&(e=this.axisX.lineCoordinates,d=(this.axisX.maximum-this.axisX.minimum)/e.width*(d-e.x1)+this.axisX.minimum,e=(this.axisX.maximum-this.axisX.minimum)/e.width*(c-e.x1)+this.axisX.minimum,d=Math.max(d,this.axisX.dataInfo.min),e=Math.min(e,this.axisX.dataInfo.max),Math.abs(e-d)>2*Math.abs(this.axisX.dataInfo.minDiff)&&(this.axisX.sessionVariables.internalMinimum=d,this.axisX.sessionVariables.internalMaximum=e,this.render())))}this._ignoreNextEvent=
+!0;this.zoomEnabled&&"none"===this._zoomButton.style.display&&(ka(this._zoomButton,this._resetButton),M(this,this._zoomButton,"pan"),M(this,this._resetButton,"reset"))}}this.isDrag=!1};v.prototype._plotAreaMouseMove=function(a,b){if(this.isDrag&&"none"!==this.plotInfo.axisPlacement){var c=0,d=0,d=this.axisX.lineCoordinates;"xySwapped"===this.plotInfo.axisPlacement?(c=b-this.dragStartPoint.y,d=Math.abs(this.axisX.maximum-this.axisX.minimum)/d.height*c):(c=this.dragStartPoint.x-a,d=Math.abs(this.axisX.maximum-
+this.axisX.minimum)/d.width*c);2<Math.abs(c)&&8>Math.abs(c)&&(this.panEnabled||this.zoomEnabled)?this._toolTip.hide():!this._toolTip.enabled||(this.panEnabled||this.zoomEnabled)||this._toolTip.mouseMoveHandler(a,b);if(2<Math.abs(c)&&(this.panEnabled||this.zoomEnabled))if(this.panEnabled){this.axisX.sessionVariables.internalMinimum=this.dragStartPoint.xMinimum+d;this.axisX.sessionVariables.internalMaximum=this.dragStartPoint.xMaximum+d;c=0;this.axisX.sessionVariables.internalMinimum<this.axisX._absoluteMinimum-
+Y(this.axisX.interval,this.axisX.intervalType)?(c=this.axisX._absoluteMinimum-Y(this.axisX.interval,this.axisX.intervalType)-this.axisX.sessionVariables.internalMinimum,this.axisX.sessionVariables.internalMinimum+=c,this.axisX.sessionVariables.internalMaximum+=c):this.axisX.sessionVariables.internalMaximum>this.axisX._absoluteMaximum+Y(this.axisX.interval,this.axisX.intervalType)&&(c=this.axisX.sessionVariables.internalMaximum-(this.axisX._absoluteMaximum+Y(this.axisX.interval,this.axisX.intervalType)),
+this.axisX.sessionVariables.internalMaximum-=c,this.axisX.sessionVariables.internalMinimum-=c);var e=this;clearTimeout(this._panTimerId);this._panTimerId=setTimeout(function(){e.render()},0)}else this.zoomEnabled&&(c=this.plotArea,this.resetOverlayedCanvas(),d=this.overlaidCanvasCtx.globalAlpha,this.overlaidCanvasCtx.globalAlpha=0.7,this.overlaidCanvasCtx.fillStyle="#A0ABB8","xySwapped"===this.plotInfo.axisPlacement?this.overlaidCanvasCtx.fillRect(c.x1,this.dragStartPoint.y,c.x2-c.x1,b-this.dragStartPoint.y):
+"normal"===this.plotInfo.axisPlacement&&this.overlaidCanvasCtx.fillRect(this.dragStartPoint.x,c.y1,a-this.dragStartPoint.x,c.y2-c.y1),this.overlaidCanvasCtx.globalAlpha=d)}else this._toolTip.enabled&&this._toolTip.mouseMoveHandler(a,b)};v.prototype.preparePlotArea=function(){var a=this.plotArea,b=this.axisY?this.axisY:this.axisY2;!t&&(0<a.x1||0<a.y1)&&a.ctx.translate(a.x1,a.y1);this.axisX&&b?(a.x1=this.axisX.lineCoordinates.x1<this.axisX.lineCoordinates.x2?this.axisX.lineCoordinates.x1:b.lineCoordinates.x1,
+a.y1=this.axisX.lineCoordinates.y1<b.lineCoordinates.y1?this.axisX.lineCoordinates.y1:b.lineCoordinates.y1,a.x2=this.axisX.lineCoordinates.x2>b.lineCoordinates.x2?this.axisX.lineCoordinates.x2:b.lineCoordinates.x2,a.y2=this.axisX.lineCoordinates.y2>this.axisX.lineCoordinates.y1?this.axisX.lineCoordinates.y2:b.lineCoordinates.y2,a.width=a.x2-a.x1,a.height=a.y2-a.y1):(b=this.layoutManager.getFreeSpace(),a.x1=b.x1,a.x2=b.x2,a.y1=b.y1,a.y2=b.y2,a.width=b.width,a.height=b.height);t||(a.canvas.width=a.width,
+a.canvas.height=a.height,a.canvas.style.left=a.x1+"px",a.canvas.style.top=a.y1+"px",(0<a.x1||0<a.y1)&&a.ctx.translate(-a.x1,-a.y1))};v.prototype.getPixelCoordinatesOnPlotArea=function(a,b){return{x:this.axisX.getPixelCoordinatesOnAxis(a).x,y:this.axisY.getPixelCoordinatesOnAxis(b).y}};v.prototype.renderIndexLabels=function(a){a=a||this.plotArea.ctx;a.textBaseline="middle";for(var b=this.plotArea,c=0;c<this._indexLabels.length;c++){var d=this._indexLabels[c],e,f,g;a.fillStyle=T("indexLabelFontColor",
+d.dataPoint,d.dataSeries);a.font=ua("indexLabel",d.dataPoint,d.dataSeries);var k=this.replaceKeywordsWithValue(T("indexLabel",d.dataPoint,d.dataSeries),d.dataPoint,d.dataSeries,null,d.indexKeyword),p=a.measureText(k).width,h=T("indexLabelFontSize",d.dataPoint,d.dataSeries),l=T("indexLabelPlacement",d.dataPoint,d.dataSeries),r=T("indexLabelOrientation",d.dataPoint,d.dataSeries),m=g=0,q=0,n=0,n=m=q=0,s=d.direction,m=d.dataSeries.axisX,n=d.dataSeries.axisY;d.dataPoint.x<m.minimum||(d.dataPoint.x>m.maximum||
+d.dataPoint.y<n.minimum||d.dataPoint.y>n.maximum)||("column"===d.chartType||"stackedColumn"===d.chartType||"stackedColumn100"===d.chartType||"bar"===d.chartType||"stackedBar"===d.chartType||"stackedBar100"===d.chartType||"candlestick"===d.chartType||"ohlc"===d.chartType||"rangeColumn"===d.chartType||"rangeBar"===d.chartType?(m=n=5,Math.abs(d.bounds.x1,d.bounds.x2),Math.abs(d.bounds.y1,d.bounds.y2),"normal"===this.plotInfo.axisPlacement?("inside"!==l?(m=b.y1,q=b.y2):(m=d.bounds.y1,q=d.bounds.y2),"horizontal"===
+r?(e=d.point.x-p/2,f=0<=s?d.point.y-h/2-n<m+h/2?"auto"===l?Math.min(Math.max(d.point.y,m)+h/2+1,q-h/2-n):Math.min(m+h/2+1,q-h/2-n):Math.min(d.point.y-h/2-n+1,q-h/2-n):d.point.y+h/2+n>q-h/2-1?"auto"===l?Math.max(Math.min(d.point.y,q)-h/2-1,m+h/2+n):Math.max(q-h/2-1,m+h/2+n):Math.max(d.point.y+h/2+n,m+h/2+n)):"vertical"===r&&(e=d.point.x,f=0<=s?d.point.y-n<m+p+1?"auto"===l?Math.min(Math.max(d.point.y,m)+p+1,q):Math.min(m+p+1,q):Math.min(d.point.y-n,q-1):d.point.y+p+n>q-1?"auto"===l?Math.max(Math.min(d.point.y,
+q)-n,m):Math.max(q-1,m):Math.max(d.point.y+p+n,m),g=-90)):"xySwapped"===this.plotInfo.axisPlacement&&("inside"!==l?(n=b.x1,q=b.x2):(n=d.bounds.x1,q=d.bounds.x2),"horizontal"===r?(f=d.point.y,e=0<=s?d.point.x+m>q-p?"auto"===l?Math.max(Math.min(d.point.x,q)-p,n):Math.max(q-p,n):Math.max(d.point.x+m,n):d.point.x-p-m<n?"auto"===l?Math.min(Math.max(d.point.x,n)+1,q):Math.min(n+1,q):Math.min(d.point.x-p-m,q)):"vertical"===r&&(f=d.point.y+p/2,e=0<=s?d.point.x+h/2+m>q-h/2?"auto"===l?Math.max(Math.min(d.point.x,
+q)-h/2,n):Math.max(q-h/2,n):Math.max(d.point.x+h/2+m,n):d.point.x-h/2-m<n+h/2?"auto"===l?Math.min(Math.max(d.point.x,n)+h/2,q+h/2):Math.min(n+h/2,q+h/2):Math.min(d.point.x-h/2-m,q+h/2),g=-90))):(n=5,"horizontal"===r?(e=d.point.x-p/2,"bubble"===d.chartType&&(n=-h/2),f=0<s?Math.max(d.point.y-h/2-n,b.y1+h/2):Math.min(d.point.y+h/2+n,b.y2-h/2)):"vertical"===r&&(e=d.point.x,"bubble"===d.chartType&&(n=-p/2),f=0<s?Math.max(d.point.y-n,b.y1+p):Math.min(d.point.y+p+n,b.y2),g=-90)),a.save(),a.translate(e,f),
+a.rotate(Math.PI/180*g),a.fillText(k,0,0),a.restore())}return{source:a,dest:this.plotArea.ctx,animationCallback:y.fadeInAnimation,easingFunction:y.easing.easeInQuad,animationBase:0,startTimePercent:0.7}};v.prototype.renderLine=function(a){var b=a.targetCanvasCtx||this.plotArea.ctx;if(!(0>=a.dataSeriesIndexes.length)){var c=this._eventManager.ghostCtx;b.save();var d=this.plotArea;b.beginPath();b.rect(d.x1,d.y1,d.width,d.height);b.clip();for(var d=[],e=0;e<a.dataSeriesIndexes.length;e++){var f=a.dataSeriesIndexes[e],
+g=this.data[f];b.lineWidth=g.lineThickness;var k=g.dataPoints,p=g.id;this._eventManager.objectMap[p]={objectType:"dataSeries",dataSeriesIndex:f};p=C(p);c.strokeStyle=p;c.lineWidth=0<g.lineThickness?Math.max(g.lineThickness,4):0;p=g._colorSet[0];b.strokeStyle=p;var h=!0,l=0,r,m;b.beginPath();if(0<k.length){for(var q=!1,l=0;l<k.length;l++)if(r=k[l].x.getTime?k[l].x.getTime():k[l].x,!(r<a.axisX.dataInfo.viewPortMin||r>a.axisX.dataInfo.viewPortMax))if("number"!==typeof k[l].y)0<l&&(b.stroke(),t&&c.stroke()),
+q=!0;else{r=a.axisX.conversionParameters.reference+a.axisX.conversionParameters.pixelPerUnit*(r-a.axisX.conversionParameters.minimum)+0.5<<0;m=a.axisY.conversionParameters.reference+a.axisY.conversionParameters.pixelPerUnit*(k[l].y-a.axisY.conversionParameters.minimum)+0.5<<0;var n=g.dataPointIds[l];this._eventManager.objectMap[n]={id:n,objectType:"dataPoint",dataSeriesIndex:f,dataPointIndex:l,x1:r,y1:m};h||q?(b.beginPath(),b.moveTo(r,m),t&&(c.beginPath(),c.moveTo(r,m)),q=h=!1):(b.lineTo(r,m),t&&
+c.lineTo(r,m),0==l%500&&(b.stroke(),b.beginPath(),b.moveTo(r,m),t&&(c.stroke(),c.beginPath(),c.moveTo(r,m))));if(0<k[l].markerSize||0<g.markerSize){var s=g.getMarkerProperties(l,r,m,b);d.push(s);n=C(n);t&&d.push({x:r,y:m,ctx:c,type:s.type,size:s.size,color:n,borderColor:n,borderThickness:s.borderThickness})}(k[l].indexLabel||g.indexLabel)&&this._indexLabels.push({chartType:"line",dataPoint:k[l],dataSeries:g,point:{x:r,y:m},direction:0<=k[l].y?1:-1,color:p})}b.stroke();t&&c.stroke()}}J.drawMarkers(d);
+b.restore();b.beginPath();t&&c.beginPath();return{source:b,dest:this.plotArea.ctx,animationCallback:y.xClipAnimation,easingFunction:y.easing.linear,animationBase:0}}};v.prototype.renderStepLine=function(a){var b=a.targetCanvasCtx||this.plotArea.ctx;if(!(0>=a.dataSeriesIndexes.length)){var c=this._eventManager.ghostCtx;b.save();var d=this.plotArea;b.beginPath();b.rect(d.x1,d.y1,d.width,d.height);b.clip();for(var d=[],e=0;e<a.dataSeriesIndexes.length;e++){var f=a.dataSeriesIndexes[e],g=this.data[f];
+b.lineWidth=g.lineThickness;var k=g.dataPoints,p=g.id;this._eventManager.objectMap[p]={objectType:"dataSeries",dataSeriesIndex:f};p=C(p);c.strokeStyle=p;c.lineWidth=0<g.lineThickness?Math.max(g.lineThickness,4):0;p=g._colorSet[0];b.strokeStyle=p;var h=!0,l=0,r,m;b.beginPath();if(0<k.length){for(var q=!1,l=0;l<k.length;l++)if(r=k[l].getTime?k[l].x.getTime():k[l].x,!(r<a.axisX.dataInfo.viewPortMin||r>a.axisX.dataInfo.viewPortMax))if("number"!==typeof k[l].y)0<l&&(b.stroke(),t&&c.stroke()),q=!0;else{var n=
+m;r=a.axisX.conversionParameters.reference+a.axisX.conversionParameters.pixelPerUnit*(r-a.axisX.conversionParameters.minimum)+0.5<<0;m=a.axisY.conversionParameters.reference+a.axisY.conversionParameters.pixelPerUnit*(k[l].y-a.axisY.conversionParameters.minimum)+0.5<<0;var s=g.dataPointIds[l];this._eventManager.objectMap[s]={id:s,objectType:"dataPoint",dataSeriesIndex:f,dataPointIndex:l,x1:r,y1:m};h||q?(b.beginPath(),b.moveTo(r,m),t&&(c.beginPath(),c.moveTo(r,m)),q=h=!1):(b.lineTo(r,n),t&&c.lineTo(r,
+n),b.lineTo(r,m),t&&c.lineTo(r,m),0==l%500&&(b.stroke(),b.beginPath(),b.moveTo(r,m),t&&(c.stroke(),c.beginPath(),c.moveTo(r,m))));if(0<k[l].markerSize||0<g.markerSize)n=g.getMarkerProperties(l,r,m,b),d.push(n),s=C(s),t&&d.push({x:r,y:m,ctx:c,type:n.type,size:n.size,color:s,borderColor:s,borderThickness:n.borderThickness});(k[l].indexLabel||g.indexLabel)&&this._indexLabels.push({chartType:"stepLine",dataPoint:k[l],dataSeries:g,point:{x:r,y:m},direction:0<=k[l].y?1:-1,color:p})}b.stroke();t&&c.stroke()}}J.drawMarkers(d);
+b.restore();b.beginPath();t&&c.beginPath();return{source:b,dest:this.plotArea.ctx,animationCallback:y.xClipAnimation,easingFunction:y.easing.linear,animationBase:0}}};v.prototype.renderSpline=function(a){function b(a){a=fa(a,2);if(0<a.length){c.beginPath();t&&d.beginPath();c.moveTo(a[0].x,a[0].y);t&&d.moveTo(a[0].x,a[0].y);for(var b=0;b<a.length-3;b+=3)c.bezierCurveTo(a[b+1].x,a[b+1].y,a[b+2].x,a[b+2].y,a[b+3].x,a[b+3].y),t&&d.bezierCurveTo(a[b+1].x,a[b+1].y,a[b+2].x,a[b+2].y,a[b+3].x,a[b+3].y),0<
+b&&0===b%3E3&&(c.stroke(),c.beginPath(),c.moveTo(a[b+3].x,a[b+3].y),t&&(d.stroke(),d.beginPath(),d.moveTo(a[b+3].x,a[b+3].y)));c.stroke();t&&d.stroke()}}var c=a.targetCanvasCtx||this.plotArea.ctx;if(!(0>=a.dataSeriesIndexes.length)){var d=this._eventManager.ghostCtx;c.save();var e=this.plotArea;c.beginPath();c.rect(e.x1,e.y1,e.width,e.height);c.clip();for(var e=[],f=0;f<a.dataSeriesIndexes.length;f++){var g=a.dataSeriesIndexes[f],k=this.data[g];c.lineWidth=k.lineThickness;var p=k.dataPoints,h=k.id;
+this._eventManager.objectMap[h]={objectType:"dataSeries",dataSeriesIndex:g};h=C(h);d.strokeStyle=h;d.lineWidth=0<k.lineThickness?Math.max(k.lineThickness,4):0;h=k._colorSet[0];c.strokeStyle=h;var l=0,r,m,q=[];c.beginPath();if(0<p.length)for(l=0;l<p.length;l++)if(r=p[l].getTime?p[l].x.getTime():p[l].x,!(r<a.axisX.dataInfo.viewPortMin||r>a.axisX.dataInfo.viewPortMax))if("number"!==typeof p[l].y)0<l&&(b(q),q=[]);else{r=a.axisX.conversionParameters.reference+a.axisX.conversionParameters.pixelPerUnit*
+(r-a.axisX.conversionParameters.minimum)+0.5<<0;m=a.axisY.conversionParameters.reference+a.axisY.conversionParameters.pixelPerUnit*(p[l].y-a.axisY.conversionParameters.minimum)+0.5<<0;var n=k.dataPointIds[l];this._eventManager.objectMap[n]={id:n,objectType:"dataPoint",dataSeriesIndex:g,dataPointIndex:l,x1:r,y1:m};q[q.length]={x:r,y:m};if(0<p[l].markerSize||0<k.markerSize){var s=k.getMarkerProperties(l,r,m,c);e.push(s);n=C(n);t&&e.push({x:r,y:m,ctx:d,type:s.type,size:s.size,color:n,borderColor:n,borderThickness:s.borderThickness})}(p[l].indexLabel||
+k.indexLabel)&&this._indexLabels.push({chartType:"spline",dataPoint:p[l],dataSeries:k,point:{x:r,y:m},direction:0<=p[l].y?1:-1,color:h})}b(q)}J.drawMarkers(e);c.restore();c.beginPath();t&&d.beginPath();return{source:c,dest:this.plotArea.ctx,animationCallback:y.xClipAnimation,easingFunction:y.easing.linear,animationBase:0}}};var F=function(a,b,c,d,e,f,g,k,p,h,l,r,m){"undefined"===typeof m&&(m=1);g=g||0;k=k||"black";var q=15<d-b&&15<e-c?8:0.35*Math.min(d-b,e-c);a.beginPath();a.moveTo(b,c);a.save();
+a.fillStyle=f;a.globalAlpha=m;a.fillRect(b,c,d-b,e-c);a.globalAlpha=1;0<g&&(m=0===g%2?0:0.5,a.beginPath(),a.lineWidth=g,a.strokeStyle=k,a.moveTo(b,c),a.rect(b-m,c-m,d-b+2*m,e-c+2*m),a.stroke());a.restore();!0===p&&(a.save(),a.beginPath(),a.moveTo(b,c),a.lineTo(b+q,c+q),a.lineTo(d-q,c+q),a.lineTo(d,c),a.closePath(),g=a.createLinearGradient((d+b)/2,c+q,(d+b)/2,c),g.addColorStop(0,f),g.addColorStop(1,"rgba(255, 255, 255, .4)"),a.fillStyle=g,a.fill(),a.restore());!0===h&&(a.save(),a.beginPath(),a.moveTo(b,
+e),a.lineTo(b+q,e-q),a.lineTo(d-q,e-q),a.lineTo(d,e),a.closePath(),g=a.createLinearGradient((d+b)/2,e-q,(d+b)/2,e),g.addColorStop(0,f),g.addColorStop(1,"rgba(255, 255, 255, .4)"),a.fillStyle=g,a.fill(),a.restore());!0===l&&(a.save(),a.beginPath(),a.moveTo(b,c),a.lineTo(b+q,c+q),a.lineTo(b+q,e-q),a.lineTo(b,e),a.closePath(),g=a.createLinearGradient(b+q,(e+c)/2,b,(e+c)/2),g.addColorStop(0,f),g.addColorStop(1,"rgba(255, 255, 255, 0.1)"),a.fillStyle=g,a.fill(),a.restore());!0===r&&(a.save(),a.beginPath(),
+a.moveTo(d,c),a.lineTo(d-q,c+q),a.lineTo(d-q,e-q),a.lineTo(d,e),g=a.createLinearGradient(d-q,(e+c)/2,d,(e+c)/2),g.addColorStop(0,f),g.addColorStop(1,"rgba(255, 255, 255, 0.1)"),a.fillStyle=g,g.addColorStop(0,f),g.addColorStop(1,"rgba(255, 255, 255, 0.1)"),a.fillStyle=g,a.fill(),a.closePath(),a.restore())};v.prototype.renderColumn=function(a){var b=a.targetCanvasCtx||this.plotArea.ctx;if(!(0>=a.dataSeriesIndexes.length)){var c=null,d=this.plotArea,e=0,f,g,k,p=a.axisY.conversionParameters.reference+
+a.axisY.conversionParameters.pixelPerUnit*(0-a.axisY.conversionParameters.minimum)<<0,e=Math.min(0.15*this.width,0.9*(this.plotArea.width/a.plotType.totalDataSeries))<<0,h=a.axisX.dataInfo.minDiff,l=0.9*(d.width/Math.abs(a.axisX.maximum-a.axisX.minimum)*Math.abs(h)/a.plotType.totalDataSeries)<<0;l>e?l=e:Infinity===h?l=e:1>l&&(l=1);b.save();t&&this._eventManager.ghostCtx.save();b.beginPath();b.rect(d.x1,d.y1,d.width,d.height);b.clip();t&&(this._eventManager.ghostCtx.rect(d.x1,d.y1,d.width,d.height),
+this._eventManager.ghostCtx.clip());for(d=0;d<a.dataSeriesIndexes.length;d++){var h=a.dataSeriesIndexes[d],r=this.data[h],m=r.dataPoints;if(0<m.length)for(var q=5<l&&r.bevelEnabled?!0:!1,e=0;e<m.length;e++)if(m[e].getTime?k=m[e].x.getTime():k=m[e].x,!(k<a.axisX.dataInfo.viewPortMin||k>a.axisX.dataInfo.viewPortMax)&&"number"===typeof m[e].y){f=a.axisX.conversionParameters.reference+a.axisX.conversionParameters.pixelPerUnit*(k-a.axisX.conversionParameters.minimum)+0.5<<0;g=a.axisY.conversionParameters.reference+
+a.axisY.conversionParameters.pixelPerUnit*(m[e].y-a.axisY.conversionParameters.minimum)+0.5<<0;f=f-a.plotType.totalDataSeries*l/2+(a.previousDataSeriesCount+d)*l<<0;var n=f+l<<0,s;0<=m[e].y?s=p:(s=g,g=p);g>s&&(s=g=s);c=m[e].color?m[e].color:r._colorSet[e%r._colorSet.length];F(b,f,g,n,s,c,0,null,q&&0<=m[e].y,0>m[e].y&&q,!1,!1,r.fillOpacity);c=r.dataPointIds[e];this._eventManager.objectMap[c]={id:c,objectType:"dataPoint",dataSeriesIndex:h,dataPointIndex:e,x1:f,y1:g,x2:n,y2:s};c=C(c);t&&F(this._eventManager.ghostCtx,
+f,g,n,s,c,0,null,!1,!1,!1,!1);(m[e].indexLabel||r.indexLabel)&&this._indexLabels.push({chartType:"column",dataPoint:m[e],dataSeries:r,point:{x:f+(n-f)/2,y:0<=m[e].y?g:s},direction:0<=m[e].y?1:-1,bounds:{x1:f,y1:Math.min(g,s),x2:n,y2:Math.max(g,s)},color:c})}}b.restore();t&&this._eventManager.ghostCtx.restore();a=Math.min(p,a.axisY.boundingRect.y2);return{source:b,dest:this.plotArea.ctx,animationCallback:y.yScaleAnimation,easingFunction:y.easing.easeOutQuart,animationBase:a}}};v.prototype.renderStackedColumn=
+function(a){var b=a.targetCanvasCtx||this.plotArea.ctx;if(!(0>=a.dataSeriesIndexes.length)){var c=null,d=this.plotArea,e=[],f=[],g=0,k,p=a.axisY.conversionParameters.reference+a.axisY.conversionParameters.pixelPerUnit*(0-a.axisY.conversionParameters.minimum)<<0,g=0.15*this.width<<0,h=a.axisX.dataInfo.minDiff,l=0.9*(d.width/Math.abs(a.axisX.maximum-a.axisX.minimum)*Math.abs(h)/a.plotType.plotUnits.length)<<0;l>g?l=g:Infinity===h?l=g:1>l&&(l=1);b.save();t&&this._eventManager.ghostCtx.save();b.beginPath();
+b.rect(d.x1,d.y1,d.width,d.height);b.clip();t&&(this._eventManager.ghostCtx.rect(d.x1,d.y1,d.width,d.height),this._eventManager.ghostCtx.clip());for(h=0;h<a.dataSeriesIndexes.length;h++){var r=a.dataSeriesIndexes[h],m=this.data[r],q=m.dataPoints;if(0<q.length){var n=5<l&&m.bevelEnabled?!0:!1;b.strokeStyle="#4572A7 ";for(g=0;g<q.length;g++)if(c=q[g].x.getTime?q[g].x.getTime():q[g].x,!(c<a.axisX.dataInfo.viewPortMin||c>a.axisX.dataInfo.viewPortMax)&&"number"===typeof q[g].y){d=a.axisX.conversionParameters.reference+
+a.axisX.conversionParameters.pixelPerUnit*(c-a.axisX.conversionParameters.minimum)+0.5<<0;k=a.axisY.conversionParameters.reference+a.axisY.conversionParameters.pixelPerUnit*(q[g].y-a.axisY.conversionParameters.minimum);var s=d-a.plotType.plotUnits.length*l/2+a.index*l<<0,B=s+l<<0,u;if(0<=q[g].y){var x=e[c]?e[c]:0;k-=x;u=p-x;e[c]=x+(u-k)}else x=f[c]?f[c]:0,u=k+x,k=p+x,f[c]=x+(u-k);c=q[g].color?q[g].color:m._colorSet[g%m._colorSet.length];F(b,s,k,B,u,c,0,null,n&&0<=q[g].y,0>q[g].y&&n,!1,!1,m.fillOpacity);
+c=m.dataPointIds[g];this._eventManager.objectMap[c]={id:c,objectType:"dataPoint",dataSeriesIndex:r,dataPointIndex:g,x1:s,y1:k,x2:B,y2:u};c=C(c);t&&F(this._eventManager.ghostCtx,s,k,B,u,c,0,null,!1,!1,!1,!1);(q[g].indexLabel||m.indexLabel)&&this._indexLabels.push({chartType:"stackedColumn",dataPoint:q[g],dataSeries:m,point:{x:d,y:0<=q[g].y?k:u},direction:0<=q[g].y?1:-1,bounds:{x1:s,y1:Math.min(k,u),x2:B,y2:Math.max(k,u)},color:c})}}}b.restore();t&&this._eventManager.ghostCtx.restore();a=Math.min(p,
+a.axisY.boundingRect.y2);return{source:b,dest:this.plotArea.ctx,animationCallback:y.yScaleAnimation,easingFunction:y.easing.easeOutQuart,animationBase:a}}};v.prototype.renderStackedColumn100=function(a){var b=a.targetCanvasCtx||this.plotArea.ctx;if(!(0>=a.dataSeriesIndexes.length)){var c=null,d=this.plotArea,e=[],f=[],g=0,k,p=a.axisY.conversionParameters.reference+a.axisY.conversionParameters.pixelPerUnit*(0-a.axisY.conversionParameters.minimum)<<0,g=0.15*this.width<<0,h=a.axisX.dataInfo.minDiff,
+l=0.9*(d.width/Math.abs(a.axisX.maximum-a.axisX.minimum)*Math.abs(h)/a.plotType.plotUnits.length)<<0;l>g?l=g:Infinity===h?l=g:1>l&&(l=1);b.save();t&&this._eventManager.ghostCtx.save();b.beginPath();b.rect(d.x1,d.y1,d.width,d.height);b.clip();t&&(this._eventManager.ghostCtx.rect(d.x1,d.y1,d.width,d.height),this._eventManager.ghostCtx.clip());for(h=0;h<a.dataSeriesIndexes.length;h++){var r=a.dataSeriesIndexes[h],m=this.data[r],q=m.dataPoints;if(0<q.length)for(var n=5<l&&m.bevelEnabled?!0:!1,g=0;g<q.length;g++)if(c=
+q[g].x.getTime?q[g].x.getTime():q[g].x,!(c<a.axisX.dataInfo.viewPortMin||c>a.axisX.dataInfo.viewPortMax)&&"number"===typeof q[g].y){d=a.axisX.conversionParameters.reference+a.axisX.conversionParameters.pixelPerUnit*(c-a.axisX.conversionParameters.minimum)+0.5<<0;k=a.axisY.conversionParameters.reference+a.axisY.conversionParameters.pixelPerUnit*((0!==a.dataPointYSums[c]?100*(q[g].y/a.dataPointYSums[c]):0)-a.axisY.conversionParameters.minimum);var s=d-a.plotType.plotUnits.length*l/2+a.index*l<<0,B=
+s+l<<0,u;if(0<=q[g].y){var x=e[c]?e[c]:0;k-=x;u=p-x;e[c]=x+(u-k)}else x=f[c]?f[c]:0,u=k+x,k=p+x,f[c]=x+(u-k);c=q[g].color?q[g].color:m._colorSet[g%m._colorSet.length];F(b,s,k,B,u,c,0,null,n&&0<=q[g].y,0>q[g].y&&n,!1,!1,m.fillOpacity);c=m.dataPointIds[g];this._eventManager.objectMap[c]={id:c,objectType:"dataPoint",dataSeriesIndex:r,dataPointIndex:g,x1:s,y1:k,x2:B,y2:u};c=C(c);t&&F(this._eventManager.ghostCtx,s,k,B,u,c,0,null,!1,!1,!1,!1);(q[g].indexLabel||m.indexLabel)&&this._indexLabels.push({chartType:"stackedColumn100",
+dataPoint:q[g],dataSeries:m,point:{x:d,y:0<=q[g].y?k:u},direction:0<=q[g].y?1:-1,bounds:{x1:s,y1:Math.min(k,u),x2:B,y2:Math.max(k,u)},color:c})}}b.restore();t&&this._eventManager.ghostCtx.restore();a=Math.min(p,a.axisY.boundingRect.y2);return{source:b,dest:this.plotArea.ctx,animationCallback:y.yScaleAnimation,easingFunction:y.easing.easeOutQuart,animationBase:a}}};v.prototype.renderBar=function(a){var b=a.targetCanvasCtx||this.plotArea.ctx;if(!(0>=a.dataSeriesIndexes.length)){var c=null,d=this.plotArea,
+e=0,f,g,k,p=a.axisY.conversionParameters.reference+a.axisY.conversionParameters.pixelPerUnit*(0-a.axisY.conversionParameters.minimum)<<0,e=Math.min(0.15*this.height,0.9*(this.plotArea.height/a.plotType.totalDataSeries))<<0,h=a.axisX.dataInfo.minDiff,l=0.9*(d.height/Math.abs(a.axisX.maximum-a.axisX.minimum)*Math.abs(h)/a.plotType.totalDataSeries)<<0;l>e?l=e:Infinity===h?l=e:1>l&&(l=1);b.save();t&&this._eventManager.ghostCtx.save();b.beginPath();b.rect(d.x1,d.y1,d.width,d.height);b.clip();t&&(this._eventManager.ghostCtx.rect(d.x1,
+d.y1,d.width,d.height),this._eventManager.ghostCtx.clip());for(d=0;d<a.dataSeriesIndexes.length;d++){var h=a.dataSeriesIndexes[d],r=this.data[h],m=r.dataPoints;if(0<m.length){var q=5<l&&r.bevelEnabled?!0:!1;b.strokeStyle="#4572A7 ";for(e=0;e<m.length;e++)if(m[e].getTime?k=m[e].x.getTime():k=m[e].x,!(k<a.axisX.dataInfo.viewPortMin||k>a.axisX.dataInfo.viewPortMax)&&"number"===typeof m[e].y){g=a.axisX.conversionParameters.reference+a.axisX.conversionParameters.pixelPerUnit*(k-a.axisX.conversionParameters.minimum)+
+0.5<<0;f=a.axisY.conversionParameters.reference+a.axisY.conversionParameters.pixelPerUnit*(m[e].y-a.axisY.conversionParameters.minimum)+0.5<<0;g=g-a.plotType.totalDataSeries*l/2+(a.previousDataSeriesCount+d)*l<<0;var n=g+l<<0,s;0<=m[e].y?s=p:(s=f,f=p);c=m[e].color?m[e].color:r._colorSet[e%r._colorSet.length];F(b,s,g,f,n,c,0,null,q,!1,!1,!1,r.fillOpacity);c=r.dataPointIds[e];this._eventManager.objectMap[c]={id:c,objectType:"dataPoint",dataSeriesIndex:h,dataPointIndex:e,x1:s,y1:g,x2:f,y2:n};c=C(c);
+t&&F(this._eventManager.ghostCtx,s,g,f,n,c,0,null,!1,!1,!1,!1);this._indexLabels.push({chartType:"bar",dataPoint:m[e],dataSeries:r,point:{x:0<=m[e].y?f:s,y:g+(n-g)/2},direction:0<=m[e].y?1:-1,bounds:{x1:Math.min(s,f),y1:g,x2:Math.max(s,f),y2:n},color:c})}}}b.restore();t&&this._eventManager.ghostCtx.restore();a=Math.max(p,a.axisX.boundingRect.x2);return{source:b,dest:this.plotArea.ctx,animationCallback:y.xScaleAnimation,easingFunction:y.easing.easeOutQuart,animationBase:a}}};v.prototype.renderStackedBar=
+function(a){var b=a.targetCanvasCtx||this.plotArea.ctx;if(!(0>=a.dataSeriesIndexes.length)){var c=null,d=this.plotArea,e=[],f=[],g=0,k,p=a.axisY.conversionParameters.reference+a.axisY.conversionParameters.pixelPerUnit*(0-a.axisY.conversionParameters.minimum)<<0,g=0.15*this.height<<0,h=a.axisX.dataInfo.minDiff,l=0.9*(d.height/Math.abs(a.axisX.maximum-a.axisX.minimum)*Math.abs(h)/a.plotType.plotUnits.length)<<0;l>g?l=g:Infinity===h?l=g:1>l&&(l=1);b.save();t&&this._eventManager.ghostCtx.save();b.beginPath();
+b.rect(d.x1,d.y1,d.width,d.height);b.clip();t&&(this._eventManager.ghostCtx.rect(d.x1,d.y1,d.width,d.height),this._eventManager.ghostCtx.clip());for(h=0;h<a.dataSeriesIndexes.length;h++){var r=a.dataSeriesIndexes[h],m=this.data[r],q=m.dataPoints;if(0<q.length){var n=5<l&&m.bevelEnabled?!0:!1;b.strokeStyle="#4572A7 ";for(g=0;g<q.length;g++)if(c=q[g].x.getTime?q[g].x.getTime():q[g].x,!(c<a.axisX.dataInfo.viewPortMin||c>a.axisX.dataInfo.viewPortMax)&&"number"===typeof q[g].y){d=a.axisX.conversionParameters.reference+
+a.axisX.conversionParameters.pixelPerUnit*(c-a.axisX.conversionParameters.minimum)+0.5<<0;k=a.axisY.conversionParameters.reference+a.axisY.conversionParameters.pixelPerUnit*(q[g].y-a.axisY.conversionParameters.minimum);var s=d-a.plotType.plotUnits.length*l/2+a.index*l<<0,B=s+l<<0,u;if(0<=q[g].y){var x=e[c]?e[c]:0;u=p+x;k+=x;e[c]=x+(k-u)}else x=f[c]?f[c]:0,u=k-x,k=p-x,f[c]=x+(k-u);c=q[g].color?q[g].color:m._colorSet[g%m._colorSet.length];F(b,u,s,k,B,c,0,null,n,!1,!1,!1,m.fillOpacity);c=m.dataPointIds[g];
+this._eventManager.objectMap[c]={id:c,objectType:"dataPoint",dataSeriesIndex:r,dataPointIndex:g,x1:u,y1:s,x2:k,y2:B};c=C(c);t&&F(this._eventManager.ghostCtx,u,s,k,B,c,0,null,!1,!1,!1,!1);this._indexLabels.push({chartType:"stackedBar",dataPoint:q[g],dataSeries:m,point:{x:0<=q[g].y?k:u,y:d},direction:0<=q[g].y?1:-1,bounds:{x1:Math.min(u,k),y1:s,x2:Math.max(u,k),y2:B},color:c})}}}b.restore();t&&this._eventManager.ghostCtx.restore();a=Math.max(p,a.axisX.boundingRect.x2);return{source:b,dest:this.plotArea.ctx,
+animationCallback:y.xScaleAnimation,easingFunction:y.easing.easeOutQuart,animationBase:a}}};v.prototype.renderStackedBar100=function(a){var b=a.targetCanvasCtx||this.plotArea.ctx;if(!(0>=a.dataSeriesIndexes.length)){var c=null,d=this.plotArea,e=[],f=[],g=0,k,p=a.axisY.conversionParameters.reference+a.axisY.conversionParameters.pixelPerUnit*(0-a.axisY.conversionParameters.minimum)<<0,g=0.15*this.height<<0,h=a.axisX.dataInfo.minDiff,l=0.9*(d.height/Math.abs(a.axisX.maximum-a.axisX.minimum)*Math.abs(h)/
+a.plotType.plotUnits.length)<<0;l>g?l=g:Infinity===h?l=g:1>l&&(l=1);b.save();t&&this._eventManager.ghostCtx.save();b.beginPath();b.rect(d.x1,d.y1,d.width,d.height);b.clip();t&&(this._eventManager.ghostCtx.rect(d.x1,d.y1,d.width,d.height),this._eventManager.ghostCtx.clip());for(h=0;h<a.dataSeriesIndexes.length;h++){var r=a.dataSeriesIndexes[h],m=this.data[r],q=m.dataPoints;if(0<q.length){var n=5<l&&m.bevelEnabled?!0:!1;b.strokeStyle="#4572A7 ";for(g=0;g<q.length;g++)if(c=q[g].x.getTime?q[g].x.getTime():
+q[g].x,!(c<a.axisX.dataInfo.viewPortMin||c>a.axisX.dataInfo.viewPortMax)&&"number"===typeof q[g].y){d=a.axisX.conversionParameters.reference+a.axisX.conversionParameters.pixelPerUnit*(c-a.axisX.conversionParameters.minimum)+0.5<<0;k=a.axisY.conversionParameters.reference+a.axisY.conversionParameters.pixelPerUnit*((0!==a.dataPointYSums[c]?100*(q[g].y/a.dataPointYSums[c]):0)-a.axisY.conversionParameters.minimum);var s=d-a.plotType.plotUnits.length*l/2+a.index*l<<0,B=s+l<<0,u;if(0<=q[g].y){var x=e[c]?
+e[c]:0;u=p+x;k+=x;e[c]=x+(k-u)}else x=f[c]?f[c]:0,u=k-x,k=p-x,f[c]=x+(k-u);c=q[g].color?q[g].color:m._colorSet[g%m._colorSet.length];F(b,u,s,k,B,c,0,null,n,!1,!1,!1,m.fillOpacity);c=m.dataPointIds[g];this._eventManager.objectMap[c]={id:c,objectType:"dataPoint",dataSeriesIndex:r,dataPointIndex:g,x1:u,y1:s,x2:k,y2:B};c=C(c);t&&F(this._eventManager.ghostCtx,u,s,k,B,c,0,null,!1,!1,!1,!1);this._indexLabels.push({chartType:"stackedBar100",dataPoint:q[g],dataSeries:m,point:{x:0<=q[g].y?k:u,y:d},direction:0<=
+q[g].y?1:-1,bounds:{x1:Math.min(u,k),y1:s,x2:Math.max(u,k),y2:B},color:c})}}}b.restore();t&&this._eventManager.ghostCtx.restore();a=Math.max(p,a.axisX.boundingRect.x2);return{source:b,dest:this.plotArea.ctx,animationCallback:y.xScaleAnimation,easingFunction:y.easing.easeOutQuart,animationBase:a}}};v.prototype.renderArea=function(a){function b(){x&&(0<h.lineThickness&&c.stroke(),0>=a.axisY.minimum&&0<=a.axisY.maximum?u=B:0>a.axisY.maximum?u=f.y1:0<a.axisY.minimum&&(u=e.y2),c.lineTo(q,u),c.lineTo(x.x,
+u),c.closePath(),c.globalAlpha=h.fillOpacity,c.fill(),c.globalAlpha=1,t&&(d.lineTo(q,u),d.lineTo(x.x,u),d.closePath(),d.fill()),c.beginPath(),c.moveTo(q,n),d.beginPath(),d.moveTo(q,n),x={x:q,y:n})}var c=a.targetCanvasCtx||this.plotArea.ctx;if(!(0>=a.dataSeriesIndexes.length)){var d=this._eventManager.ghostCtx,e=a.axisX.lineCoordinates,f=a.axisY.lineCoordinates,g=[],k=this.plotArea;c.save();t&&d.save();c.beginPath();c.rect(k.x1,k.y1,k.width,k.height);c.clip();t&&(d.beginPath(),d.rect(k.x1,k.y1,k.width,
+k.height),d.clip());for(k=0;k<a.dataSeriesIndexes.length;k++){var p=a.dataSeriesIndexes[k],h=this.data[p],l=h.dataPoints,g=h.id;this._eventManager.objectMap[g]={objectType:"dataSeries",dataSeriesIndex:p};g=C(g);d.fillStyle=g;var g=[],r=!0,m=0,q,n,s,B=a.axisY.conversionParameters.reference+a.axisY.conversionParameters.pixelPerUnit*(0-a.axisY.conversionParameters.minimum)+0.5<<0,u,x=null;if(0<l.length){var w=h._colorSet[m%h._colorSet.length];c.fillStyle=w;c.strokeStyle=w;c.lineWidth=h.lineThickness;
+for(var Q=!0;m<l.length;m++)if(s=l[m].x.getTime?l[m].x.getTime():l[m].x,!(s<a.axisX.dataInfo.viewPortMin||s>a.axisX.dataInfo.viewPortMax))if("number"!==typeof l[m].y)b(),Q=!0;else{q=a.axisX.conversionParameters.reference+a.axisX.conversionParameters.pixelPerUnit*(s-a.axisX.conversionParameters.minimum)+0.5<<0;n=a.axisY.conversionParameters.reference+a.axisY.conversionParameters.pixelPerUnit*(l[m].y-a.axisY.conversionParameters.minimum)+0.5<<0;r||Q?(c.beginPath(),c.moveTo(q,n),x={x:q,y:n},t&&(d.beginPath(),
+d.moveTo(q,n)),Q=r=!1):(c.lineTo(q,n),t&&d.lineTo(q,n),0==m%250&&b());var G=h.dataPointIds[m];this._eventManager.objectMap[G]={id:G,objectType:"dataPoint",dataSeriesIndex:p,dataPointIndex:m,x1:q,y1:n};0!==l[m].markerSize&&(0<l[m].markerSize||0<h.markerSize)&&(s=h.getMarkerProperties(m,q,n,c),g.push(s),G=C(G),t&&g.push({x:q,y:n,ctx:d,type:s.type,size:s.size,color:G,borderColor:G,borderThickness:s.borderThickness}));(l[m].indexLabel||h.indexLabel)&&this._indexLabels.push({chartType:"area",dataPoint:l[m],
+dataSeries:h,point:{x:q,y:n},direction:0<=l[m].y?1:-1,color:w})}b();J.drawMarkers(g)}}c.restore();t&&this._eventManager.ghostCtx.restore();return{source:c,dest:this.plotArea.ctx,animationCallback:y.xClipAnimation,easingFunction:y.easing.linear,animationBase:0}}};v.prototype.renderSplineArea=function(a){function b(){var b=fa(u,2);if(0<b.length){c.beginPath();c.moveTo(b[0].x,b[0].y);t&&(d.beginPath(),d.moveTo(b[0].x,b[0].y));for(var g=0;g<b.length-3;g+=3)c.bezierCurveTo(b[g+1].x,b[g+1].y,b[g+2].x,b[g+
+2].y,b[g+3].x,b[g+3].y),t&&d.bezierCurveTo(b[g+1].x,b[g+1].y,b[g+2].x,b[g+2].y,b[g+3].x,b[g+3].y);0<h.lineThickness&&c.stroke();0>=a.axisY.minimum&&0<=a.axisY.maximum?s=n:0>a.axisY.maximum?s=f.y1:0<a.axisY.minimum&&(s=e.y2);B={x:b[0].x,y:b[0].y};c.lineTo(b[b.length-1].x,s);c.lineTo(B.x,s);c.closePath();c.globalAlpha=h.fillOpacity;c.fill();c.globalAlpha=1;t&&(d.lineTo(b[b.length-1].x,s),d.lineTo(B.x,s),d.closePath(),d.fill())}}var c=a.targetCanvasCtx||this.plotArea.ctx;if(!(0>=a.dataSeriesIndexes.length)){var d=
+this._eventManager.ghostCtx,e=a.axisX.lineCoordinates,f=a.axisY.lineCoordinates,g=[],k=this.plotArea;c.save();t&&d.save();c.beginPath();c.rect(k.x1,k.y1,k.width,k.height);c.clip();t&&(d.beginPath(),d.rect(k.x1,k.y1,k.width,k.height),d.clip());for(k=0;k<a.dataSeriesIndexes.length;k++){var p=a.dataSeriesIndexes[k],h=this.data[p],l=h.dataPoints,g=h.id;this._eventManager.objectMap[g]={objectType:"dataSeries",dataSeriesIndex:p};g=C(g);d.fillStyle=g;var g=[],r=0,m,q,n=a.axisY.conversionParameters.reference+
+a.axisY.conversionParameters.pixelPerUnit*(0-a.axisY.conversionParameters.minimum)+0.5<<0,s,B=null,u=[];if(0<l.length){color=h._colorSet[r%h._colorSet.length];c.fillStyle=color;c.strokeStyle=color;for(c.lineWidth=h.lineThickness;r<l.length;r++)if(m=l[r].x.getTime?l[r].x.getTime():l[r].x,!(m<a.axisX.dataInfo.viewPortMin||m>a.axisX.dataInfo.viewPortMax))if("number"!==typeof l[r].y)0<r&&(b(),u=[]);else{m=a.axisX.conversionParameters.reference+a.axisX.conversionParameters.pixelPerUnit*(m-a.axisX.conversionParameters.minimum)+
+0.5<<0;q=a.axisY.conversionParameters.reference+a.axisY.conversionParameters.pixelPerUnit*(l[r].y-a.axisY.conversionParameters.minimum)+0.5<<0;var x=h.dataPointIds[r];this._eventManager.objectMap[x]={id:x,objectType:"dataPoint",dataSeriesIndex:p,dataPointIndex:r,x1:m,y1:q};u[u.length]={x:m,y:q};if(0!==l[r].markerSize&&(0<l[r].markerSize||0<h.markerSize)){var w=h.getMarkerProperties(r,m,q,c);g.push(w);x=C(x);t&&g.push({x:m,y:q,ctx:d,type:w.type,size:w.size,color:x,borderColor:x,borderThickness:w.borderThickness})}(l[r].indexLabel||
+h.indexLabel)&&this._indexLabels.push({chartType:"splineArea",dataPoint:l[r],dataSeries:h,point:{x:m,y:q},direction:0<=l[r].y?1:-1,color:color})}b();J.drawMarkers(g)}}c.restore();t&&this._eventManager.ghostCtx.restore();return{source:c,dest:this.plotArea.ctx,animationCallback:y.xClipAnimation,easingFunction:y.easing.linear,animationBase:0}}};v.prototype.renderStepArea=function(a){function b(){x&&(0<h.lineThickness&&c.stroke(),0>=a.axisY.minimum&&0<=a.axisY.maximum?u=B:0>a.axisY.maximum?u=f.y1:0<a.axisY.minimum&&
+(u=e.y2),c.lineTo(q,u),c.lineTo(x.x,u),c.closePath(),c.globalAlpha=h.fillOpacity,c.fill(),c.globalAlpha=1,t&&(d.lineTo(q,u),d.lineTo(x.x,u),d.closePath(),d.fill()),c.beginPath(),c.moveTo(q,n),d.beginPath(),d.moveTo(q,n),x={x:q,y:n})}var c=a.targetCanvasCtx||this.plotArea.ctx;if(!(0>=a.dataSeriesIndexes.length)){var d=this._eventManager.ghostCtx,e=a.axisX.lineCoordinates,f=a.axisY.lineCoordinates,g=[],k=this.plotArea;c.save();t&&d.save();c.beginPath();c.rect(k.x1,k.y1,k.width,k.height);c.clip();t&&
+(d.beginPath(),d.rect(k.x1,k.y1,k.width,k.height),d.clip());for(k=0;k<a.dataSeriesIndexes.length;k++){var p=a.dataSeriesIndexes[k],h=this.data[p],l=h.dataPoints,g=h.id;this._eventManager.objectMap[g]={objectType:"dataSeries",dataSeriesIndex:p};g=C(g);d.fillStyle=g;var g=[],r=!0,m=0,q,n,s,B=a.axisY.conversionParameters.reference+a.axisY.conversionParameters.pixelPerUnit*(0-a.axisY.conversionParameters.minimum)+0.5<<0,u,x=null,w=!1;if(0<l.length){var Q=h._colorSet[m%h._colorSet.length];c.fillStyle=
+Q;c.strokeStyle=Q;for(c.lineWidth=h.lineThickness;m<l.length;m++)if(s=l[m].x.getTime?l[m].x.getTime():l[m].x,!(s<a.axisX.dataInfo.viewPortMin||s>a.axisX.dataInfo.viewPortMax)){var v=n;"number"!==typeof l[m].y?(b(),w=!0):(q=a.axisX.conversionParameters.reference+a.axisX.conversionParameters.pixelPerUnit*(s-a.axisX.conversionParameters.minimum)+0.5<<0,n=a.axisY.conversionParameters.reference+a.axisY.conversionParameters.pixelPerUnit*(l[m].y-a.axisY.conversionParameters.minimum)+0.5<<0,r||w?(c.beginPath(),
+c.moveTo(q,n),x={x:q,y:n},t&&(d.beginPath(),d.moveTo(q,n)),w=r=!1):(c.lineTo(q,v),t&&d.lineTo(q,v),c.lineTo(q,n),t&&d.lineTo(q,n),0==m%250&&b()),v=h.dataPointIds[m],this._eventManager.objectMap[v]={id:v,objectType:"dataPoint",dataSeriesIndex:p,dataPointIndex:m,x1:q,y1:n},0!==l[m].markerSize&&(0<l[m].markerSize||0<h.markerSize)&&(s=h.getMarkerProperties(m,q,n,c),g.push(s),v=C(v),t&&g.push({x:q,y:n,ctx:d,type:s.type,size:s.size,color:v,borderColor:v,borderThickness:s.borderThickness})),(l[m].indexLabel||
+h.indexLabel)&&this._indexLabels.push({chartType:"stepArea",dataPoint:l[m],dataSeries:h,point:{x:q,y:n},direction:0<=l[m].y?1:-1,color:Q}))}b();J.drawMarkers(g)}}c.restore();t&&this._eventManager.ghostCtx.restore();return{source:c,dest:this.plotArea.ctx,animationCallback:y.xClipAnimation,easingFunction:y.easing.linear,animationBase:0}}};v.prototype.renderStackedArea=function(a){var b=a.targetCanvasCtx||this.plotArea.ctx;if(!(0>=a.dataSeriesIndexes.length)){var c=null,d=[],e=this.plotArea,f=[],g=[],
+k=0,p,h,l,r=a.axisY.conversionParameters.reference+a.axisY.conversionParameters.pixelPerUnit*(0-a.axisY.conversionParameters.minimum)<<0,m=this._eventManager.ghostCtx;t&&m.beginPath();b.save();t&&m.save();b.beginPath();b.rect(e.x1,e.y1,e.width,e.height);b.clip();t&&(m.beginPath(),m.rect(e.x1,e.y1,e.width,e.height),m.clip());xValuePresent=[];for(e=0;e<a.dataSeriesIndexes.length;e++){var q=a.dataSeriesIndexes[e],n=this.data[q],s=n.dataPoints;n.dataPointIndexes=[];for(k=0;k<s.length;k++)q=s[k].x.getTime?
+s[k].x.getTime():s[k].x,n.dataPointIndexes[q]=k,xValuePresent[q]||(g.push(q),xValuePresent[q]=!0);g.sort(ra)}for(e=0;e<a.dataSeriesIndexes.length;e++){var q=a.dataSeriesIndexes[e],n=this.data[q],s=n.dataPoints,B=!0,u=[],k=n.id;this._eventManager.objectMap[k]={objectType:"dataSeries",dataSeriesIndex:q};k=C(k);m.fillStyle=k;if(0<g.length){c=n._colorSet[0];b.fillStyle=c;b.strokeStyle=c;b.lineWidth=n.lineThickness;for(k=0;k<g.length;k++){l=g[k];var x=null,x=0<=n.dataPointIndexes[l]?s[n.dataPointIndexes[l]]:
+{x:l,y:0};if(!(l<a.axisX.dataInfo.viewPortMin||l>a.axisX.dataInfo.viewPortMax)&&"number"===typeof x.y){p=a.axisX.conversionParameters.reference+a.axisX.conversionParameters.pixelPerUnit*(l-a.axisX.conversionParameters.minimum)+0.5<<0;h=a.axisY.conversionParameters.reference+a.axisY.conversionParameters.pixelPerUnit*(x.y-a.axisY.conversionParameters.minimum);var w=f[l]?f[l]:0;h-=w;u.push({x:p,y:r-w});f[l]=r-h;if(B)b.beginPath(),b.moveTo(p,h),t&&(m.beginPath(),m.moveTo(p,h)),B=!1;else if(b.lineTo(p,
+h),t&&m.lineTo(p,h),0==k%250){for(0<n.lineThickness&&b.stroke();0<u.length;){var v=u.pop();b.lineTo(v.x,v.y);t&&m.lineTo(v.x,v.y)}b.closePath();b.globalAlpha=n.fillOpacity;b.fill();b.globalAlpha=1;b.beginPath();b.moveTo(p,h);t&&(m.closePath(),m.fill(),m.beginPath(),m.moveTo(p,h));u.push({x:p,y:r-w})}if(0<=n.dataPointIndexes[l]){var G=n.dataPointIds[n.dataPointIndexes[l]];this._eventManager.objectMap[G]={id:G,objectType:"dataPoint",dataSeriesIndex:q,dataPointIndex:n.dataPointIndexes[l],x1:p,y1:h}}0<=
+n.dataPointIndexes[l]&&0!==x.markerSize&&(0<x.markerSize||0<n.markerSize)&&(l=n.getMarkerProperties(k,p,h,b),d.push(l),markerColor=C(G),t&&d.push({x:p,y:h,ctx:m,type:l.type,size:l.size,color:markerColor,borderColor:markerColor,borderThickness:l.borderThickness}));(x.indexLabel||n.indexLabel)&&this._indexLabels.push({chartType:"stackedArea",dataPoint:x,dataSeries:n,point:{x:p,y:h},direction:0<=s[k].y?1:-1,color:c})}}for(0<n.lineThickness&&b.stroke();0<u.length;)v=u.pop(),b.lineTo(v.x,v.y),t&&m.lineTo(v.x,
+v.y);b.closePath();b.globalAlpha=n.fillOpacity;b.fill();b.globalAlpha=1;b.beginPath();b.moveTo(p,h);t&&(m.closePath(),m.fill(),m.beginPath(),m.moveTo(p,h))}delete n.dataPointIndexes}J.drawMarkers(d);b.restore();t&&m.restore();return{source:b,dest:this.plotArea.ctx,animationCallback:y.xClipAnimation,easingFunction:y.easing.linear,animationBase:0}}};v.prototype.renderStackedArea100=function(a){var b=a.targetCanvasCtx||this.plotArea.ctx;if(!(0>=a.dataSeriesIndexes.length)){var c=null,d=this.plotArea,
+e=[],f=[],g=[],k=0,p,h,l,r=a.axisY.conversionParameters.reference+a.axisY.conversionParameters.pixelPerUnit*(0-a.axisY.conversionParameters.minimum)<<0,m=0.15*this.width<<0,q=a.axisX.dataInfo.minDiff,q=0.9*d.width/Math.abs(a.axisX.maximum-a.axisX.minimum)*Math.abs(q)<<0,n=this._eventManager.ghostCtx;b.save();t&&n.save();b.beginPath();b.rect(d.x1,d.y1,d.width,d.height);b.clip();t&&(n.beginPath(),n.rect(d.x1,d.y1,d.width,d.height),n.clip());xValuePresent=[];for(d=0;d<a.dataSeriesIndexes.length;d++){var s=
+a.dataSeriesIndexes[d],B=this.data[s],u=B.dataPoints;B.dataPointIndexes=[];for(k=0;k<u.length;k++)s=u[k].x.getTime?u[k].x.getTime():u[k].x,B.dataPointIndexes[s]=k,xValuePresent[s]||(g.push(s),xValuePresent[s]=!0);g.sort(ra)}for(d=0;d<a.dataSeriesIndexes.length;d++){var s=a.dataSeriesIndexes[d],B=this.data[s],u=B.dataPoints,x=!0,c=B.id;this._eventManager.objectMap[c]={objectType:"dataSeries",dataSeriesIndex:s};c=C(c);n.fillStyle=c;1==u.length&&(q=m);1>q?q=1:q>m&&(q=m);var w=[];if(0<g.length){c=B._colorSet[k%
+B._colorSet.length];b.fillStyle=c;b.strokeStyle=c;b.lineWidth=B.lineThickness;for(k=0;k<g.length;k++){l=g[k];var v=null,v=0<=B.dataPointIndexes[l]?u[B.dataPointIndexes[l]]:{x:l,y:0};if(!(l<a.axisX.dataInfo.viewPortMin||l>a.axisX.dataInfo.viewPortMax)&&"number"===typeof v.y){h=0!==a.dataPointYSums[l]?100*(v.y/a.dataPointYSums[l]):0;p=a.axisX.conversionParameters.reference+a.axisX.conversionParameters.pixelPerUnit*(l-a.axisX.conversionParameters.minimum)+0.5<<0;h=a.axisY.conversionParameters.reference+
+a.axisY.conversionParameters.pixelPerUnit*(h-a.axisY.conversionParameters.minimum);var G=f[l]?f[l]:0;h-=G;w.push({x:p,y:r-G});f[l]=r-h;if(x)b.beginPath(),b.moveTo(p,h),t&&(n.beginPath(),n.moveTo(p,h)),x=!1;else if(b.lineTo(p,h),t&&n.lineTo(p,h),0==k%250){for(0<B.lineThickness&&b.stroke();0<w.length;){var z=w.pop();b.lineTo(z.x,z.y);t&&n.lineTo(z.x,z.y)}b.closePath();b.globalAlpha=B.fillOpacity;b.fill();b.globalAlpha=1;b.beginPath();b.moveTo(p,h);t&&(n.closePath(),n.fill(),n.beginPath(),n.moveTo(p,
+h));w.push({x:p,y:r-G})}if(0<=B.dataPointIndexes[l]){var A=B.dataPointIds[B.dataPointIndexes[l]];this._eventManager.objectMap[A]={id:A,objectType:"dataPoint",dataSeriesIndex:s,dataPointIndex:B.dataPointIndexes[l],x1:p,y1:h}}0<=B.dataPointIndexes[l]&&0!==v.markerSize&&(0<v.markerSize||0<B.markerSize)&&(l=B.getMarkerProperties(k,p,h,b),e.push(l),markerColor=C(A),t&&e.push({x:p,y:h,ctx:n,type:l.type,size:l.size,color:markerColor,borderColor:markerColor,borderThickness:l.borderThickness}));(v.indexLabel||
+B.indexLabel)&&this._indexLabels.push({chartType:"stackedArea100",dataPoint:v,dataSeries:B,point:{x:p,y:h},direction:0<=u[k].y?1:-1,color:c})}}for(0<B.lineThickness&&b.stroke();0<w.length;)z=w.pop(),b.lineTo(z.x,z.y),t&&n.lineTo(z.x,z.y);b.closePath();b.globalAlpha=B.fillOpacity;b.fill();b.globalAlpha=1;b.beginPath();b.moveTo(p,h);t&&(n.closePath(),n.fill(),n.beginPath(),n.moveTo(p,h))}delete B.dataPointIndexes}J.drawMarkers(e);b.restore();t&&n.restore();return{source:b,dest:this.plotArea.ctx,animationCallback:y.xClipAnimation,
+easingFunction:y.easing.linear,animationBase:0}}};v.prototype.renderBubble=function(a){var b=a.targetCanvasCtx||this.plotArea.ctx,c=a.dataSeriesIndexes.length;if(!(0>=c)){var d=this.plotArea,e=0,f,g,k=0.15*this.width<<0,e=a.axisX.dataInfo.minDiff,c=0.9*(d.width/Math.abs(a.axisX.maximum-a.axisX.minimum)*Math.abs(e)/c)<<0;b.save();t&&this._eventManager.ghostCtx.save();b.beginPath();b.rect(d.x1,d.y1,d.width,d.height);b.clip();t&&(this._eventManager.ghostCtx.rect(d.x1,d.y1,d.width,d.height),this._eventManager.ghostCtx.clip());
+for(var p=-Infinity,h=Infinity,l=0;l<a.dataSeriesIndexes.length;l++)for(var r=a.dataSeriesIndexes[l],m=this.data[r],q=m.dataPoints,n=0,e=0;e<q.length;e++)f=q[e].getTime?f=q[e].x.getTime():f=q[e].x,f<a.axisX.dataInfo.viewPortMin||f>a.axisX.dataInfo.viewPortMax||"undefined"===typeof q[e].z||(n=q[e].z,n>p&&(p=n),n<h&&(h=n));for(var s=25*Math.PI,d=Math.max(Math.pow(0.25*Math.min(d.height,d.width)/2,2)*Math.PI,s),l=0;l<a.dataSeriesIndexes.length;l++)if(r=a.dataSeriesIndexes[l],m=this.data[r],q=m.dataPoints,
+1==q.length&&(c=k),1>c?c=1:c>k&&(c=k),0<q.length)for(b.strokeStyle="#4572A7 ",e=0;e<q.length;e++)if(f=q[e].getTime?f=q[e].x.getTime():f=q[e].x,!(f<a.axisX.dataInfo.viewPortMin||f>a.axisX.dataInfo.viewPortMax)&&"number"===typeof q[e].y){f=a.axisX.conversionParameters.reference+a.axisX.conversionParameters.pixelPerUnit*(f-a.axisX.conversionParameters.minimum)+0.5<<0;g=a.axisY.conversionParameters.reference+a.axisY.conversionParameters.pixelPerUnit*(q[e].y-a.axisY.conversionParameters.minimum)+0.5<<
+0;var n=q[e].z,B=2*Math.max(Math.sqrt((p===h?d/2:s+(d-s)/(p-h)*(n-h))/Math.PI)<<0,1),n=m.getMarkerProperties(e,b);n.size=B;b.globalAlpha=m.fillOpacity;J.drawMarker(f,g,b,n.type,n.size,n.color,n.borderColor,n.borderThickness);b.globalAlpha=1;var u=m.dataPointIds[e];this._eventManager.objectMap[u]={id:u,objectType:"dataPoint",dataSeriesIndex:r,dataPointIndex:e,x1:f,y1:g,size:B};B=C(u);t&&J.drawMarker(f,g,this._eventManager.ghostCtx,n.type,n.size,B,B,n.borderThickness);(q[e].indexLabel||m.indexLabel)&&
+this._indexLabels.push({chartType:"bubble",dataPoint:q[e],dataSeries:m,point:{x:f,y:g},direction:1,color:null})}b.restore();t&&this._eventManager.ghostCtx.restore();return{source:b,dest:this.plotArea.ctx,animationCallback:y.fadeInAnimation,easingFunction:y.easing.easeInQuad,animationBase:0}}};v.prototype.renderScatter=function(a){var b=a.targetCanvasCtx||this.plotArea.ctx,c=a.dataSeriesIndexes.length;if(!(0>=c)){var d=this.plotArea,e=0,f,g,k=0.15*this.width<<0,e=a.axisX.dataInfo.minDiff,c=0.9*(d.width/
+Math.abs(a.axisX.maximum-a.axisX.minimum)*Math.abs(e)/c)<<0;b.save();t&&this._eventManager.ghostCtx.save();b.beginPath();b.rect(d.x1,d.y1,d.width,d.height);b.clip();t&&(this._eventManager.ghostCtx.rect(d.x1,d.y1,d.width,d.height),this._eventManager.ghostCtx.clip());for(var p=0;p<a.dataSeriesIndexes.length;p++){var h=a.dataSeriesIndexes[p],l=this.data[h],r=l.dataPoints;1==r.length&&(c=k);1>c?c=1:c>k&&(c=k);if(0<r.length){b.strokeStyle="#4572A7 ";Math.pow(0.3*Math.min(d.height,d.width)/2,2);for(var m=
+0,q=0,e=0;e<r.length;e++)if(f=r[e].getTime?f=r[e].x.getTime():f=r[e].x,!(f<a.axisX.dataInfo.viewPortMin||f>a.axisX.dataInfo.viewPortMax)&&"number"===typeof r[e].y){f=a.axisX.conversionParameters.reference+a.axisX.conversionParameters.pixelPerUnit*(f-a.axisX.conversionParameters.minimum)+0.5<<0;g=a.axisY.conversionParameters.reference+a.axisY.conversionParameters.pixelPerUnit*(r[e].y-a.axisY.conversionParameters.minimum)+0.5<<0;var n=l.getMarkerProperties(e,f,g,b);b.globalAlpha=l.fillOpacity;J.drawMarker(n.x,
+n.y,n.ctx,n.type,n.size,n.color,n.borderColor,n.borderThickness);b.globalAlpha=1;Math.sqrt((m-f)*(m-f)+(q-g)*(q-g))<Math.min(n.size,5)&&r.length>Math.min(this.plotArea.width,this.plotArea.height)||(m=l.dataPointIds[e],this._eventManager.objectMap[m]={id:m,objectType:"dataPoint",dataSeriesIndex:h,dataPointIndex:e,x1:f,y1:g},m=C(m),t&&J.drawMarker(n.x,n.y,this._eventManager.ghostCtx,n.type,n.size,m,m,n.borderThickness),(r[e].indexLabel||l.indexLabel)&&this._indexLabels.push({chartType:"scatter",dataPoint:r[e],
+dataSeries:l,point:{x:f,y:g},direction:1,color:null}),m=f,q=g)}}}b.restore();t&&this._eventManager.ghostCtx.restore();return{source:b,dest:this.plotArea.ctx,animationCallback:y.fadeInAnimation,easingFunction:y.easing.easeInQuad,animationBase:0}}};v.prototype.renderCandlestick=function(a){var b=a.targetCanvasCtx||this.plotArea.ctx,c=this._eventManager.ghostCtx;if(!(0>=a.dataSeriesIndexes.length)){var d=null,d=this.plotArea,e=0,f,g,k,p,h,l,e=0.015*this.width;f=a.axisX.dataInfo.minDiff;var r=0.7*d.width/
+Math.abs(a.axisX.maximum-a.axisX.minimum)*Math.abs(f)<<0;r>e?r=e:Infinity===f?r=e:1>r&&(r=1);b.save();t&&c.save();b.beginPath();b.rect(d.x1,d.y1,d.width,d.height);b.clip();t&&(c.rect(d.x1,d.y1,d.width,d.height),c.clip());for(var m=0;m<a.dataSeriesIndexes.length;m++){var q=a.dataSeriesIndexes[m],n=this.data[q],s=n.dataPoints;if(0<s.length)for(var B=5<r&&n.bevelEnabled?!0:!1,e=0;e<s.length;e++)if(s[e].getTime?l=s[e].x.getTime():l=s[e].x,!(l<a.axisX.dataInfo.viewPortMin||l>a.axisX.dataInfo.viewPortMax)&&
+null!==s[e].y&&s[e].y.length&&"number"===typeof s[e].y[0]&&"number"===typeof s[e].y[1]&&"number"===typeof s[e].y[2]&&"number"===typeof s[e].y[3]){f=a.axisX.conversionParameters.reference+a.axisX.conversionParameters.pixelPerUnit*(l-a.axisX.conversionParameters.minimum)+0.5<<0;g=a.axisY.conversionParameters.reference+a.axisY.conversionParameters.pixelPerUnit*(s[e].y[0]-a.axisY.conversionParameters.minimum)+0.5<<0;k=a.axisY.conversionParameters.reference+a.axisY.conversionParameters.pixelPerUnit*(s[e].y[1]-
+a.axisY.conversionParameters.minimum)+0.5<<0;p=a.axisY.conversionParameters.reference+a.axisY.conversionParameters.pixelPerUnit*(s[e].y[2]-a.axisY.conversionParameters.minimum)+0.5<<0;h=a.axisY.conversionParameters.reference+a.axisY.conversionParameters.pixelPerUnit*(s[e].y[3]-a.axisY.conversionParameters.minimum)+0.5<<0;var u=f-r/2<<0,x=u+r<<0,d=s[e].color?s[e].color:n._colorSet[0],w=Math.round(Math.max(1,0.15*r)),v=0===w%2?0:0.5,G=n.dataPointIds[e];this._eventManager.objectMap[G]={id:G,objectType:"dataPoint",
+dataSeriesIndex:q,dataPointIndex:e,x1:u,y1:g,x2:x,y2:k,x3:f,y3:p,x4:f,y4:h,borderThickness:w,color:d};b.strokeStyle=d;b.beginPath();b.lineWidth=w;c.lineWidth=Math.max(w,4);"candlestick"===n.type?(b.moveTo(f-v,k),b.lineTo(f-v,Math.min(g,h)),b.stroke(),b.moveTo(f-v,Math.max(g,h)),b.lineTo(f-v,p),b.stroke(),F(b,u,Math.min(g,h),x,Math.max(g,h),s[e].y[0]<=s[e].y[3]?n.risingColor:d,w,d,B,B,!1,!1,n.fillOpacity),t&&(d=C(G),c.strokeStyle=d,c.moveTo(f-v,k),c.lineTo(f-v,Math.min(g,h)),c.stroke(),c.moveTo(f-
+v,Math.max(g,h)),c.lineTo(f-v,p),c.stroke(),F(c,u,Math.min(g,h),x,Math.max(g,h),d,0,null,!1,!1,!1,!1))):"ohlc"===n.type&&(b.moveTo(f-v,k),b.lineTo(f-v,p),b.stroke(),b.beginPath(),b.moveTo(f,g),b.lineTo(u,g),b.stroke(),b.beginPath(),b.moveTo(f,h),b.lineTo(x,h),b.stroke(),t&&(d=C(G),c.strokeStyle=d,c.moveTo(f-v,k),c.lineTo(f-v,p),c.stroke(),c.beginPath(),c.moveTo(f,g),c.lineTo(u,g),c.stroke(),c.beginPath(),c.moveTo(f,h),c.lineTo(x,h),c.stroke()));(s[e].indexLabel||n.indexLabel)&&this._indexLabels.push({chartType:n.type,
+dataPoint:s[e],dataSeries:n,point:{x:u+(x-u)/2,y:k},direction:1,bounds:{x1:u,y1:Math.min(k,p),x2:x,y2:Math.max(k,p)},color:d})}}b.restore();t&&c.restore();return{source:b,dest:this.plotArea.ctx,animationCallback:y.fadeInAnimation,easingFunction:y.easing.easeInQuad,animationBase:0}}};v.prototype.renderRangeColumn=function(a){var b=a.targetCanvasCtx||this.plotArea.ctx;if(!(0>=a.dataSeriesIndexes.length)){var c=null,d=this.plotArea,e=0,f,g,e=0.03*this.width;f=a.axisX.dataInfo.minDiff;var k=0.9*(d.width/
+Math.abs(a.axisX.maximum-a.axisX.minimum)*Math.abs(f)/a.plotType.totalDataSeries)<<0;k>e?k=e:Infinity===f?k=e:1>k&&(k=1);b.save();t&&this._eventManager.ghostCtx.save();b.beginPath();b.rect(d.x1,d.y1,d.width,d.height);b.clip();t&&(this._eventManager.ghostCtx.rect(d.x1,d.y1,d.width,d.height),this._eventManager.ghostCtx.clip());for(var p=0;p<a.dataSeriesIndexes.length;p++){var h=a.dataSeriesIndexes[p],l=this.data[h],r=l.dataPoints;if(0<r.length)for(var m=5<k&&l.bevelEnabled?!0:!1,e=0;e<r.length;e++)if(r[e].getTime?
+g=r[e].x.getTime():g=r[e].x,!(g<a.axisX.dataInfo.viewPortMin||g>a.axisX.dataInfo.viewPortMax)&&null!==r[e].y&&r[e].y.length&&"number"===typeof r[e].y[0]&&"number"===typeof r[e].y[1]){c=a.axisX.conversionParameters.reference+a.axisX.conversionParameters.pixelPerUnit*(g-a.axisX.conversionParameters.minimum)+0.5<<0;d=a.axisY.conversionParameters.reference+a.axisY.conversionParameters.pixelPerUnit*(r[e].y[0]-a.axisY.conversionParameters.minimum)+0.5<<0;f=a.axisY.conversionParameters.reference+a.axisY.conversionParameters.pixelPerUnit*
+(r[e].y[1]-a.axisY.conversionParameters.minimum)+0.5<<0;var q=c-a.plotType.totalDataSeries*k/2+(a.previousDataSeriesCount+p)*k<<0,n=q+k<<0,c=r[e].color?r[e].color:l._colorSet[e%l._colorSet.length];if(d>f){var s=d,d=f;f=s}s=l.dataPointIds[e];this._eventManager.objectMap[s]={id:s,objectType:"dataPoint",dataSeriesIndex:h,dataPointIndex:e,x1:q,y1:d,x2:n,y2:f};F(b,q,d,n,f,c,0,c,m,m,!1,!1,l.fillOpacity);c=C(s);t&&F(this._eventManager.ghostCtx,q,d,n,f,c,0,null,!1,!1,!1,!1);if(r[e].indexLabel||l.indexLabel)this._indexLabels.push({chartType:"rangeColumn",
+dataPoint:r[e],dataSeries:l,indexKeyword:0,point:{x:q+(n-q)/2,y:r[e].y[1]>=r[e].y[0]?f:d},direction:r[e].y[1]>=r[e].y[0]?-1:1,bounds:{x1:q,y1:Math.min(d,f),x2:n,y2:Math.max(d,f)},color:c}),this._indexLabels.push({chartType:"rangeColumn",dataPoint:r[e],dataSeries:l,indexKeyword:1,point:{x:q+(n-q)/2,y:r[e].y[1]>=r[e].y[0]?d:f},direction:r[e].y[1]>=r[e].y[0]?1:-1,bounds:{x1:q,y1:Math.min(d,f),x2:n,y2:Math.max(d,f)},color:c})}}b.restore();t&&this._eventManager.ghostCtx.restore();return{source:b,dest:this.plotArea.ctx,
+animationCallback:y.fadeInAnimation,easingFunction:y.easing.easeInQuad,animationBase:0}}};v.prototype.renderRangeBar=function(a){var b=a.targetCanvasCtx||this.plotArea.ctx;if(!(0>=a.dataSeriesIndexes.length)){var c=null,d=this.plotArea,e=0,f,g,k,e=Math.min(0.15*this.height,0.9*(this.plotArea.height/a.plotType.totalDataSeries))<<0;f=a.axisX.dataInfo.minDiff;var p=0.9*(d.height/Math.abs(a.axisX.maximum-a.axisX.minimum)*Math.abs(f)/a.plotType.totalDataSeries)<<0;p>e?p=e:Infinity===f?p=e:1>p&&(p=1);b.save();
+t&&this._eventManager.ghostCtx.save();b.beginPath();b.rect(d.x1,d.y1,d.width,d.height);b.clip();t&&(this._eventManager.ghostCtx.rect(d.x1,d.y1,d.width,d.height),this._eventManager.ghostCtx.clip());for(var h=0;h<a.dataSeriesIndexes.length;h++){var l=a.dataSeriesIndexes[h],r=this.data[l],m=r.dataPoints;if(0<m.length){var q=5<p&&r.bevelEnabled?!0:!1;b.strokeStyle="#4572A7 ";for(e=0;e<m.length;e++)if(m[e].getTime?k=m[e].x.getTime():k=m[e].x,!(k<a.axisX.dataInfo.viewPortMin||k>a.axisX.dataInfo.viewPortMax)&&
+null!==m[e].y&&m[e].y.length&&"number"===typeof m[e].y[0]&&"number"===typeof m[e].y[1]){d=a.axisY.conversionParameters.reference+a.axisY.conversionParameters.pixelPerUnit*(m[e].y[0]-a.axisY.conversionParameters.minimum)+0.5<<0;f=a.axisY.conversionParameters.reference+a.axisY.conversionParameters.pixelPerUnit*(m[e].y[1]-a.axisY.conversionParameters.minimum)+0.5<<0;g=a.axisX.conversionParameters.reference+a.axisX.conversionParameters.pixelPerUnit*(k-a.axisX.conversionParameters.minimum)+0.5<<0;g=g-
+a.plotType.totalDataSeries*p/2+(a.previousDataSeriesCount+h)*p<<0;var n=g+p<<0;d>f&&(c=d,d=f,f=c);c=m[e].color?m[e].color:r._colorSet[e%r._colorSet.length];F(b,d,g,f,n,c,0,null,q,!1,!1,!1,r.fillOpacity);c=r.dataPointIds[e];this._eventManager.objectMap[c]={id:c,objectType:"dataPoint",dataSeriesIndex:l,dataPointIndex:e,x1:d,y1:g,x2:f,y2:n};c=C(c);t&&F(this._eventManager.ghostCtx,d,g,f,n,c,0,null,!1,!1,!1,!1);if(m[e].indexLabel||r.indexLabel)this._indexLabels.push({chartType:"rangeBar",dataPoint:m[e],
+dataSeries:r,indexKeyword:0,point:{x:m[e].y[1]>=m[e].y[0]?d:f,y:g+(n-g)/2},direction:m[e].y[1]>=m[e].y[0]?-1:1,bounds:{x1:Math.min(d,f),y1:g,x2:Math.max(d,f),y2:n},color:c}),this._indexLabels.push({chartType:"rangeBar",dataPoint:m[e],dataSeries:r,indexKeyword:1,point:{x:m[e].y[1]>=m[e].y[0]?f:d,y:g+(n-g)/2},direction:m[e].y[1]>=m[e].y[0]?1:-1,bounds:{x1:Math.min(d,f),y1:g,x2:Math.max(d,f),y2:n},color:c})}}}b.restore();t&&this._eventManager.ghostCtx.restore();return{source:b,dest:this.plotArea.ctx,
+animationCallback:y.fadeInAnimation,easingFunction:y.easing.easeInQuad,animationBase:0}}};v.prototype.renderRangeArea=function(a){function b(){if(v){var a=null;0<p.lineThickness&&c.stroke();for(var b=g.length-1;0<=b;b--)a=g[b],c.lineTo(a.x,a.y),d.lineTo(a.x,a.y);c.closePath();c.globalAlpha=p.fillOpacity;c.fill();c.globalAlpha=1;d.fill();if(0<p.lineThickness){c.beginPath();c.moveTo(a.x,a.y);for(b=0;b<g.length;b++)a=g[b],c.lineTo(a.x,a.y);c.stroke()}c.beginPath();c.moveTo(m,q);d.beginPath();d.moveTo(m,
+q);v={x:m,y:q};g=[];g.push({x:m,y:n})}}var c=a.targetCanvasCtx||this.plotArea.ctx;if(!(0>=a.dataSeriesIndexes.length)){var d=this._eventManager.ghostCtx,e=[],f=this.plotArea;c.save();t&&d.save();c.beginPath();c.rect(f.x1,f.y1,f.width,f.height);c.clip();t&&(d.beginPath(),d.rect(f.x1,f.y1,f.width,f.height),d.clip());for(f=0;f<a.dataSeriesIndexes.length;f++){var g=[],k=a.dataSeriesIndexes[f],p=this.data[k],h=p.dataPoints,e=p.id;this._eventManager.objectMap[e]={objectType:"dataSeries",dataSeriesIndex:k};
+e=C(e);d.fillStyle=e;var e=[],l=!0,r=0,m,q,n,s,v=null;if(0<h.length){var u=p._colorSet[r%p._colorSet.length];c.fillStyle=u;c.strokeStyle=u;c.lineWidth=p.lineThickness;for(var x=!0;r<h.length;r++)if(s=h[r].x.getTime?h[r].x.getTime():h[r].x,!(s<a.axisX.dataInfo.viewPortMin||s>a.axisX.dataInfo.viewPortMax))if(null!==h[r].y&&h[r].y.length&&"number"===typeof h[r].y[0]&&"number"===typeof h[r].y[1]){m=a.axisX.conversionParameters.reference+a.axisX.conversionParameters.pixelPerUnit*(s-a.axisX.conversionParameters.minimum)+
+0.5<<0;q=a.axisY.conversionParameters.reference+a.axisY.conversionParameters.pixelPerUnit*(h[r].y[0]-a.axisY.conversionParameters.minimum)+0.5<<0;n=a.axisY.conversionParameters.reference+a.axisY.conversionParameters.pixelPerUnit*(h[r].y[1]-a.axisY.conversionParameters.minimum)+0.5<<0;l||x?(c.beginPath(),c.moveTo(m,q),v={x:m,y:q},g=[],g.push({x:m,y:n}),t&&(d.beginPath(),d.moveTo(m,q)),x=l=!1):(c.lineTo(m,q),g.push({x:m,y:n}),t&&d.lineTo(m,q),0==r%250&&b());s=p.dataPointIds[r];this._eventManager.objectMap[s]=
+{id:s,objectType:"dataPoint",dataSeriesIndex:k,dataPointIndex:r,x1:m,y1:q,y2:n};if(0!==h[r].markerSize&&(0<h[r].markerSize||0<p.markerSize)){var w=p.getMarkerProperties(r,m,n,c);e.push(w);var z=C(s);t&&e.push({x:m,y:n,ctx:d,type:w.type,size:w.size,color:z,borderColor:z,borderThickness:w.borderThickness});w=p.getMarkerProperties(r,m,q,c);e.push(w);z=C(s);t&&e.push({x:m,y:q,ctx:d,type:w.type,size:w.size,color:z,borderColor:z,borderThickness:w.borderThickness})}if(h[r].indexLabel||p.indexLabel)this._indexLabels.push({chartType:"rangeArea",
+dataPoint:h[r],dataSeries:p,indexKeyword:0,point:{x:m,y:q},direction:h[r].y[0]<=h[r].y[1]?-1:1,color:u}),this._indexLabels.push({chartType:"rangeArea",dataPoint:h[r],dataSeries:p,indexKeyword:1,point:{x:m,y:n},direction:h[r].y[0]<=h[r].y[1]?1:-1,color:u})}else b(),x=!0;b();J.drawMarkers(e)}}c.restore();t&&this._eventManager.ghostCtx.restore();return{source:c,dest:this.plotArea.ctx,animationCallback:y.xClipAnimation,easingFunction:y.easing.linear,animationBase:0}}};v.prototype.renderRangeSplineArea=
+function(a){function b(){var a=fa(q,2);if(0<a.length){c.beginPath();c.moveTo(a[0].x,a[0].y);t&&(d.beginPath(),d.moveTo(a[0].x,a[0].y));for(var b=0;b<a.length-3;b+=3)c.bezierCurveTo(a[b+1].x,a[b+1].y,a[b+2].x,a[b+2].y,a[b+3].x,a[b+3].y),t&&d.bezierCurveTo(a[b+1].x,a[b+1].y,a[b+2].x,a[b+2].y,a[b+3].x,a[b+3].y);0<k.lineThickness&&c.stroke();a=fa(n,2);c.lineTo(n[n.length-1].x,n[n.length-1].y);for(b=a.length-1;2<b;b-=3)c.bezierCurveTo(a[b-1].x,a[b-1].y,a[b-2].x,a[b-2].y,a[b-3].x,a[b-3].y),t&&d.bezierCurveTo(a[b-
+1].x,a[b-1].y,a[b-2].x,a[b-2].y,a[b-3].x,a[b-3].y);c.closePath();c.globalAlpha=k.fillOpacity;c.fill();c.globalAlpha=1;if(0<k.lineThickness){c.beginPath();c.moveTo(n[n.length-1].x,n[n.length-1].y);for(b=a.length-1;2<b;b-=3)c.bezierCurveTo(a[b-1].x,a[b-1].y,a[b-2].x,a[b-2].y,a[b-3].x,a[b-3].y),t&&d.bezierCurveTo(a[b-1].x,a[b-1].y,a[b-2].x,a[b-2].y,a[b-3].x,a[b-3].y);c.stroke()}c.beginPath();t&&(d.closePath(),d.fill())}}var c=a.targetCanvasCtx||this.plotArea.ctx;if(!(0>=a.dataSeriesIndexes.length)){var d=
+this._eventManager.ghostCtx,e=[],f=this.plotArea;c.save();t&&d.save();c.beginPath();c.rect(f.x1,f.y1,f.width,f.height);c.clip();t&&(d.beginPath(),d.rect(f.x1,f.y1,f.width,f.height),d.clip());for(f=0;f<a.dataSeriesIndexes.length;f++){var g=a.dataSeriesIndexes[f],k=this.data[g],p=k.dataPoints,e=k.id;this._eventManager.objectMap[e]={objectType:"dataSeries",dataSeriesIndex:g};e=C(e);d.fillStyle=e;var e=[],h=0,l,r,m,q=[],n=[];if(0<p.length){color=k._colorSet[h%k._colorSet.length];c.fillStyle=color;c.strokeStyle=
+color;for(c.lineWidth=k.lineThickness;h<p.length;h++)if(l=p[h].x.getTime?p[h].x.getTime():p[h].x,!(l<a.axisX.dataInfo.viewPortMin||l>a.axisX.dataInfo.viewPortMax))if(null!==p[h].y&&p[h].y.length&&"number"===typeof p[h].y[0]&&"number"===typeof p[h].y[1]){l=a.axisX.conversionParameters.reference+a.axisX.conversionParameters.pixelPerUnit*(l-a.axisX.conversionParameters.minimum)+0.5<<0;r=a.axisY.conversionParameters.reference+a.axisY.conversionParameters.pixelPerUnit*(p[h].y[0]-a.axisY.conversionParameters.minimum)+
+0.5<<0;m=a.axisY.conversionParameters.reference+a.axisY.conversionParameters.pixelPerUnit*(p[h].y[1]-a.axisY.conversionParameters.minimum)+0.5<<0;var s=k.dataPointIds[h];this._eventManager.objectMap[s]={id:s,objectType:"dataPoint",dataSeriesIndex:g,dataPointIndex:h,x1:l,y1:r,y2:m};q[q.length]={x:l,y:r};n[n.length]={x:l,y:m};if(0!==p[h].markerSize&&(0<p[h].markerSize||0<k.markerSize)){var v=k.getMarkerProperties(h,l,r,c);e.push(v);var u=C(s);t&&e.push({x:l,y:r,ctx:d,type:v.type,size:v.size,color:u,
+borderColor:u,borderThickness:v.borderThickness});v=k.getMarkerProperties(h,l,m,c);e.push(v);u=C(s);t&&e.push({x:l,y:m,ctx:d,type:v.type,size:v.size,color:u,borderColor:u,borderThickness:v.borderThickness})}if(p[h].indexLabel||k.indexLabel)this._indexLabels.push({chartType:"splineArea",dataPoint:p[h],dataSeries:k,indexKeyword:0,point:{x:l,y:r},direction:p[h].y[0]<=p[h].y[1]?-1:1,color:color}),this._indexLabels.push({chartType:"splineArea",dataPoint:p[h],dataSeries:k,indexKeyword:1,point:{x:l,y:m},
+direction:p[h].y[0]<=p[h].y[1]?1:-1,color:color})}else 0<h&&(b(),q=[],n=[]);b();J.drawMarkers(e)}}c.restore();t&&this._eventManager.ghostCtx.restore();return{source:c,dest:this.plotArea.ctx,animationCallback:y.xClipAnimation,easingFunction:y.easing.linear,animationBase:0}}};var pa=function(a,b,c,d,e,f,g,k){"undefined"===typeof k&&(k=1);if(!t){var p=Number((g%(2*Math.PI)).toFixed(8));Number((f%(2*Math.PI)).toFixed(8))===p&&(g-=1E-4)}a.save();a.globalAlpha=k;"pie"===e?(a.beginPath(),a.moveTo(b.x,b.y),
+a.arc(b.x,b.y,c,f,g,!1),a.fillStyle=d,a.strokeStyle="white",a.lineWidth=2,a.closePath(),a.fill()):"doughnut"===e&&(a.beginPath(),a.arc(b.x,b.y,c,f,g,!1),a.arc(b.x,b.y,0.6*c,g,f,!0),a.closePath(),a.fillStyle=d,a.strokeStyle="white",a.lineWidth=2,a.fill());a.globalAlpha=1;a.restore()};v.prototype.renderPie=function(a){function b(){if(h&&l){var a=0,b=0,c=0,d=0;for(A=0;A<l.length;A++){var e=l[A],g=h.dataPointIds[A],f={id:g,objectType:"dataPoint",dataPointIndex:A,dataSeriesIndex:0};n.push(f);var k=e.indexLabel?
+e.indexLabel:h.indexLabel?h.indexLabel:e.label?e.label:h.label?h.label:"";p._eventManager.objectMap[g]=f;f.center={x:w.x,y:w.y};f.y=e.y;f.radius=y;f.indexLabelText=p.replaceKeywordsWithValue(k,e,h,A);f.indexLabelPlacement=h.indexLabelPlacement;f.indexLabelLineColor=e.indexLabelLineColor?e.indexLabelLineColor:h.indexLabelLineColor?h.indexLabelLineColor:e.color?e.color:h._colorSet[A%h._colorSet.length];f.indexLabelLineThickness=e.indexLabelLineThickness?e.indexLabelLineThickness:h.indexLabelLineThickness;
+f.indexLabelFontColor=e.indexLabelFontColor?e.indexLabelFontColor:h.indexLabelFontColor;f.indexLabelFontStyle=e.indexLabelFontStyle?e.indexLabelFontStyle:h.indexLabelFontStyle;f.indexLabelFontWeight=e.indexLabelFontWeight?e.indexLabelFontWeight:h.indexLabelFontWeight;f.indexLabelFontSize=e.indexLabelFontSize?e.indexLabelFontSize:h.indexLabelFontSize;f.indexLabelFontFamily=e.indexLabelFontFamily?e.indexLabelFontFamily:h.indexLabelFontFamily;f.indexLabelBackgroundColor=e.indexLabelBackgroundColor?e.indexLabelBackgroundColor:
+h.indexLabelBackgroundColor?h.indexLabelBackgroundColor:null;f.indexLabelMaxWidth=e.indexLabelMaxWidth?e.indexLabelMaxWidth:h.indexLabelMaxWidth?h.indexLabelMaxWidth:0.33*m.width;f.indexLabelWrap="undefined"!==typeof e.indexLabelWrap?e.indexLabelWrap:h.indexLabelWrap;f.startAngle=0===A?h.startAngle?h.startAngle/180*Math.PI:0:n[A-1].endAngle;f.startAngle=(f.startAngle+2*Math.PI)%(2*Math.PI);f.endAngle=f.startAngle+2*Math.PI/C*Math.abs(e.y);e=(f.endAngle+f.startAngle)/2;e=(e+2*Math.PI)%(2*Math.PI);
+f.midAngle=e;if(f.midAngle>Math.PI/2-u&&f.midAngle<Math.PI/2+u){if(0===a||n[c].midAngle>f.midAngle)c=A;a++}else if(f.midAngle>3*Math.PI/2-u&&f.midAngle<3*Math.PI/2+u){if(0===b||n[d].midAngle>f.midAngle)d=A;b++}f.hemisphere=e>Math.PI/2&&e<=3*Math.PI/2?"left":"right";f.indexLabelTextBlock=new I(p.plotArea.ctx,{fontSize:f.indexLabelFontSize,fontFamily:f.indexLabelFontFamily,fontColor:f.indexLabelFontColor,fontStyle:f.indexLabelFontStyle,fontWeight:f.indexLabelFontWeight,horizontalAlign:"left",backgroundColor:f.indexLabelBackgroundColor,
+maxWidth:f.indexLabelMaxWidth,maxHeight:f.indexLabelWrap?5*f.indexLabelFontSize:1.5*f.indexLabelFontSize,text:f.indexLabelText,padding:0,textBaseline:"top"});f.indexLabelTextBlock.measureText()}g=e=0;k=!1;for(A=0;A<l.length;A++)f=n[(c+A)%l.length],1<a&&(f.midAngle>Math.PI/2-u&&f.midAngle<Math.PI/2+u)&&(e<=a/2&&!k?(f.hemisphere="right",e++):(f.hemisphere="left",k=!0));k=!1;for(A=0;A<l.length;A++)f=n[(d+A)%l.length],1<b&&(f.midAngle>3*Math.PI/2-u&&f.midAngle<3*Math.PI/2+u)&&(g<=b/2&&!k?(f.hemisphere=
+"left",g++):(f.hemisphere="right",k=!0))}}function c(a){var b=p.plotArea.ctx;b.clearRect(m.x1,m.y1,m.width,m.height);b.fillStyle=p.backgroundColor;b.fillRect(m.x1,m.y1,m.width,m.height);for(b=0;b<l.length;b++){var c=n[b].startAngle,d=n[b].endAngle;if(d>c){var e=0.07*y*Math.cos(n[b].midAngle),f=0.07*y*Math.sin(n[b].midAngle),g=!1;if(l[b].exploded){if(1E-9<Math.abs(n[b].center.x-(w.x+e))||1E-9<Math.abs(n[b].center.y-(w.y+f)))n[b].center.x=w.x+e*a,n[b].center.y=w.y+f*a,g=!0}else if(0<Math.abs(n[b].center.x-
+w.x)||0<Math.abs(n[b].center.y-w.y))n[b].center.x=w.x+e*(1-a),n[b].center.y=w.y+f*(1-a),g=!0;g&&(e={},e.dataSeries=h,e.dataPoint=h.dataPoints[b],e.index=b,p._toolTip.highlightObjects([e]));pa(p.plotArea.ctx,n[b].center,n[b].radius,l[b].color?l[b].color:h._colorSet[b%h._colorSet.length],h.type,c,d,h.fillOpacity)}}a=p.plotArea.ctx;a.fillStyle="black";a.strokeStyle="grey";a.textBaseline="middle";a.lineJoin="round";for(b=b=0;b<l.length;b++)c=n[b],c.indexLabelText&&(c.indexLabelTextBlock.y-=c.indexLabelTextBlock.height/
+2,d=0,d="left"===c.hemisphere?"inside"!==h.indexLabelPlacement?-(c.indexLabelTextBlock.width+r):-c.indexLabelTextBlock.width/2:"inside"!==h.indexLabelPlacement?r:-c.indexLabelTextBlock.width/2,c.indexLabelTextBlock.x+=d,c.indexLabelTextBlock.render(!0),c.indexLabelTextBlock.x-=d,c.indexLabelTextBlock.y+=c.indexLabelTextBlock.height/2,"inside"!==c.indexLabelPlacement&&(d=c.center.x+y*Math.cos(c.midAngle),e=c.center.y+y*Math.sin(c.midAngle),a.strokeStyle=c.indexLabelLineColor,a.lineWidth=c.indexLabelLineThickness,
+a.beginPath(),a.moveTo(d,e),a.lineTo(c.indexLabelTextBlock.x,c.indexLabelTextBlock.y),a.lineTo(c.indexLabelTextBlock.x+("left"===c.hemisphere?-r:r),c.indexLabelTextBlock.y),a.stroke()),a.lineJoin="miter")}function d(a,b){var c=a.indexLabelTextBlock.x,d=a.indexLabelTextBlock.y-a.indexLabelTextBlock.height/2,e=a.indexLabelTextBlock.y+a.indexLabelTextBlock.height/2,f=b.indexLabelTextBlock.y-b.indexLabelTextBlock.height/2,g=b.indexLabelTextBlock.x+b.indexLabelTextBlock.width,l=b.indexLabelTextBlock.y+
+b.indexLabelTextBlock.height/2;return a.indexLabelTextBlock.x+a.indexLabelTextBlock.width<b.indexLabelTextBlock.x-r||c>g+r||d>l+r||e<f-r?!1:!0}function e(a,b){var c=0,c=a.indexLabelTextBlock.y-a.indexLabelTextBlock.height/2,d=a.indexLabelTextBlock.y+a.indexLabelTextBlock.height/2,e=b.indexLabelTextBlock.y-b.indexLabelTextBlock.height/2,f=b.indexLabelTextBlock.y+b.indexLabelTextBlock.height/2;return c=b.indexLabelTextBlock.y>a.indexLabelTextBlock.y?e-d:c-f}function f(a){for(var b=null,c=1;c<l.length;c++)if(b=
+(a+c+n.length)%n.length,n[b].hemisphere!==n[a].hemisphere){b=null;break}else if(n[b].indexLabelText&&b!==a&&(0>e(n[b],n[a])||("right"===n[a].hemisphere?n[b].indexLabelTextBlock.y>=n[a].indexLabelTextBlock.y:n[b].indexLabelTextBlock.y<=n[a].indexLabelTextBlock.y)))break;else b=null;return b}function g(a,b){b=b||0;var c=0,d=w.y-1*z,h=w.y+1*z;if(0<=a&&a<l.length){var m=n[a];if(0>b&&m.indexLabelTextBlock.y<d||0<b&&m.indexLabelTextBlock.y>h)return 0;var k=b,p=0,r=0,r=p=p=0;0>k?m.indexLabelTextBlock.y-
+m.indexLabelTextBlock.height/2>d&&m.indexLabelTextBlock.y-m.indexLabelTextBlock.height/2+k<d&&(k=-(d-(m.indexLabelTextBlock.y-m.indexLabelTextBlock.height/2+k))):m.indexLabelTextBlock.y+m.indexLabelTextBlock.height/2<d&&m.indexLabelTextBlock.y+m.indexLabelTextBlock.height/2+k>h&&(k=m.indexLabelTextBlock.y+m.indexLabelTextBlock.height/2+k-h);k=m.indexLabelTextBlock.y+k;d=0;d="right"===m.hemisphere?w.x+Math.sqrt(Math.pow(z,2)-Math.pow(k-w.y,2)):w.x-Math.sqrt(Math.pow(z,2)-Math.pow(k-w.y,2));r=w.x+y*
+Math.cos(m.midAngle);p=w.y+y*Math.sin(m.midAngle);p=Math.sqrt(Math.pow(d-r,2)+Math.pow(k-p,2));r=Math.acos(y/z);p=Math.acos((z*z+y*y-p*p)/(2*y*z));k=p<r?k-m.indexLabelTextBlock.y:0;d=null;for(h=1;h<l.length;h++)if(d=(a-h+n.length)%n.length,n[d].hemisphere!==n[a].hemisphere){d=null;break}else if(n[d].indexLabelText&&n[d].hemisphere===n[a].hemisphere&&d!==a&&(0>e(n[d],n[a])||("right"===n[a].hemisphere?n[d].indexLabelTextBlock.y<=n[a].indexLabelTextBlock.y:n[d].indexLabelTextBlock.y>=n[a].indexLabelTextBlock.y)))break;
+else d=null;r=d;p=f(a);h=d=0;0>k?(h="right"===m.hemisphere?r:p,c=k,null!==h&&(r=-k,k=m.indexLabelTextBlock.y-m.indexLabelTextBlock.height/2-(n[h].indexLabelTextBlock.y+n[h].indexLabelTextBlock.height/2),k-r<s&&(d=-r,q++,h=g(h,d),+h.toFixed(v)>+d.toFixed(v)&&(c=k>s?-(k-s):-(r-(h-d)))))):0<k&&(h="right"===m.hemisphere?p:r,c=k,null!==h&&(r=k,k=n[h].indexLabelTextBlock.y-n[h].indexLabelTextBlock.height/2-(m.indexLabelTextBlock.y+m.indexLabelTextBlock.height/2),k-r<s&&(d=r,q++,h=g(h,d),+h.toFixed(v)<+d.toFixed(v)&&
+(c=k>s?k-s:r-(d-h)))));c&&(k=m.indexLabelTextBlock.y+c,d=0,d="right"===m.hemisphere?w.x+Math.sqrt(Math.pow(z,2)-Math.pow(k-w.y,2)):w.x-Math.sqrt(Math.pow(z,2)-Math.pow(k-w.y,2)),m.midAngle>Math.PI/2-u&&m.midAngle<Math.PI/2+u?(h=(a-1+n.length)%n.length,h=n[h],r=n[(a+1+n.length)%n.length],"left"===m.hemisphere&&"right"===h.hemisphere&&d>h.indexLabelTextBlock.x?d=h.indexLabelTextBlock.x-15:"right"===m.hemisphere&&("left"===r.hemisphere&&d<r.indexLabelTextBlock.x)&&(d=r.indexLabelTextBlock.x+15)):m.midAngle>
+3*Math.PI/2-u&&m.midAngle<3*Math.PI/2+u&&(h=(a-1+n.length)%n.length,h=n[h],r=n[(a+1+n.length)%n.length],"right"===m.hemisphere&&"left"===h.hemisphere&&d<h.indexLabelTextBlock.x?d=h.indexLabelTextBlock.x+15:"left"===m.hemisphere&&("right"===r.hemisphere&&d>r.indexLabelTextBlock.x)&&(d=r.indexLabelTextBlock.x-15)),m.indexLabelTextBlock.y=k,m.indexLabelTextBlock.x=d,m.indexLabelAngle=Math.atan2(m.indexLabelTextBlock.y-w.y,m.indexLabelTextBlock.x-w.x))}return c}function k(){var a=p.plotArea.ctx;a.fillStyle=
+"grey";a.strokeStyle="grey";a.font="16px Arial";a.textBaseline="middle";for(var b=0,c=a=0,a=0;10>a&&(1>a||0<c);a++){y-=c;c=0;if("inside"!==h.indexLabelPlacement){z=y*t;for(b=0;b<l.length;b++){var k=n[b];k.indexLabelTextBlock.x=w.x+z*Math.cos(k.midAngle);k.indexLabelTextBlock.y=w.y+z*Math.sin(k.midAngle);k.indexLabelAngle=k.midAngle;k.radius=y}for(var q,u,b=0;b<l.length;b++){var k=n[b],A=f(b);if(null!==A){q=n[b];u=n[A];var D=0,D=e(q,u)-s;if(0>D){for(var C=u=q=0;C<l.length;C++)C!==b&&n[C].hemisphere===
+k.hemisphere&&(n[C].indexLabelTextBlock.y<k.indexLabelTextBlock.y?q++:u++);C=D/(q+u||1)*u;q=-1*(D-C);var E=u=0;"right"===k.hemisphere?(u=g(b,C),q=-1*(D-u),E=g(A,q),+E.toFixed(v)<+q.toFixed(v)&&+u.toFixed(v)<=+C.toFixed(v)&&g(b,-(q-E))):(u=g(A,C),q=-1*(D-u),E=g(b,q),+E.toFixed(v)<+q.toFixed(v)&&+u.toFixed(v)<=+C.toFixed(v)&&g(A,-(q-E)))}}}}else for(b=0;b<l.length;b++)k=n[b],z="pie"===h.type?0.7*y:0.8*y,D=w.x+z*Math.cos(k.midAngle),C=w.y+z*Math.sin(k.midAngle),k.indexLabelTextBlock.x=D,k.indexLabelTextBlock.y=
+C;for(b=0;b<l.length;b++)k=n[b],D=k.indexLabelTextBlock.measureText(),0!==D.height&&0!==D.width&&(D=D=0,"right"===k.hemisphere?(D=m.x2-(k.indexLabelTextBlock.x+k.indexLabelTextBlock.width+r),D*=-1):D=m.x1-(k.indexLabelTextBlock.x-k.indexLabelTextBlock.width-r),0<D&&(Math.abs(k.indexLabelTextBlock.y-k.indexLabelTextBlock.height/2-w.y)<y||Math.abs(k.indexLabelTextBlock.y+k.indexLabelTextBlock.height/2-w.y)<y)&&(D/=Math.abs(Math.cos(k.indexLabelAngle)),9<D&&(D*=0.3),D>c&&(c=D)),D=D=0,0<k.indexLabelAngle&&
+k.indexLabelAngle<Math.PI?(D=m.y2-(k.indexLabelTextBlock.y+k.indexLabelTextBlock.height/2+5),D*=-1):D=m.y1-(k.indexLabelTextBlock.y-k.indexLabelTextBlock.height/2-5),0<D&&Math.abs(k.indexLabelTextBlock.x-w.x)<y&&(D/=Math.abs(Math.sin(k.indexLabelAngle)),9<D&&(D*=0.3),D>c&&(c=D)));b=function(a,b,c){for(var d=[],e=0;d.push(n[b]),b!==c;b=(b+1+l.length)%l.length);d.sort(function(a,b){return a.y-b.y});for(b=0;b<d.length;b++)if(c=d[b],e<a)e+=c.indexLabelTextBlock.height,c.indexLabelTextBlock.text="",c.indexLabelText=
+"",c.indexLabelTextBlock.measureText();else break};A=k=-1;for(C=E=0;C<l.length;C++)if(q=n[C],q.indexLabelText){var F=f(C);null!==F&&(u=n[F],D=0,D=e(q,u),0>D&&d(q,u)?(0>k&&(k=C),F!==k&&(A=F),E+=-D):0<E&&(b(E,k,A),A=k=-1,E=0))}0<E&&b(E,k,A)}}var p=this;if(!(0>=a.dataSeriesIndexes.length)){for(var h=this.data[a.dataSeriesIndexes[0]],l=h.dataPoints,r=10,m=this.plotArea,q=0,n=[],s=2,t=1.3,u=20/180*Math.PI,v=6,w={x:(m.x2+m.x1)/2,y:(m.y2+m.y1)/2},y="inside"===h.indexLabelPlacement?0.92*Math.min(m.width,
+m.height)/2:0.8*Math.min(m.width,m.height)/2,z=y*t,C=0,A=0;A<l.length;A++)C+=Math.abs(l[A].y);0!==C&&(this.pieDoughnutClickHandler=function(a){p.isAnimating||(a=a.dataPoint,a.exploded=a.exploded?!1:!0,1<this.dataPoints.length&&p._animator.animate(0,500,function(a){c(a)}))},b(),k(),this.disableToolTip=!0,this._animator.animate(0,this.animatedRender?this.animationDuration:0,function(a){var b=p.plotArea.ctx;b.clearRect(m.x1,m.y1,m.width,m.height);b.fillStyle=p.backgroundColor;b.fillRect(m.x1,m.y1,m.width,
+m.height);a=n[0].startAngle+2*Math.PI*a;for(b=0;b<l.length;b++){var c=0===b?n[b].startAngle:d,d=c+(n[b].endAngle-n[b].startAngle),e=!1;d>a&&(d=a,e=!0);var f=l[b].color?l[b].color:h._colorSet[b%h._colorSet.length];d>c&&pa(p.plotArea.ctx,n[b].center,n[b].radius,f,h.type,c,d,h.fillOpacity);if(e)break}},function(){p.disableToolTip=!1;p._animator.animate(0,p.animatedRender?500:0,function(a){c(a)})}))}};v.prototype.animationRequestId=null;v.prototype.requestAnimFrame=function(){return window.requestAnimationFrame||
+window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||function(a){window.setTimeout(a,1E3/60)}}();v.prototype.cancelRequestAnimFrame=window.cancelAnimationFrame||window.webkitCancelRequestAnimationFrame||window.mozCancelRequestAnimationFrame||window.oCancelRequestAnimationFrame||window.msCancelRequestAnimationFrame||clearTimeout;aa.prototype.registerSpace=function(a,b){"top"===a?this._topOccupied+=b.height:"bottom"===a?
+this._bottomOccupied+=b.height:"left"===a?this._leftOccupied+=b.width:"right"===a&&(this._rightOccupied+=b.width)};aa.prototype.unRegisterSpace=function(a,b){"top"===a?this._topOccupied-=b.height:"bottom"===a?this._bottomOccupied-=b.height:"left"===a?this._leftOccupied-=b.width:"right"===a&&(this._rightOccupied-=b.width)};aa.prototype.getFreeSpace=function(){return{x1:this._leftOccupied,y1:this._topOccupied,x2:this.chart.width-this._rightOccupied,y2:this.chart.height-this._bottomOccupied,width:this.chart.width-
+this._rightOccupied-this._leftOccupied,height:this.chart.height-this._bottomOccupied-this._topOccupied}};aa.prototype.reset=function(){this._topOccupied=0;this._bottomOccupied=3;this._rightOccupied=this._leftOccupied=0};O(I,L);I.prototype.render=function(a){a&&this.ctx.save();var b=this.ctx.font;this.ctx.textBaseline=this.textBaseline;var c=0;this._isDirty&&this.measureText(this.ctx);this.ctx.translate(this.x,this.y+c);"middle"===this.textBaseline&&(c=-this._lineHeight/2);this.ctx.font=this._getFontString();
+this.ctx.rotate(Math.PI/180*this.angle);var d=0,e=this.padding,f=null;(0<this.borderThickness&&this.borderColor||this.backgroundColor)&&this.ctx.roundRect(0,c,this.width,this.height,this.cornerRadius,this.borderThickness,this.backgroundColor,this.borderColor);this.ctx.fillStyle=this.fontColor;for(c=0;c<this._wrappedText.lines.length;c++)f=this._wrappedText.lines[c],"right"===this.horizontalAlign?d=this.width-f.width-this.padding:"left"===this.horizontalAlign?d=this.padding:"center"===this.horizontalAlign&&
+(d=(this.width-2*this.padding)/2-f.width/2+this.padding),this.ctx.fillText(f.text,d,e),e+=f.height;this.ctx.font=b;a&&this.ctx.restore()};I.prototype.setText=function(a){this.text=a;this._isDirty=!0;this._wrappedText=null};I.prototype.measureText=function(){if(null===this.maxWidth)throw"Please set maxWidth and height for TextBlock";this._wrapText(this.ctx);this._isDirty=!1;return{width:this.width,height:this.height}};I.prototype._getLineWithWidth=function(a,b,c){a=String(a);if(!a)return{text:"",width:0};
+var d=c=0,e=a.length-1,f=Infinity;for(this.ctx.font=this._getFontString();d<=e;){var f=Math.floor((d+e)/2),g=a.substr(0,f+1);c=this.ctx.measureText(g).width;if(c<b)d=f+1;else if(c>b)e=f-1;else break}c>b&&1<g.length&&(g=g.substr(0,g.length-1),c=this.ctx.measureText(g).width);b=!0;if(g.length===a.length||" "===a[g.length])b=!1;b&&(a=g.split(" "),1<a.length&&a.pop(),g=a.join(" "),c=this.ctx.measureText(g).width);return{text:g,width:c}};I.prototype._wrapText=function(){var a=new String(Z(this.text)),
+b=[],c=this.ctx.font,d=0,e=0;for(this.ctx.font=this._getFontString();0<a.length;){var f=this.maxHeight-2*this.padding,g=this._getLineWithWidth(a,this.maxWidth-2*this.padding,!1);g.height=this._lineHeight;b.push(g);e=Math.max(e,g.width);d+=g.height;a=Z(a.slice(g.text.length,a.length));f&&d>f&&(g=b.pop(),d-=g.height)}this._wrappedText={lines:b,width:e,height:d};this.width=e+2*this.padding;this.height=d+2*this.padding;this.ctx.font=c};I.prototype._getFontString=function(){return ua("",this,null)};O(ba,
+L);ba.prototype.render=function(){if(this.text){var a=this.chart.layoutManager.getFreeSpace(),b=0,c=0,d=0,e=0,f=0,g,k;"top"===this.verticalAlign||"bottom"===this.verticalAlign?(e=a.width-2*this.margin,f=0.5*a.height-2*this.margin,d=0):"center"===this.verticalAlign&&("left"===this.horizontalAlign||"right"===this.horizontalAlign?(e=a.height-2*this.margin,f=0.5*a.width-2*this.margin):"center"===this.horizontalAlign&&(e=a.width-2*this.margin,f=0.5*a.height-2*this.margin));var f=new I(this.ctx,{fontSize:this.fontSize,
+fontFamily:this.fontFamily,fontColor:this.fontColor,fontStyle:this.fontStyle,fontWeight:this.fontWeight,horizontalAlign:this.horizontalAlign,verticalAlign:this.verticalAlign,borderColor:this.borderColor,borderThickness:this.borderThickness,backgroundColor:this.backgroundColor,maxWidth:e,maxHeight:f,cornerRadius:this.cornerRadius,text:this.text,padding:this.padding,textBaseline:"top"}),p=f.measureText();"top"===this.verticalAlign||"bottom"===this.verticalAlign?("top"===this.verticalAlign?(c=this.margin,
+k="top"):"bottom"===this.verticalAlign&&(c=a.y2-this.margin-p.height,k="bottom"),"left"===this.horizontalAlign?b=a.x1+this.margin:"center"===this.horizontalAlign?b=a.x1+(e/2-p.width/2)+this.margin:"right"===this.horizontalAlign&&(b=a.x2-this.margin-p.width),g=this.horizontalAlign,this.width=p.width,this.height=p.height):"center"===this.verticalAlign&&("left"===this.horizontalAlign?(b=a.x1+this.margin,c=a.y2-this.margin-(e/2-p.width/2),d=-90,k="left",this.width=p.height,this.height=p.width):"right"===
+this.horizontalAlign?(b=a.x2-this.margin,c=a.y1+this.margin+(e/2-p.width/2),d=90,k="right",this.width=p.height,this.height=p.width):"center"===this.horizontalAlign&&(c=a.y1+(a.height/2-p.height/2),b=a.x1+(a.width/2-p.width/2),k="center",this.width=p.width,this.height=p.height),g="center");f.x=b;f.y=c;f.angle=d;f.horizontalAlign=g;f.render(!0);this.chart.layoutManager.registerSpace(k,{width:this.width+2*this.margin,height:this.height+2*this.margin});this.bounds={x1:b,y1:c,x2:b+this.width,y2:c+this.height};
+this.ctx.textBaseline="top"}};O(ga,L);ga.prototype.render=function(){var a=this.chart.layoutManager.getFreeSpace(),b=null,c=0,d=0,e=0,f=0,g=[],k=[];"top"===this.verticalAlign||"bottom"===this.verticalAlign?(this.orientation="horizontal",b=this.verticalAlign,e=0.9*a.width,f=0.5*a.height):"center"===this.verticalAlign&&(this.orientation="vertical",b=this.horizontalAlign,e=0.5*a.width,f=0.9*a.height);for(var p=0;p<this.dataSeries.length;p++){var h=this.dataSeries[p],l=h.legendMarkerType?h.legendMarkerType:
+"line"!==h.type&&"stepLine"!==h.type&&"spline"!==h.type&&"scatter"!==h.type&&"bubble"!==h.type||!h.markerType?P.getDefaultLegendMarker(h.type):h.markerType,r=h.legendText?h.legendText:h.name,m=h.legendMarkerColor?h.legendMarkerColor:h.markerColor?h.markerColor:h._colorSet[0],q=h.markerSize||"line"!==h.type&&"stepLine"!==h.type&&"spline"!==h.type?0.6*this.lineHeight:0,n=h.legendMarkerBorderColor?h.legendMarkerBorderColor:h.markerBorderColor,s=h.legendMarkerBorderThickness?h.legendMarkerBorderThickness:
+h.markerBorderThickness?Math.max(1,Math.round(0.2*q)):0;if("pie"!==h.type&&"doughnut"!==h.type&&"funnel"!==h.type)l={markerType:l,markerColor:m,text:r,textBlock:null,chartType:h.type,markerSize:q,lineColor:h._colorSet[0],dataSeriesIndex:h.index,dataPointIndex:null,markerBorderColor:n,markerBorderThickness:s},g.push(l);else for(var t=0;t<h.dataPoints.length;t++)s=h.dataPoints[t],l=s.legendMarkerType?s.legendMarkerType:h.legendMarkerType?h.legendMarkerType:P.getDefaultLegendMarker(h.type),r=s.legendText?
+s.legendText:h.legendText?h.legendText:s.name?s.name:"DataPoint: "+(t+1),m=s.legendMarkerColor?s.legendMarkerColor:h.legendMarkerColor?h.legendMarkerColor:s.color?s.color:h.color?h.color:h._colorSet[t%h._colorSet.length],q=0===s.markerSize||0===h.markerSize&&!s.markerSize?0:0.6*this.lineHeight,n=s.legendMarkerBorderColor?s.legendMarkerBorderColor:h.legendMarkerBorderColor?h.legendMarkerBorderColor:s.markerBorderColor?s.markerBorderColor:h.markerBorderColor,s=s.legendMarkerBorderThickness?s.legendMarkerBorderThickness:
+h.legendMarkerBorderThickness?h.legendMarkerBorderThickness:s.markerBorderThickness||h.markerBorderThickness?Math.max(1,Math.round(0.2*q)):0,l={markerType:l,markerColor:m,text:r,textBlock:null,chartType:h.type,markerSize:q,dataSeriesIndex:p,dataPointIndex:t,markerBorderColor:n,markerBorderThickness:s},g.push(l)}if(0<g.length){h=null;for(p=t=0;p<g.length;p++){l=g[p];if("horizontal"===this.orientation){l.textBlock=new I(this.ctx,{x:0,y:0,maxWidth:e,maxHeight:this.lineHeight,angle:0,text:l.text,horizontalAlign:"left",
+fontSize:this.fontSize,fontFamily:this.fontFamily,fontWeight:this.fontWeight,fontColor:this.fontColor,fontStyle:this.fontStyle,textBaseline:"top"});l.textBlock.measureText();if(!h||h.width+l.textBlock.width+(0===h.width?0:this.horizontalSpacing)>e)h={items:[],width:0},k.push(h),this.height=k.length*(l.textBlock.height+5);l.textBlock.x=h.width;l.textBlock.y=0;h.width+=Math.round(l.textBlock.width+l.textBlock._lineHeight+(0===h.width?0:0.5*l.textBlock._lineHeight))}else this.height+this.lineHeight<
+f?(h={items:[],width:0},k.push(h),this.height=k.length*this.lineHeight):(h=k[t],t=(t+1)%k.length),l.textBlock=new I(this.ctx,{x:0,y:0,maxWidth:e,maxHeight:1.5*this.fontSize,angle:0,text:l.text,horizontalAlign:"left",fontSize:this.fontSize,fontFamily:this.fontFamily,fontWeight:this.fontWeight,fontColor:this.fontColor,fontStyle:this.fontStyle,textBaseline:"top"}),l.textBlock.measureText(),l.textBlock.x=h.width,l.textBlock.y=0,h.width+=l.textBlock.width+l.textBlock._lineHeight+(0===h.width?0:0.5*l.textBlock._lineHeight);
+h.items.push(l);this.width=Math.max(h.width,this.width)}this.height=k.length*this.lineHeight}"top"===this.verticalAlign?(d="left"===this.horizontalAlign?a.x1+2:"right"===this.horizontalAlign?a.x2-this.width-2:a.x1+a.width/2-this.width/2,c=a.y1):"center"===this.verticalAlign?(d="left"===this.horizontalAlign?a.x1+2:"right"===this.horizontalAlign?a.x2-this.width-2:a.x1+a.width/2-this.width/2,c=a.y1+a.height/2-this.height/2):"bottom"===this.verticalAlign&&(d="left"===this.horizontalAlign?a.x1+2:"right"===
+this.horizontalAlign?a.x2-this.width-2:a.x1+a.width/2-this.width/2,c=a.y2-this.height-5);this.items=g;for(p=0;p<this.items.length;p++)l=g[p],l.id=++this.chart._eventManager.lastObjectId,this.chart._eventManager.objectMap[l.id]={id:l.id,objectType:"legendItem",legendItemIndex:p,dataSeriesIndex:l.dataSeriesIndex,dataPointIndex:l.dataPointIndex};for(p=0;p<k.length;p++)for(h=k[p],a=0;a<h.items.length;a++){l=h.items[a];f=l.textBlock.x+d+(0===a?0.2*l.markerSize:0.4*this.lineHeight+0.2*l.markerSize);g=c+
+p*this.lineHeight;e=f;this.chart.data[l.dataSeriesIndex].visible||(this.ctx.globalAlpha=0.5);if("line"===l.chartType||"stepLine"===l.chartType||"spline"===l.chartType)this.ctx.strokeStyle=l.lineColor,this.ctx.lineWidth=Math.ceil(this.lineHeight/8),this.ctx.beginPath(),this.ctx.moveTo(f-0.1*this.lineHeight,g+this.lineHeight/2),this.ctx.lineTo(f+0.7*this.lineHeight,g+this.lineHeight/2),this.ctx.stroke(),e-=0.1*this.lineHeight;J.drawMarker(f+l.markerSize/2,g+this.lineHeight/2,this.ctx,l.markerType,l.markerSize,
+l.markerColor,l.markerBorderColor,l.markerBorderThickness);l.textBlock.x=f+Math.round(0.9*this.lineHeight);l.textBlock.y=g;l.textBlock.render(!0);this.chart.data[l.dataSeriesIndex].visible||(this.ctx.globalAlpha=1);f=C(l.id);this.ghostCtx.fillStyle=f;this.ghostCtx.beginPath();this.ghostCtx.fillRect(e,l.textBlock.y,l.textBlock.x+l.textBlock.width-e,l.textBlock.height);l.x1=this.chart._eventManager.objectMap[l.id].x1=e;l.y1=this.chart._eventManager.objectMap[l.id].y1=l.textBlock.y;l.x2=this.chart._eventManager.objectMap[l.id].x2=
+l.textBlock.x+l.textBlock.width;l.y2=this.chart._eventManager.objectMap[l.id].y2=l.textBlock.y+l.textBlock.height}this.chart.layoutManager.registerSpace(b,{width:this.width+2+2,height:this.height+5+5});this.bounds={x1:d,y1:c,x2:d+this.width,y2:c+this.height}};O(la,L);la.prototype.render=function(){var a=this.chart.layoutManager.getFreeSpace();this.ctx.fillStyle="red";this.ctx.fillRect(a.x1,a.y1,a.x2,a.y2)};O(P,L);P.prototype.getDefaultAxisPlacement=function(){var a=this.type;if("column"===a||"line"===
+a||"stepLine"===a||"spline"===a||"area"===a||"stepArea"===a||"splineArea"===a||"stackedColumn"===a||"stackedLine"===a||"bubble"===a||"scatter"===a||"stackedArea"===a||"stackedColumn100"===a||"stackedLine100"===a||"stackedArea100"===a||"candlestick"===a||"ohlc"===a||"rangeColumn"===a||"rangeArea"===a||"rangeSplineArea"===a)return"normal";if("bar"===a||"stackedBar"===a||"stackedBar100"===a||"rangeBar"===a)return"xySwapped";if("pie"===a||"doughnut"===a||"funnel"===a)return"none";window.console.log("Unknown Chart Type: "+
+a);return null};P.getDefaultLegendMarker=function(a){if("column"===a||"stackedColumn"===a||"stackedLine"===a||"bar"===a||"stackedBar"===a||"stackedBar100"===a||"bubble"===a||"scatter"===a||"stackedColumn100"===a||"stackedLine100"===a||"stepArea"===a||"candlestick"===a||"ohlc"===a||"rangeColumn"===a||"rangeBar"===a||"rangeArea"===a||"rangeSplineArea"===a)return"square";if("line"===a||"stepLine"===a||"spline"===a||"pie"===a||"doughnut"===a||"funnel"===a)return"circle";if("area"===a||"splineArea"===
+a||"stackedArea"===a||"stackedArea100"===a)return"triangle";window.console.log("Unknown Chart Type: "+a);return null};P.prototype.getDataPointAtX=function(a,b){if(!this.dataPoints||0===this.dataPoints.length)return null;var c={dataPoint:null,distance:Infinity,index:NaN},d=null,e=0,f=0,g=1,k=Infinity,p=0,h=0,l=0;"none"!==this.chart.plotInfo.axisPlacement&&(l=this.dataPoints[this.dataPoints.length-1].x-this.dataPoints[0].x,l=0<l?Math.min(Math.max((this.dataPoints.length-1)/l*(a-this.dataPoints[0].x)>>
+0,0),this.dataPoints.length):0);for(;;){f=0<g?l+e:l-e;if(0<=f&&f<this.dataPoints.length){var d=this.dataPoints[f],r=Math.abs(d.x-a);r<c.distance&&(c.dataPoint=d,c.distance=r,c.index=f);d=Math.abs(d.x-a);d<=k?k=d:0<g?p++:h++;if(1E3<p&&1E3<h)break}else if(0>l-e&&l+e>=this.dataPoints.length)break;-1===g?(e++,g=1):g=-1}return b||c.dataPoint.x!==a?b&&null!==c.dataPoint?c:null:c};P.prototype.getDataPointAtXY=function(a,b,c){if(!this.dataPoints||0===this.dataPoints.length)return null;c=c||!1;var d=[],e=
+0,f=0,g=1,k=!1,p=Infinity,h=0,l=0,r=0;"none"!==this.chart.plotInfo.axisPlacement&&(r=this.chart.axisX.getXValueAt({x:a,y:b}),f=this.dataPoints[this.dataPoints.length-1].x-this.dataPoints[0].x,r=0<f?Math.min(Math.max((this.dataPoints.length-1)/f*(r-this.dataPoints[0].x)>>0,0),this.dataPoints.length):0);for(;;){f=0<g?r+e:r-e;if(0<=f&&f<this.dataPoints.length){var m=this.chart._eventManager.objectMap[this.dataPointIds[f]],q=this.dataPoints[f],n=null;if(m){switch(this.type){case "column":case "stackedColumn":case "stackedColumn100":case "bar":case "stackedBar":case "stackedBar100":case "rangeColumn":case "rangeBar":a>=
+m.x1&&(a<=m.x2&&b>=m.y1&&b<=m.y2)&&(d.push({dataPoint:q,dataPointIndex:f,dataSeries:this,distance:Math.min(Math.abs(m.x1-a),Math.abs(m.x2-a),Math.abs(m.y1-b),Math.abs(m.y2-b))}),k=!0);break;case "line":case "stepLine":case "spline":case "area":case "stepArea":case "stackedArea":case "stackedArea100":case "splineArea":case "scatter":var s=T("markerSize",q,this)||4,t=c?20:s,n=Math.sqrt(Math.pow(m.x1-a,2)+Math.pow(m.y1-b,2));n<=t&&d.push({dataPoint:q,dataPointIndex:f,dataSeries:this,distance:n});f=Math.abs(m.x1-
+a);f<=p?p=f:0<g?h++:l++;n<=s/2&&(k=!0);break;case "rangeArea":case "rangeSplineArea":s=T("markerSize",q,this)||4;t=c?20:s;n=Math.min(Math.sqrt(Math.pow(m.x1-a,2)+Math.pow(m.y1-b,2)),Math.sqrt(Math.pow(m.x1-a,2)+Math.pow(m.y2-b,2)));n<=t&&d.push({dataPoint:q,dataPointIndex:f,dataSeries:this,distance:n});f=Math.abs(m.x1-a);f<=p?p=f:0<g?h++:l++;n<=s/2&&(k=!0);break;case "bubble":s=m.size;n=Math.sqrt(Math.pow(m.x1-a,2)+Math.pow(m.y1-b,2));n<=s/2&&(d.push({dataPoint:q,dataPointIndex:f,dataSeries:this,
+distance:n}),k=!0);break;case "pie":case "doughnut":s=m.center;t="doughnut"===this.type?0.6*m.radius:0;n=Math.sqrt(Math.pow(s.x-a,2)+Math.pow(s.y-b,2));n<m.radius&&n>t&&(n=Math.atan2(b-s.y,a-s.x),0>n&&(n+=2*Math.PI),n=Number(((180*(n/Math.PI)%360+360)%360).toFixed(12)),s=Number(((180*(m.startAngle/Math.PI)%360+360)%360).toFixed(12)),t=Number(((180*(m.endAngle/Math.PI)%360+360)%360).toFixed(12)),0===t&&1<m.endAngle&&(t=360),s>=t&&0!==q.y&&(t+=360,n<s&&(n+=360)),n>s&&n<t&&(d.push({dataPoint:q,dataPointIndex:f,
+dataSeries:this,distance:0}),k=!0));break;case "candlestick":if(a>=m.x1-m.borderThickness/2&&a<=m.x2+m.borderThickness/2&&b>=m.y2-m.borderThickness/2&&b<=m.y3+m.borderThickness/2||Math.abs(m.x2-a+m.x1-a)<m.borderThickness&&b>=m.y1&&b<=m.y4)d.push({dataPoint:q,dataPointIndex:f,dataSeries:this,distance:Math.min(Math.abs(m.x1-a),Math.abs(m.x2-a),Math.abs(m.y2-b),Math.abs(m.y3-b))}),k=!0;break;case "ohlc":if(Math.abs(m.x2-a+m.x1-a)<m.borderThickness&&b>=m.y2&&b<=m.y3||a>=m.x1&&a<=(m.x2+m.x1)/2&&b>=m.y1-
+m.borderThickness/2&&b<=m.y1+m.borderThickness/2||a>=(m.x1+m.x2)/2&&a<=m.x2&&b>=m.y4-m.borderThickness/2&&b<=m.y4+m.borderThickness/2)d.push({dataPoint:q,dataPointIndex:f,dataSeries:this,distance:Math.min(Math.abs(m.x1-a),Math.abs(m.x2-a),Math.abs(m.y2-b),Math.abs(m.y3-b))}),k=!0}if(k||1E3<h&&1E3<l)break}}else if(0>r-e&&r+e>=this.dataPoints.length)break;-1===g?(e++,g=1):g=-1}a=null;for(b=0;b<d.length;b++)a?d[b].distance<=a.distance&&(a=d[b]):a=d[b];return a};P.prototype.getMarkerProperties=function(a,
+b,c,d){var e=this.dataPoints;return{x:b,y:c,ctx:d,type:e[a].markerType?e[a].markerType:this.markerType,size:e[a].markerSize?e[a].markerSize:this.markerSize,color:e[a].markerColor?e[a].markerColor:this.markerColor?this.markerColor:e[a].color?e[a].color:this.color?this.color:this._colorSet[a%this._colorSet.length],borderColor:e[a].markerBorderColor?e[a].markerBorderColor:this.markerBorderColor?this.markerBorderColor:null,borderThickness:e[a].markerBorderThickness?e[a].markerBorderThickness:this.markerBorderThickness?
+this.markerBorderThickness:null}};O(A,L);A.prototype.createLabels=function(){var a,b=0,c,d=0,e=0,b=0;if("bottom"===this._position||"top"===this._position)b=this.lineCoordinates.width/Math.abs(this.maximum-this.minimum)*this.interval,d=this.labelAutoFit?"undefined"===typeof this._options.labelMaxWidth?0.9*b>>0:this.labelMaxWidth:"undefined"===typeof this._options.labelMaxWidth?0.7*this.chart.width>>0:this.labelMaxWidth,e="undefined"===typeof this._options.labelWrap||this.labelWrap?0.5*this.chart.height>>
+0:1.5*this.labelFontSize;else if("left"===this._position||"right"===this._position)b=this.lineCoordinates.height/Math.abs(this.maximum-this.minimum)*this.interval,d=this.labelAutoFit?"undefined"===typeof this._options.labelMaxWidth?0.3*this.chart.width>>0:this.labelMaxWidth:"undefined"===typeof this._options.labelMaxWidth?0.5*this.chart.width>>0:this.labelMaxWidth,e="undefined"===typeof this._options.labelWrap||this.labelWrap?2*b>>0:1.5*this.labelFontSize;if("axisX"===this.type&&"dateTime"===this.chart.plotInfo.axisXValueType)for(c=
+qa(new Date(this.maximum),this.interval,this.intervalType),b=this.intervalstartTimePercent;b<c;qa(b,this.interval,this.intervalType))a=b.getTime(),a="axisX"===this.type&&this.labels[a]?this.labels[a]:ya(b,this.valueFormatString,this.chart._cultureInfo),a=new I(this.ctx,{x:0,y:0,maxWidth:d,maxHeight:e,angle:this.labelAngle,text:this.prefix+a+this.suffix,horizontalAlign:"left",fontSize:this.labelFontSize,fontFamily:this.labelFontFamily,fontWeight:this.labelFontWeight,fontColor:this.labelFontColor,fontStyle:this.labelFontStyle,
+textBaseline:"middle"}),this._labels.push({position:b.getTime(),textBlock:a,effectiveHeight:null});else{c=this.maximum;if(this.labels&&this.labels.length){a=Math.ceil(this.interval);for(var f=Math.ceil(this.intervalstartTimePercent),g=!1,b=f;b<this.maximum;b+=a)if(this.labels[b])g=!0;else{g=!1;break}g&&(this.interval=a,this.intervalstartTimePercent=f)}for(b=this.intervalstartTimePercent;b<=c;b=parseFloat((b+this.interval).toFixed(14)))a="axisX"===this.type&&this.labels[b]?this.labels[b]:X(b,this.valueFormatString,
+this.chart._cultureInfo),a=new I(this.ctx,{x:0,y:0,maxWidth:d,maxHeight:e,angle:this.labelAngle,text:this.prefix+a+this.suffix,horizontalAlign:"left",fontSize:this.labelFontSize,fontFamily:this.labelFontFamily,fontWeight:this.labelFontWeight,fontColor:this.labelFontColor,fontStyle:this.labelFontStyle,textBaseline:"middle",borderThickness:0}),this._labels.push({position:b,textBlock:a,effectiveHeight:null})}for(b=0;b<this.stripLines.length;b++)c=this.stripLines[b],a=new I(this.ctx,{x:0,y:0,backgroundColor:c.labelBackgroundColor,
+maxWidth:d,maxHeight:e,angle:this.labelAngle,text:c.label,horizontalAlign:"left",fontSize:c.labelFontSize,fontFamily:c.labelFontFamily,fontWeight:c.labelFontWeight,fontColor:c.labelFontColor,fontStyle:c.labelFontStyle,textBaseline:"middle",borderThickness:0}),this._labels.push({position:c.value,textBlock:a,effectiveHeight:null,stripLine:c})};A.prototype.createLabelsAndCalculateWidth=function(){var a=0;this._labels=[];if("left"===this._position||"right"===this._position)for(this.createLabels(),i=0;i<
+this._labels.length;i++){var b=this._labels[i].textBlock.measureText(),c=0,c=0===this.labelAngle?b.width:b.width*Math.cos(Math.PI/180*Math.abs(this.labelAngle))+b.height/2*Math.sin(Math.PI/180*Math.abs(this.labelAngle));a<c&&(a=c);this._labels[i].effectiveWidth=c}return(this.title?ca(this.titleFontFamily,this.titleFontSize,this.titleFontWeight)+2:0)+a+this.tickLength+5};A.prototype.createLabelsAndCalculateHeight=function(){var a=0;this._labels=[];var b,c=0;this.createLabels();if("bottom"===this._position||
+"top"===this._position)for(c=0;c<this._labels.length;c++){b=this._labels[c].textBlock;b=b.measureText();var d=0,d=0===this.labelAngle?b.height:b.width*Math.sin(Math.PI/180*Math.abs(this.labelAngle))+b.height/2*Math.cos(Math.PI/180*Math.abs(this.labelAngle));a<d&&(a=d);this._labels[c].effectiveHeight=d}return(this.title?ca(this.titleFontFamily,this.titleFontSize,this.titleFontWeight)+2:0)+a+this.tickLength+5};A.setLayoutAndRender=function(a,b,c,d,e){var f,g,k,p=a.chart,h=p.ctx;a.calculateAxisParameters();
+b&&b.calculateAxisParameters();c&&c.calculateAxisParameters();if(b&&c&&"undefined"===typeof b._options.maximum&&"undefined"===typeof b._options.minimum&&"undefined"===typeof b._options.interval&&"undefined"===typeof c._options.maximum&&"undefined"===typeof c._options.minimum&&"undefined"===typeof c._options.interval){k=(b.maximum-b.minimum)/b.interval;var l=(c.maximum-c.minimum)/c.interval;k>l?c.maximum=c.interval*k+c.minimum:l>k&&(b.maximum=b.interval*l+b.minimum)}l=b?b.margin:0;if("normal"===d){a.lineCoordinates=
+{};var r=Math.ceil(b?b.createLabelsAndCalculateWidth():0);f=Math.round(e.x1+r+l);a.lineCoordinates.x1=f;l=Math.ceil(c?c.createLabelsAndCalculateWidth():0);g=Math.round(e.x2-l>a.chart.width-10?a.chart.width-10:e.x2-l);a.lineCoordinates.x2=g;a.lineCoordinates.width=Math.abs(g-f);var m=Math.ceil(a.createLabelsAndCalculateHeight());d=Math.round(e.y2-m-a.margin);k=Math.round(e.y2-a.margin);a.lineCoordinates.y1=d;a.lineCoordinates.y2=d;a.boundingRect={x1:f,y1:d,x2:g,y2:k,width:g-f,height:k-d};b&&(f=Math.round(e.x1+
+b.margin),d=Math.round(10>e.y1?10:e.y1),g=Math.round(e.x1+r+b.margin),k=Math.round(e.y2-m-a.margin),b.lineCoordinates={x1:g,y1:d,x2:g,y2:k,height:Math.abs(k-d)},b.boundingRect={x1:f,y1:d,x2:g,y2:k,width:g-f,height:k-d});c&&(f=Math.round(a.lineCoordinates.x2),d=Math.round(10>e.y1?10:e.y1),g=Math.round(f+l+c.margin),k=Math.round(e.y2-m-a.margin),c.lineCoordinates={x1:f,y1:d,x2:f,y2:k,height:Math.abs(k-d)},c.boundingRect={x1:f,y1:d,x2:g,y2:k,width:g-f,height:k-d});a.calculateValueToPixelconversionParameters();
+b&&b.calculateValueToPixelconversionParameters();c&&c.calculateValueToPixelconversionParameters();h.save();h.rect(5,a.boundingRect.y1,a.chart.width-10,a.boundingRect.height);h.clip();a.renderLabelsTicksAndTitle();h.restore();b&&b.renderLabelsTicksAndTitle();c&&c.renderLabelsTicksAndTitle()}else{r=Math.ceil(a.createLabelsAndCalculateWidth());b&&(b.lineCoordinates={},f=Math.round(e.x1+r+a.margin),g=Math.round(e.x2>b.chart.width-10?b.chart.width-10:e.x2),b.lineCoordinates.x1=f,b.lineCoordinates.x2=g,
+b.lineCoordinates.width=Math.abs(g-f));c&&(c.lineCoordinates={},f=Math.round(e.x1+r+a.margin),g=Math.round(e.x2>c.chart.width-10?c.chart.width-10:e.x2),c.lineCoordinates.x1=f,c.lineCoordinates.x2=g,c.lineCoordinates.width=Math.abs(g-f));var m=Math.ceil(b?b.createLabelsAndCalculateHeight():0),q=Math.ceil(c?c.createLabelsAndCalculateHeight():0);b&&(d=Math.round(e.y2-m-b.margin),k=Math.round(e.y2-l>b.chart.height-10?b.chart.height-10:e.y2-l),b.lineCoordinates.y1=d,b.lineCoordinates.y2=d,b.boundingRect=
+{x1:f,y1:d,x2:g,y2:k,width:g-f,height:m});c&&(d=Math.round(e.y1+c.margin),k=e.y1+c.margin+q,c.lineCoordinates.y1=k,c.lineCoordinates.y2=k,c.boundingRect={x1:f,y1:d,x2:g,y2:k,width:g-f,height:q});f=Math.round(e.x1+a.margin);d=Math.round(c?c.lineCoordinates.y2:10>e.y1?10:e.y1);g=Math.round(e.x1+r+a.margin);k=Math.round(b?b.lineCoordinates.y1:e.y2-l>a.chart.height-10?a.chart.height-10:e.y2-l);a.lineCoordinates={x1:g,y1:d,x2:g,y2:k,height:Math.abs(k-d)};a.boundingRect={x1:f,y1:d,x2:g,y2:k,width:g-f,height:k-
+d};a.calculateValueToPixelconversionParameters();b&&b.calculateValueToPixelconversionParameters();c&&c.calculateValueToPixelconversionParameters();b&&b.renderLabelsTicksAndTitle();c&&c.renderLabelsTicksAndTitle();a.renderLabelsTicksAndTitle()}p.preparePlotArea();e=a.chart.plotArea;h.save();h.rect(e.x1,e.y1,Math.abs(e.x2-e.x1),Math.abs(e.y2-e.y1));h.clip();a.renderStripLinesOfThicknessType("value");b&&b.renderStripLinesOfThicknessType("value");c&&c.renderStripLinesOfThicknessType("value");a.renderInterlacedColors();
+b&&b.renderInterlacedColors();c&&c.renderInterlacedColors();h.restore();a.renderGrid();b&&b.renderGrid();c&&c.renderGrid();a.renderAxisLine();b&&b.renderAxisLine();c&&c.renderAxisLine();a.renderStripLinesOfThicknessType("pixel");b&&b.renderStripLinesOfThicknessType("pixel");c&&c.renderStripLinesOfThicknessType("pixel")};A.prototype.renderLabelsTicksAndTitle=function(){var a=!1,b=0,c=1,d=0;0!==this.labelAngle&&360!==this.labelAngle&&(c=1.2);if("undefined"===typeof this._options.interval){if("bottom"===
+this._position||"top"===this._position){for(e=0;e<this._labels.length;e++)f=this._labels[e],f.position<this.minimum||f.stripLine||(f=f.textBlock.width*Math.cos(Math.PI/180*this.labelAngle)+f.textBlock.height*Math.sin(Math.PI/180*this.labelAngle),b+=f);b>this.lineCoordinates.width*c&&(a=!0)}if("left"===this._position||"right"===this._position){for(e=0;e<this._labels.length;e++)f=this._labels[e],f.position<this.minimum||f.stripLine||(f=f.textBlock.height*Math.cos(Math.PI/180*this.labelAngle)+f.textBlock.width*
+Math.sin(Math.PI/180*this.labelAngle),b+=f);b>this.lineCoordinates.height*c&&(a=!0)}}if("bottom"===this._position){for(var e=0,f,e=0;e<this._labels.length;e++)if(f=this._labels[e],!(f.position<this.minimum||f.position>this.maximum)){b=this.getPixelCoordinatesOnAxis(f.position);if(this.tickThickness&&!this._labels[e].stripLine||this._labels[e].stripLine&&"pixel"===this._labels[e].stripLine._thicknessType)this._labels[e].stripLine?(c=this._labels[e].stripLine,this.ctx.lineWidth=c.thickness,this.ctx.strokeStyle=
+c.color):(this.ctx.lineWidth=this.tickThickness,this.ctx.strokeStyle=this.tickColor),c=1===this.ctx.lineWidth%2?(b.x<<0)+0.5:b.x<<0,this.ctx.beginPath(),this.ctx.moveTo(c,b.y<<0),this.ctx.lineTo(c,b.y+this.tickLength<<0),this.ctx.stroke();if(!a||0===d++%2||this._labels[e].stripLine)0===f.textBlock.angle?(b.x-=f.textBlock.width/2,b.y+=this.tickLength+f.textBlock.fontSize/2):(b.x-=0>this.labelAngle?f.textBlock.width*Math.cos(Math.PI/180*this.labelAngle):0,b.y+=this.tickLength+Math.abs(0>this.labelAngle?
+f.textBlock.width*Math.sin(Math.PI/180*this.labelAngle)-5:5)),f.textBlock.x=b.x,f.textBlock.y=b.y,f.textBlock.render(!0)}this.title&&(this._titleTextBlock=new I(this.ctx,{x:this.lineCoordinates.x1,y:this.boundingRect.y2-this.titleFontSize-5,maxWidth:this.lineCoordinates.width,maxHeight:1.5*this.titleFontSize,angle:0,text:this.title,horizontalAlign:"center",fontSize:this.titleFontSize,fontFamily:this.titleFontFamily,fontWeight:this.titleFontWeight,fontColor:this.titleFontColor,fontStyle:this.titleFontStyle,
+textBaseline:"top"}),this._titleTextBlock.measureText(),this._titleTextBlock.x=this.lineCoordinates.x1+this.lineCoordinates.width/2-this._titleTextBlock.width/2,this._titleTextBlock.y=this.boundingRect.y2-this._titleTextBlock.height-3,this._titleTextBlock.render(!0))}else if("top"===this._position){for(e=0;e<this._labels.length;e++)if(f=this._labels[e],!(f.position<this.minimum||f.position>this.maximum)){b=this.getPixelCoordinatesOnAxis(f.position);if(this.tickThickness&&!this._labels[e].stripLine||
+this._labels[e].stripLine&&"pixel"===this._labels[e].stripLine._thicknessType)this._labels[e].stripLine?(c=this._labels[e].stripLine,this.ctx.lineWidth=c.thickness,this.ctx.strokeStyle=c.color):(this.ctx.lineWidth=this.tickThickness,this.ctx.strokeStyle=this.tickColor),c=1===this.ctx.lineWidth%2?(b.x<<0)+0.5:b.x<<0,this.ctx.beginPath(),this.ctx.moveTo(c,b.y<<0),this.ctx.lineTo(c,b.y-this.tickLength<<0),this.ctx.stroke();if(!a||0===d++%2||this._labels[e].stripLine)0===f.textBlock.angle?(b.x-=f.textBlock.width/
+2,b.y-=this.tickLength+f.textBlock.height/2):(b.x-=0<this.labelAngle?f.textBlock.width*Math.cos(Math.PI/180*this.labelAngle):0,b.y-=this.tickLength+Math.abs(0<this.labelAngle?f.textBlock.width*Math.sin(Math.PI/180*this.labelAngle)+5:5)),f.textBlock.x=b.x,f.textBlock.y=b.y,f.textBlock.render(!0)}this.title&&(this._titleTextBlock=new I(this.ctx,{x:this.lineCoordinates.x1,y:this.boundingRect.y1+1,maxWidth:this.lineCoordinates.width,maxHeight:1.5*this.titleFontSize,angle:0,text:this.title,horizontalAlign:"center",
+fontSize:this.titleFontSize,fontFamily:this.titleFontFamily,fontWeight:this.titleFontWeight,fontColor:this.titleFontColor,fontStyle:this.titleFontStyle,textBaseline:"top"}),this._titleTextBlock.measureText(),this._titleTextBlock.x=this.lineCoordinates.x1+this.lineCoordinates.width/2-this._titleTextBlock.width/2,this._titleTextBlock.render(!0))}else if("left"===this._position){for(e=0;e<this._labels.length;e++)if(f=this._labels[e],!(f.position<this.minimum||f.position>this.maximum)){b=this.getPixelCoordinatesOnAxis(f.position);
+if(this.tickThickness&&!this._labels[e].stripLine||this._labels[e].stripLine&&"pixel"===this._labels[e].stripLine._thicknessType)this._labels[e].stripLine?(c=this._labels[e].stripLine,this.ctx.lineWidth=c.thickness,this.ctx.strokeStyle=c.color):(this.ctx.lineWidth=this.tickThickness,this.ctx.strokeStyle=this.tickColor),c=1===this.ctx.lineWidth%2?(b.y<<0)+0.5:b.y<<0,this.ctx.beginPath(),this.ctx.moveTo(b.x<<0,c),this.ctx.lineTo(b.x-this.tickLength<<0,c),this.ctx.stroke();if(!a||0===d++%2||this._labels[e].stripLine)f.textBlock.x=
+b.x-f.textBlock.width*Math.cos(Math.PI/180*this.labelAngle)-this.tickLength-5,f.textBlock.y=0===this.labelAngle?b.y-f.textBlock.height/2+this.labelFontSize/2:b.y-f.textBlock.width*Math.sin(Math.PI/180*this.labelAngle),f.textBlock.render(!0)}this.title&&(this._titleTextBlock=new I(this.ctx,{x:this.boundingRect.x1+1,y:this.lineCoordinates.y2,maxWidth:this.lineCoordinates.height,maxHeight:1.5*this.titleFontSize,angle:-90,text:this.title,horizontalAlign:"center",fontSize:this.titleFontSize,fontFamily:this.titleFontFamily,
+fontWeight:this.titleFontWeight,fontColor:this.titleFontColor,fontStyle:this.titleFontStyle,textBaseline:"top"}),this._titleTextBlock.measureText(),this._titleTextBlock.y=this.lineCoordinates.height/2+this._titleTextBlock.width/2+this.lineCoordinates.y1,this._titleTextBlock.render(!0))}else if("right"===this._position){for(e=0;e<this._labels.length;e++)if(f=this._labels[e],!(f.position<this.minimum||f.position>this.maximum)){b=this.getPixelCoordinatesOnAxis(f.position);if(this.tickThickness&&!this._labels[e].stripLine||
+this._labels[e].stripLine&&"pixel"===this._labels[e].stripLine._thicknessType)this._labels[e].stripLine?(c=this._labels[e].stripLine,this.ctx.lineWidth=c.thickness,this.ctx.strokeStyle=c.color):(this.ctx.lineWidth=this.tickThickness,this.ctx.strokeStyle=this.tickColor),c=1===this.ctx.lineWidth%2?(b.y<<0)+0.5:b.y<<0,this.ctx.beginPath(),this.ctx.moveTo(b.x<<0,c),this.ctx.lineTo(b.x+this.tickLength<<0,c),this.ctx.stroke();if(!a||0===d++%2||this._labels[e].stripLine)f.textBlock.x=b.x+this.tickLength+
+5,f.textBlock.y=0===this.labelAngle?b.y-f.textBlock.height/2+this.labelFontSize/2:b.y,f.textBlock.render(!0)}this.title&&(this._titleTextBlock=new I(this.ctx,{x:this.boundingRect.x2-1,y:this.lineCoordinates.y2,maxWidth:this.lineCoordinates.height,maxHeight:1.5*this.titleFontSize,angle:90,text:this.title,horizontalAlign:"center",fontSize:this.titleFontSize,fontFamily:this.titleFontFamily,fontWeight:this.titleFontWeight,fontColor:this.titleFontColor,fontStyle:this.titleFontStyle,textBaseline:"top"}),
+this._titleTextBlock.measureText(),this._titleTextBlock.y=this.lineCoordinates.height/2-this._titleTextBlock.width/2+this.lineCoordinates.y1,this._titleTextBlock.render(!0))}};A.prototype.renderInterlacedColors=function(){var a=this.chart.plotArea.ctx,b,c,d=this.chart.plotArea,e=0;b=!0;if(("bottom"===this._position||"top"===this._position)&&this.interlacedColor)for(a.fillStyle=this.interlacedColor,e=0;e<this._labels.length;e++)this._labels[e].stripLine||(b?(b=this.getPixelCoordinatesOnAxis(this._labels[e].position),
+c=e+1>=this._labels.length?this.getPixelCoordinatesOnAxis(this.maximum):this.getPixelCoordinatesOnAxis(this._labels[e+1].position),a.fillRect(b.x,d.y1,Math.abs(c.x-b.x),Math.abs(d.y1-d.y2)),b=!1):b=!0);else if(("left"===this._position||"right"===this._position)&&this.interlacedColor)for(a.fillStyle=this.interlacedColor,e=0;e<this._labels.length;e++)this._labels[e].stripLine||(b?(c=this.getPixelCoordinatesOnAxis(this._labels[e].position),b=e+1>=this._labels.length?this.getPixelCoordinatesOnAxis(this.maximum):
+this.getPixelCoordinatesOnAxis(this._labels[e+1].position),a.fillRect(d.x1,b.y,Math.abs(d.x1-d.x2),Math.abs(b.y-c.y)),b=!1):b=!0);a.beginPath()};A.prototype.renderStripLinesOfThicknessType=function(a){if(this.stripLines&&0<this.stripLines.length&&a)for(var b=this.chart.plotArea.ctx,c=0,c=0;c<this.stripLines.length;c++){var d=this.stripLines[c];if(d._thicknessType===a&&("pixel"!==a||!(d.value<this.minimum||d.value>this.maximum))){var e=this.getPixelCoordinatesOnAxis(d.value),f=Math.abs("pixel"===a?
+d.thickness:this.conversionParameters.pixelPerUnit*d.thickness);if(!(0>=f)){b.strokeStyle=d.color;b.beginPath();C(d.id);var g,k,p,h;b.lineWidth=f;if("bottom"===this._position||"top"===this._position)g=k=1===b.lineWidth%2?(e.x<<0)+0.5:e.x<<0,p=this.chart.plotArea.y1,h=this.chart.plotArea.y2;else if("left"===this._position||"right"===this._position)p=h=1===b.lineWidth%2?(e.y<<0)+0.5:e.y<<0,g=this.chart.plotArea.x1,k=this.chart.plotArea.x2;b.moveTo(g,p);b.lineTo(k,h);b.stroke()}}}};A.prototype.renderGrid=
+function(){if(this.gridThickness&&0<this.gridThickness){var a=this.chart.ctx,b,c=this.chart.plotArea;a.lineWidth=this.gridThickness;a.strokeStyle=this.gridColor;if("bottom"===this._position||"top"===this._position)for(d=0;d<this._labels.length&&!this._labels[d].stripLine;d++)this._labels[d].position<this.minimum||this._labels[d].position>this.maximum||(a.beginPath(),b=this.getPixelCoordinatesOnAxis(this._labels[d].position),b=1===a.lineWidth%2?(b.x<<0)+0.5:b.x<<0,a.moveTo(b,c.y1<<0),a.lineTo(b,c.y2<<
+0),a.stroke());else if("left"===this._position||"right"===this._position)for(var d=0;d<this._labels.length&&!this._labels[d].stripLine;d++)this._labels[d].position<this.minimum||this._labels[d].position>this.maximum||(a.beginPath(),b=this.getPixelCoordinatesOnAxis(this._labels[d].position),b=1===a.lineWidth%2?(b.y<<0)+0.5:b.y<<0,a.moveTo(c.x1<<0,b),a.lineTo(c.x2<<0,b),a.stroke())}};A.prototype.renderAxisLine=function(){var a=this.chart.ctx;if("bottom"===this._position||"top"===this._position){if(this.lineThickness){a.lineWidth=
+this.lineThickness;a.strokeStyle=this.lineColor?this.lineColor:"black";var b=1===this.lineThickness%2?(this.lineCoordinates.y1<<0)+0.5:this.lineCoordinates.y1<<0;a.beginPath();a.moveTo(this.lineCoordinates.x1,b);a.lineTo(this.lineCoordinates.x2,b);a.stroke()}}else"left"!==this._position&&"right"!==this._position||!this.lineThickness||(a.lineWidth=this.lineThickness,a.strokeStyle=this.lineColor,b=1===this.lineThickness%2?(this.lineCoordinates.x1<<0)+0.5:this.lineCoordinates.x1<<0,a.beginPath(),a.moveTo(b,
+this.lineCoordinates.y1),a.lineTo(b,this.lineCoordinates.y2),a.stroke())};A.prototype.getPixelCoordinatesOnAxis=function(a){var b={},c=this.lineCoordinates.width,d=this.lineCoordinates.height;if("bottom"===this._position||"top"===this._position)c/=Math.abs(this.maximum-this.minimum),b.x=this.lineCoordinates.x1+c*(a-this.minimum),b.y=this.lineCoordinates.y1;if("left"===this._position||"right"===this._position)c=d/Math.abs(this.maximum-this.minimum),b.y=this.lineCoordinates.y2-c*(a-this.minimum),b.x=
+this.lineCoordinates.x2;return b};A.prototype.getXValueAt=function(a){if(!a)return null;var b=null;"left"===this._position?b=(this.chart.axisX.maximum-this.chart.axisX.minimum)/this.chart.axisX.lineCoordinates.height*(this.chart.axisX.lineCoordinates.y2-a.y)+this.chart.axisX.minimum:"bottom"===this._position&&(b=(this.chart.axisX.maximum-this.chart.axisX.minimum)/this.chart.axisX.lineCoordinates.width*(a.x-this.chart.axisX.lineCoordinates.x1)+this.chart.axisX.minimum);return b};A.prototype.calculateValueToPixelconversionParameters=
+function(a){a={pixelPerUnit:null,minimum:null,reference:null};var b=this.lineCoordinates.width,c=this.lineCoordinates.height;a.minimum=this.minimum;if("bottom"===this._position||"top"===this._position)a.pixelPerUnit=b/Math.abs(this.maximum-this.minimum),a.reference=this.lineCoordinates.x1;if("left"===this._position||"right"===this._position)a.pixelPerUnit=-1*c/Math.abs(this.maximum-this.minimum),a.reference=this.lineCoordinates.y2;this.conversionParameters=a};A.prototype.calculateAxisParameters=function(){var a=
+this.chart.layoutManager.getFreeSpace();"bottom"===this._position||"top"===this._position?(this.maxWidth=a.width,this.maxHeight=a.height):(this.maxWidth=a.height,this.maxHeight=a.width);var a="axisX"===this.type?500>this.maxWidth?8:Math.max(6,Math.floor(this.maxWidth/62)):Math.max(Math.floor(this.maxWidth/40),2),b,c,d,e;e=0;"axisX"===this.type?(b=null!==this.sessionVariables.internalMinimum?this.sessionVariables.internalMinimum:this.dataInfo.viewPortMin,c=null!==this.sessionVariables.internalMaximum?
+this.sessionVariables.internalMaximum:this.dataInfo.viewPortMax,0===c-b&&(c+=0.4,b-=0.4),d=Infinity!==this.dataInfo.minDiff?this.dataInfo.minDiff:1<c-b?0.5*Math.abs(c-b):1):"axisY"===this.type&&(b="undefined"===typeof this._options.minimum?this.dataInfo.viewPortMin:this._options.minimum,c="undefined"===typeof this._options.maximum?this.dataInfo.viewPortMax:this._options.maximum,0===b&&0===c?(c+=9,b=0):0===c-b?(e=Math.min(Math.abs(0.01*Math.abs(c)),5),c+=e,b-=e):(e=Math.min(Math.abs(0.01*Math.abs(c-
+b)),0.05),0!==c&&(c+=e),0!==b&&(b-=e)),this.includeZero&&"undefined"===typeof this._options.minimum&&0<b&&(b=0),this.includeZero&&"undefined"===typeof this._options.maximum&&0>c&&(c=0));"axisX"===this.type&&"dateTime"===this.chart.plotInfo.axisXValueType?(e=c-b,this.intervalType||(e/1<=a?(this.interval=1,this.intervalType="millisecond"):e/2<=a?(this.interval=2,this.intervalType="millisecond"):e/5<=a?(this.interval=5,this.intervalType="millisecond"):e/10<=a?(this.interval=10,this.intervalType="millisecond"):
+e/20<=a?(this.interval=20,this.intervalType="millisecond"):e/50<=a?(this.interval=50,this.intervalType="millisecond"):e/100<=a?(this.interval=100,this.intervalType="millisecond"):e/200<=a?(this.interval=200,this.intervalType="millisecond"):e/250<=a?(this.interval=250,this.intervalType="millisecond"):e/300<=a?(this.interval=300,this.intervalType="millisecond"):e/400<=a?(this.interval=400,this.intervalType="millisecond"):e/500<=a?(this.interval=500,this.intervalType="millisecond"):e/(1*z.secondDuration)<=
+a?(this.interval=1,this.intervalType="second"):e/(2*z.secondDuration)<=a?(this.interval=2,this.intervalType="second"):e/(5*z.secondDuration)<=a?(this.interval=5,this.intervalType="second"):e/(10*z.secondDuration)<=a?(this.interval=10,this.intervalType="second"):e/(15*z.secondDuration)<=a?(this.interval=15,this.intervalType="second"):e/(20*z.secondDuration)<=a?(this.interval=20,this.intervalType="second"):e/(30*z.secondDuration)<=a?(this.interval=30,this.intervalType="second"):e/(1*z.minuteDuration)<=
+a?(this.interval=1,this.intervalType="minute"):e/(2*z.minuteDuration)<=a?(this.interval=2,this.intervalType="minute"):e/(5*z.minuteDuration)<=a?(this.interval=5,this.intervalType="minute"):e/(10*z.minuteDuration)<=a?(this.interval=10,this.intervalType="minute"):e/(15*z.minuteDuration)<=a?(this.interval=15,this.intervalType="minute"):e/(20*z.minuteDuration)<=a?(this.interval=20,this.intervalType="minute"):e/(30*z.minuteDuration)<=a?(this.interval=30,this.intervalType="minute"):e/(1*z.hourDuration)<=
+a?(this.interval=1,this.intervalType="hour"):e/(2*z.hourDuration)<=a?(this.interval=2,this.intervalType="hour"):e/(3*z.hourDuration)<=a?(this.interval=3,this.intervalType="hour"):e/(6*z.hourDuration)<=a?(this.interval=6,this.intervalType="hour"):e/(1*z.dayDuration)<=a?(this.interval=1,this.intervalType="day"):e/(2*z.dayDuration)<=a?(this.interval=2,this.intervalType="day"):e/(4*z.dayDuration)<=a?(this.interval=4,this.intervalType="day"):e/(1*z.weekDuration)<=a?(this.interval=1,this.intervalType="week"):
+e/(2*z.weekDuration)<=a?(this.interval=2,this.intervalType="week"):e/(3*z.weekDuration)<=a?(this.interval=3,this.intervalType="week"):e/(1*z.monthDuration)<=a?(this.interval=1,this.intervalType="month"):e/(2*z.monthDuration)<=a?(this.interval=2,this.intervalType="month"):e/(3*z.monthDuration)<=a?(this.interval=3,this.intervalType="month"):e/(6*z.monthDuration)<=a?(this.interval=6,this.intervalType="month"):(this.interval=e/(1*z.yearDuration)<=a?1:e/(2*z.yearDuration)<=a?2:e/(4*z.yearDuration)<=a?
+4:Math.floor(A.getNiceNumber(e/(a-1),!0)/z.yearDuration),this.intervalType="year")),this.minimum=null!==this.sessionVariables.internalMinimum?this.sessionVariables.internalMinimum:b-d/2,this.maximum=this.sessionVariables.internalMaximum?this.sessionVariables.internalMaximum:c+d/2,this.valueFormatString||("year"===this.intervalType?this.valueFormatString="YYYY":"month"===this.intervalType?this.valueFormatString="MMM YYYY":"week"===this.intervalType?this.valueFormatString="MMM DD YYYY":"day"===this.intervalType?
+this.valueFormatString="MMM DD YYYY":"hour"===this.intervalType?this.valueFormatString="hh:mm TT":"minute"===this.intervalType?this.valueFormatString="hh:mm TT":"second"===this.intervalType?this.valueFormatString="hh:mm:ss TT":"millisecond"===this.intervalType&&(this.valueFormatString="fff'ms'")),this.intervalstartTimePercent=this.getLabelStartPoint(new Date(this.minimum),this.intervalType,this.interval)):(this.intervalType="number",e=A.getNiceNumber(c-b,!1),this.interval=this._options&&this._options.interval?
+this._options.interval:A.getNiceNumber(e/(a-1),!0),this.minimum=null!==this.sessionVariables.internalMinimum?this.sessionVariables.internalMinimum:Math.floor(b/this.interval)*this.interval,this.maximum=null!==this.sessionVariables.internalMaximum?this.sessionVariables.internalMaximum:Math.ceil(c/this.interval)*this.interval,"axisX"===this.type?(null===this.sessionVariables.internalMinimum&&(this.minimum=b-d/2),this.sessionVariables.internalMaximum||(this.maximum=c+d/2),this.intervalstartTimePercent=
+Math.floor((this.minimum+0.2*this.interval)/this.interval)*this.interval):"axisY"===this.type&&(this.intervalstartTimePercent=this.minimum));"axisX"===this.type&&(this._absoluteMinimum=this._options&&"undefined"!==typeof this._options.minimum?this._options.minimum:this.dataInfo.min-d/2,this._absoluteMaximum=this._options&&"undefined"!==typeof this._options.maximum?this._options.maximum:this.dataInfo.max+d/2);if(!this.valueFormatString&&(this.valueFormatString="#,##0.##",e=Math.abs(this.maximum-this.minimum),
+1>e&&(b=Math.floor(Math.abs(Math.log(e)/Math.LN10))+2,2<b)))for(c=0;c<b-2;c++)this.valueFormatString+="#"};A.getNiceNumber=function(a,b){var c=Math.floor(Math.log(a)/Math.LN10),d=a/Math.pow(10,c);return Number(((b?1.5>d?1:3>d?2:7>d?5:10:1>=d?1:2>=d?2:5>=d?5:10)*Math.pow(10,c)).toFixed(20))};A.prototype.getLabelStartPoint=function(){var a=Y(this.interval,this.intervalType),a=new Date(Math.floor(this.minimum/a)*a);if("millisecond"!==this.intervalType)if("second"===this.intervalType)0<a.getMilliseconds()&&
+(a.setSeconds(a.getSeconds()+1),a.setMilliseconds(0));else if("minute"===this.intervalType){if(0<a.getSeconds()||0<a.getMilliseconds())a.setMinutes(a.getMinutes()+1),a.setSeconds(0),a.setMilliseconds(0)}else if("hour"===this.intervalType){if(0<a.getMinutes()||0<a.getSeconds()||0<a.getMilliseconds())a.setHours(a.getHours()+1),a.setMinutes(0),a.setSeconds(0),a.setMilliseconds(0)}else if("day"===this.intervalType){if(0<a.getHours()||0<a.getMinutes()||0<a.getSeconds()||0<a.getMilliseconds())a.setDate(a.getDate()+
+1),a.setHours(0),a.setMinutes(0),a.setSeconds(0),a.setMilliseconds(0)}else if("week"===this.intervalType){if(0<a.getDay()||0<a.getHours()||0<a.getMinutes()||0<a.getSeconds()||0<a.getMilliseconds())a.setDate(a.getDate()+(7-a.getDay())),a.setHours(0),a.setMinutes(0),a.setSeconds(0),a.setMilliseconds(0)}else if("month"===this.intervalType){if(1<a.getDate()||0<a.getHours()||0<a.getMinutes()||0<a.getSeconds()||0<a.getMilliseconds())a.setMonth(a.getMonth()+1),a.setDate(1),a.setHours(0),a.setMinutes(0),
+a.setSeconds(0),a.setMilliseconds(0)}else"year"===this.intervalType&&(0<a.getMonth()||1<a.getDate()||0<a.getHours()||0<a.getMinutes()||0<a.getSeconds()||0<a.getMilliseconds())&&(a.setFullYear(a.getFullYear()+1),a.setMonth(0),a.setDate(1),a.setHours(0),a.setMinutes(0),a.setSeconds(0),a.setMilliseconds(0));return a};O(ma,L);O(N,L);N.prototype._initialize=function(){if(this.enabled){this.container=document.createElement("div");this.container.setAttribute("class","canvasjs-chart-tooltip");this.container.style.position=
+"absolute";this.container.style.height="auto";this.container.style.boxShadow="1px 1px 2px 2px rgba(0,0,0,0.1)";this.container.style.zIndex="1000";this.container.style.display="none";var a;a='<div style=" width: auto;height: auto;min-width: 50px;';a+="line-height: 20px;";a+="margin: 0px 0px 0px 0px;";a+="padding: 5px;";a+="font-family: Calibri, Arial, Georgia, serif;";a+="font-weight: 400;";a+="font-style: "+(t?"italic;":"normal;");a+="font-size: 14px;";a+="color: #000000;";a+="text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.1);";
+a+="text-align: left;";a+="border: 2px solid gray;";a+=t?"background: rgba(255,255,255,.9);":"background: rgb(255,255,255);";a+="text-indent: 0px;";a+="white-space: nowrap;";a+="border-radius: 5px;";a+="-moz-user-select:none;";a+="-khtml-user-select: none;";a+="-webkit-user-select: none;";a+="-ms-user-select: none;";a+="user-select: none;";t||(a+="filter: alpha(opacity = 90);",a+="filter: progid:DXImageTransform.Microsoft.Shadow(Strength=3, Direction=135, Color='#666666');");a+='} "> Sample Tooltip</div>';
+this.container.innerHTML=a;this.contentDiv=this.container.firstChild;this.container.style.borderRadius=this.contentDiv.style.borderRadius;this.chart._canvasJSContainer.appendChild(this.container)}};N.prototype.mouseMoveHandler=function(a,b){this._lastUpdated&&40>(new Date).getTime()-this._lastUpdated||(this._lastUpdated=(new Date).getTime(),this._updateToolTip(a,b))};N.prototype._updateToolTip=function(a,b){if(this.enabled&&!this.chart.disableToolTip){if("undefined"===typeof a||"undefined"===typeof b){if(isNaN(this._prevX)||
+isNaN(this._prevY))return;a=this._prevX;b=this._prevY}else this._prevX=a,this._prevY=b;var c=null,d=null,e=[],f=0;if(this.shared&&"none"!==this.chart.plotInfo.axisPlacement){f="xySwapped"===this.chart.plotInfo.axisPlacement?(this.chart.axisX.maximum-this.chart.axisX.minimum)/this.chart.axisX.lineCoordinates.height*(this.chart.axisX.lineCoordinates.y2-b)+this.chart.axisX.minimum:(this.chart.axisX.maximum-this.chart.axisX.minimum)/this.chart.axisX.lineCoordinates.width*(a-this.chart.axisX.lineCoordinates.x1)+
+this.chart.axisX.minimum;c=[];for(d=0;d<this.chart.data.length;d++){var g=this.chart.data[d].getDataPointAtX(f,!0);g&&0<=g.index&&(g.dataSeries=this.chart.data[d],null!==g.dataPoint.y&&c.push(g))}if(0===c.length)return;c.sort(function(a,b){return a.distance-b.distance});f=c[0];for(d=0;d<c.length;d++)c[d].dataPoint.x.valueOf()===f.dataPoint.x.valueOf()&&e.push(c[d]);c=null}else if((g=this.chart.getDataPointAtXY(a,b,!0))?(this.currentDataPointIndex=g.dataPointIndex,this.currentSeriesIndex=g.dataSeries.index):
+t?(g=ta(a,b,this.chart._eventManager.ghostCtx),0<g&&"undefined"!==typeof this.chart._eventManager.objectMap[g]?(eventObject=this.chart._eventManager.objectMap[g],this.currentSeriesIndex=eventObject.dataSeriesIndex,this.currentDataPointIndex=0<=eventObject.dataPointIndex?eventObject.dataPointIndex:-1):this.currentDataPointIndex=-1):this.currentDataPointIndex=-1,0<=this.currentSeriesIndex){d=this.chart.data[this.currentSeriesIndex];g={};if(0<=this.currentDataPointIndex)c=d.dataPoints[this.currentDataPointIndex],
+g.dataSeries=d,g.dataPoint=c,g.index=this.currentDataPointIndex,g.distance=Math.abs(c.x-f);else if("line"===d.type||"stepLine"===d.type||"spline"===d.type||"area"===d.type||"stepArea"===d.type||"splineArea"===d.type||"stackedArea"===d.type||"stackedArea100"===d.type||"rangeArea"===d.type||"rangeSplineArea"===d.type||"candlestick"===d.type||"ohlc"===d.type)f=(this.chart.axisX.maximum-this.chart.axisX.minimum)/this.chart.axisX.lineCoordinates.width*(a-this.chart.axisX.lineCoordinates.x1)+this.chart.axisX.minimum.valueOf(),
+g=d.getDataPointAtX(f,!0),g.dataSeries=d,this.currentDataPointIndex=g.index,c=g.dataPoint;else return;null!==g.dataPoint.y&&e.push(g)}if(0<e.length)if(this.highlightObjects(e),f="",f=this.getToolTipInnerHTML({entries:e}),null!==f){this.contentDiv.innerHTML=f;this.contentDiv.innerHTML=f;f=!1;"none"===this.container.style.display&&(f=!0,this.container.style.display="block");try{this.contentDiv.style.borderRightColor=this.contentDiv.style.borderLeftColor=this.contentDiv.style.borderColor=this.borderColor?
+this.borderColor:e[0].dataPoint.color?e[0].dataPoint.color:e[0].dataSeries.color?e[0].dataSeries.color:e[0].dataSeries._colorSet[e[0].index%e[0].dataSeries._colorSet.length]}catch(k){}"pie"===e[0].dataSeries.type||"doughnut"===e[0].dataSeries.type||"funnel"===e[0].dataSeries.type||"bar"===e[0].dataSeries.type||"rangeBar"===e[0].dataSeries.type||"stackedBar"===e[0].dataSeries.type||"stackedBar100"===e[0].dataSeries.type?toolTipLeft=a-10-this.container.clientWidth:(toolTipLeft=this.chart.axisX.lineCoordinates.width/
+Math.abs(this.chart.axisX.maximum-this.chart.axisX.minimum)*Math.abs(e[0].dataPoint.x-this.chart.axisX.minimum)+this.chart.axisX.lineCoordinates.x1+0.5-this.container.clientWidth<<0,toolTipLeft-=10);0>toolTipLeft&&(toolTipLeft+=this.container.clientWidth+20);toolTipLeft+this.container.clientWidth>this.chart._container.clientWidth&&(toolTipLeft=Math.max(0,this.chart._container.clientWidth-this.container.clientWidth));toolTipLeft+="px";e=1!==e.length||this.shared||"line"!==e[0].dataSeries.type&&"stepLine"!==
+e[0].dataSeries.type&&"spline"!==e[0].dataSeries.type&&"area"!==e[0].dataSeries.type&&"stepArea"!==e[0].dataSeries.type&&"splineArea"!==e[0].dataSeries.type&&"stackedArea"!==e[0].dataSeries.type&&"stackedArea100"!==e[0].dataSeries.type?"bar"===e[0].dataSeries.type||"rangeBar"===e[0].dataSeries.type||"stackedBar"===e[0].dataSeries.type||"stackedBar100"===e[0].dataSeries.type?e[0].dataSeries.axisX.lineCoordinates.y2-e[0].dataSeries.axisX.lineCoordinates.height/Math.abs(e[0].dataSeries.axisX.maximum-
+e[0].dataSeries.axisX.minimum)*Math.abs(e[0].dataPoint.x-e[0].dataSeries.axisX.minimum)+0.5<<0:b:e[0].dataSeries.axisY.lineCoordinates.y2-e[0].dataSeries.axisY.lineCoordinates.height/Math.abs(e[0].dataSeries.axisY.maximum-e[0].dataSeries.axisY.minimum)*Math.abs(e[0].dataPoint.y-e[0].dataSeries.axisY.minimum)+0.5<<0;e=-e+10;0<e+this.container.clientHeight+5&&(e-=e+this.container.clientHeight+5-0);this.container.style.left=toolTipLeft;this.container.style.bottom=e+"px";!this.animationEnabled||f?this.disableAnimation():
+this.enableAnimation()}else this.hide(!1)}};N.prototype.highlightObjects=function(a){if(this.enabled){var b=this.chart.overlaidCanvasCtx;this.chart.resetOverlayedCanvas();b.save();for(var c=0,d=0;d<a.length;d++){var e=a[d];if((e=this.chart._eventManager.objectMap[e.dataSeries.dataPointIds[e.index]])&&e.objectType&&"dataPoint"===e.objectType){var c=this.chart.data[e.dataSeriesIndex],f=e.dataPointIndex;if("line"===c.type||"stepLine"===c.type||"spline"===c.type||"scatter"===c.type||"area"===c.type||
+"stepArea"===c.type||"splineArea"===c.type||"stackedArea"===c.type||"stackedArea100"===c.type||"rangeArea"===c.type||"rangeSplineArea"===c.type){var g=c.getMarkerProperties(f,e.x1,e.y1,this.chart.overlaidCanvasCtx);g.size=Math.max(1.5*g.size<<0,10);g.borderColor=g.borderColor||"#FFFFFF";g.borderThickness=g.borderThickness||Math.ceil(0.1*g.size);J.drawMarkers([g]);"undefined"!==typeof e.y2&&(g=c.getMarkerProperties(f,e.x1,e.y2,this.chart.overlaidCanvasCtx),g.size=Math.max(1.5*g.size<<0,10),g.borderColor=
+g.borderColor||"#FFFFFF",g.borderThickness=g.borderThickness||Math.ceil(0.1*g.size),J.drawMarkers([g]))}else"bubble"===c.type?(g=c.getMarkerProperties(f,e.x1,e.y1,this.chart.overlaidCanvasCtx),g.size=e.size,g.color="white",g.borderColor="white",b.globalAlpha=0.3,J.drawMarkers([g]),b.globalAlpha=1):"column"===c.type||"stackedColumn"===c.type||"stackedColumn100"===c.type||"bar"===c.type||"rangeBar"===c.type||"stackedBar"===c.type||"stackedBar100"===c.type||"rangeColumn"===c.type?F(b,e.x1,e.y1,e.x2,
+e.y2,"white",0,null,!1,!1,!1,!1,0.3):"pie"===c.type||"doughnut"===c.type?pa(b,e.center,e.radius,"white",c.type,e.startAngle,e.endAngle,0.3):"candlestick"===c.type?(b.globalAlpha=1,b.strokeStyle=e.color,b.lineWidth=2*e.borderThickness,c=0===b.lineWidth%2?0:0.5,b.beginPath(),b.moveTo(e.x3-c,e.y2),b.lineTo(e.x3-c,Math.min(e.y1,e.y4)),b.stroke(),b.beginPath(),b.moveTo(e.x3-c,Math.max(e.y1,e.y4)),b.lineTo(e.x3-c,e.y3),b.stroke(),F(b,e.x1,Math.min(e.y1,e.y4),e.x2,Math.max(e.y1,e.y4),"transparent",2*e.borderThickness,
+e.color,!1,!1,!1,!1),b.globalAlpha=1):"ohlc"===c.type&&(b.globalAlpha=1,b.strokeStyle=e.color,b.lineWidth=2*e.borderThickness,c=0===b.lineWidth%2?0:0.5,b.beginPath(),b.moveTo(e.x3-c,e.y2),b.lineTo(e.x3-c,e.y3),b.stroke(),b.beginPath(),b.moveTo(e.x3,e.y1),b.lineTo(e.x1,e.y1),b.stroke(),b.beginPath(),b.moveTo(e.x3,e.y4),b.lineTo(e.x2,e.y4),b.stroke(),b.globalAlpha=1)}}b.globalAlpha=1;b.beginPath()}};N.prototype.getToolTipInnerHTML=function(a){a=a.entries;for(var b=null,c=null,d=null,e=0,f="",g=!0,k=
+0;k<a.length;k++)if(a[k].dataSeries.toolTipContent||a[k].dataPoint.toolTipContent){g=!1;break}if(g&&this.content&&"function"===typeof this.content)b=this.content({entries:a});else if(this.shared){for(var p="",k=0;k<a.length;k++)if(c=a[k].dataSeries,d=a[k].dataPoint,e=a[k].index,f="",0===k&&(g&&!this.content)&&(p+="undefined"!==typeof this.chart.axisX.labels[d.x]?this.chart.axisX.labels[d.x]:"{x}",p+="</br>",p=this.chart.replaceKeywordsWithValue(p,d,c,e)),null!==d.toolTipContent&&("undefined"!==typeof d.toolTipContent||
+null!==c._options.toolTipContent)){if("line"===c.type||"stepLine"===c.type||"spline"===c.type||"area"===c.type||"stepArea"===c.type||"splineArea"===c.type||"column"===c.type||"bar"===c.type||"scatter"===c.type||"stackedColumn"===c.type||"stackedColumn100"===c.type||"stackedBar"===c.type||"stackedBar100"===c.type||"stackedArea"===c.type||"stackedArea100"===c.type)f+=d.toolTipContent?d.toolTipContent:c.toolTipContent?c.toolTipContent:this.content&&"function"!==typeof this.content?this.content:"<span style='\"'color:{color};'\"'>{name}:</span> {y}";
+else if("bubble"===c.type)f+=d.toolTipContent?d.toolTipContent:c.toolTipContent?c.toolTipContent:this.content&&"function"!==typeof this.content?this.content:"<span style='\"'color:{color};'\"'>{name}:</span> {y}, {z}";else if("pie"===c.type||"doughnut"===c.type||"funnel"===c.type)f+=d.toolTipContent?d.toolTipContent:c.toolTipContent?c.toolTipContent:this.content&&"function"!==typeof this.content?this.content:" {y}";else if("rangeColumn"===c.type||"rangeBar"===c.type||
+"rangeArea"===c.type||"rangeSplineArea"===c.type)f+=d.toolTipContent?d.toolTipContent:c.toolTipContent?c.toolTipContent:this.content&&"function"!==typeof this.content?this.content:"<span style='\"'color:{color};'\"'>{name}:</span> {y[0]}, {y[1]}";else if("candlestick"===c.type||"ohlc"===c.type)f+=d.toolTipContent?d.toolTipContent:c.toolTipContent?c.toolTipContent:this.content&&"function"!==typeof this.content?this.content:"<span style='\"'color:{color};'\"'>{name}:</span><br/>Open: {y[0]}<br/>High: {y[1]}<br/>Low: {y[2]}<br/>Close: {y[3]}";
+null===b&&(b="");b+=this.chart.replaceKeywordsWithValue(f,d,c,e);k<a.length-1&&(b+="</br>")}null!==b&&(b=p+b)}else{c=a[0].dataSeries;d=a[0].dataPoint;e=a[0].index;if(null===d.toolTipContent||"undefined"===typeof d.toolTipContent&&null===c._options.toolTipContent)return null;if("line"===c.type||"stepLine"===c.type||"spline"===c.type||"area"===c.type||"stepArea"===c.type||"splineArea"===c.type||"column"===c.type||"bar"===c.type||"scatter"===c.type||"stackedColumn"===c.type||"stackedColumn100"===c.type||
+"stackedBar"===c.type||"stackedBar100"===c.type||"stackedArea"===c.type||"stackedArea100"===c.type)f=d.toolTipContent?d.toolTipContent:c.toolTipContent?c.toolTipContent:this.content&&"function"!==typeof this.content?this.content:"<span style='\"'color:{color};'\"'>"+(d.label?"{label}":"{x}")+" :</span> {y}";else if("bubble"===c.type)f=d.toolTipContent?d.toolTipContent:c.toolTipContent?c.toolTipContent:this.content&&"function"!==typeof this.content?this.content:"<span style='\"'color:{color};'\"'>"+
+(d.label?"{label}":"{x}")+":</span> {y}, {z}";else if("pie"===c.type||"doughnut"===c.type||"funnel"===c.type)f=d.toolTipContent?d.toolTipContent:c.toolTipContent?c.toolTipContent:this.content&&"function"!==typeof this.content?this.content:(d.name?"{name}: ":d.label?"{label}: ":"")+"{y}";else if("rangeColumn"===c.type||"rangeBar"===c.type||"rangeArea"===c.type||"rangeSplineArea"===c.type)f=d.toolTipContent?d.toolTipContent:c.toolTipContent?c.toolTipContent:
+this.content&&"function"!==typeof this.content?this.content:"<span style='\"'color:{color};'\"'>"+(d.label?"{label}":"{x}")+" :</span> {y[0]}, {y[1]}";else if("candlestick"===c.type||"ohlc"===c.type)f=d.toolTipContent?d.toolTipContent:c.toolTipContent?c.toolTipContent:this.content&&"function"!==typeof this.content?this.content:"<span style='\"'color:{color};'\"'>"+(d.label?"{label}":"{x}")+"</span><br/>Open: {y[0]}<br/>High: {y[1]}<br/>Low: {y[2]}<br/>Close: {y[3]}";
+null===b&&(b="");b+=this.chart.replaceKeywordsWithValue(f,d,c,e)}return b};N.prototype.enableAnimation=function(){this.container.style.WebkitTransition||(this.container.style.WebkitTransition="left .2s ease-out, bottom .2s ease-out",this.container.style.MozTransition="left .2s ease-out, bottom .2s ease-out",this.container.style.MsTransition="left .2s ease-out, bottom .2s ease-out",this.container.style.transition="left .2s ease-out, bottom .2s ease-out")};N.prototype.disableAnimation=function(){this.container.style.WebkitTransition&&
+(this.container.style.WebkitTransition="",this.container.style.MozTransition="",this.container.style.MsTransition="",this.container.style.transition="")};N.prototype.hide=function(a){this.enabled&&(this.container.style.display="none",this.currentSeriesIndex=-1,this._prevY=this._prevX=NaN,("undefined"===typeof a||a)&&this.chart.resetOverlayedCanvas())};v.prototype.replaceKeywordsWithValue=function(a,b,c,d,e){var f=this;e="undefined"===typeof e?0:e;if((0<=c.type.indexOf("stacked")||"pie"===c.type||
+"doughnut"===c.type)&&(0<=a.indexOf("#percent")||0<=a.indexOf("#total"))){var g="#percent",k="#total",p=null;if(0<=c.type.indexOf("stacked"))k=0,p=b.x.getTime?b.x.getTime():b.x,p in c.plotUnit.yTotals&&(k=c.plotUnit.yTotals[p],g=isNaN(b.y)?0:100*(b.y/k));else if("pie"===c.type||"doughnut"===c.type){for(i=k=0;i<c.dataPoints.length;i++)isNaN(c.dataPoints[i].y)||(k+=c.dataPoints[i].y);g=isNaN(b.y)?0:100*(b.y/k)}do{p="";if(c.percentFormatString)p=c.percentFormatString;else{var p="#,##0.",h=Math.max(Math.ceil(Math.log(1/
+Math.abs(g))/Math.LN10),2);if(isNaN(h)||!isFinite(h))h=2;for(var l=0;l<h;l++)p+="#"}a=a.replace("#percent",X(g,p,f._cultureInfo));a=a.replace("#total",X(k,c.yValueFormatString?c.yValueFormatString:"#,##0.########"))}while(0<=a.indexOf("#percent")||0<=a.indexOf("#total"))}return a.replace(/\{.*?\}|"[^"]*"|'[^']*'/g,function(a){if('"'===a[0]&&'"'===a[a.length-1]||"'"===a[0]&&"'"===a[a.length-1])return a.slice(1,a.length-1);a=Z(a.slice(1,a.length-1));a=a.replace("#index",e);var g=null;try{var h=a.match(/(.*?)\s*\[\s*(.*?)\s*\]/);
+h&&0<h.length&&(g=Z(h[2]),a=Z(h[1]))}catch(k){}h=null;if("color"===a)return b.color?b.color:c.color?c.color:c._colorSet[d%c._colorSet.length];if(b.hasOwnProperty(a))h=b;else if(c.hasOwnProperty(a))h=c;else return"";h=h[a];null!==g&&(h=h[g]);return"x"===a?f.axisX&&"dateTime"===f.plotInfo.axisXValueType?ya(h,b.xValueFormatString?b.xValueFormatString:c.xValueFormatString?c.xValueFormatString:f.axisX&&f.axisX.valueFormatString?f.axisX.valueFormatString:"DD MMM YY",f._cultureInfo):X(h,b.xValueFormatString?
+b.xValueFormatString:c.xValueFormatString?c.xValueFormatString:"#,##0.########",f._cultureInfo):"y"===a?X(h,b.yValueFormatString?b.yValueFormatString:c.yValueFormatString?c.yValueFormatString:"#,##0.########",f._cultureInfo):"z"===a?X(h,b.zValueFormatString?b.zValueFormatString:c.zValueFormatString?c.zValueFormatString:"#,##0.########",f._cultureInfo):h})};$.prototype.reset=function(){this.lastObjectId=0;this.objectMap=[];this.rectangularRegionEventSubscriptions=[];this.previousDataPointEventObject=
+null;this.eventObjects=[];t&&(this.ghostCtx.clearRect(0,0,this.chart.width,this.chart.height),this.ghostCtx.beginPath())};$.prototype.getNewObjectTrackingId=function(){return++this.lastObjectId};$.prototype.mouseEventHandler=function(a){if("mousemove"===a.type||"click"===a.type){var b=[],c=ia(a),d=null;if((d=this.chart.getObjectAtXY(c.x,c.y,!1))&&"undefined"!==typeof this.objectMap[d])if(d=this.objectMap[d],"dataPoint"===d.objectType){var e=this.chart.data[d.dataSeriesIndex],f=e.dataPoints[d.dataPointIndex],
+g=d.dataPointIndex;d.eventParameter={x:c.x,y:c.y,dataPoint:f,dataSeries:e._options,dataPointIndex:g,dataSeriesIndex:e.index,chart:this.chart._publicChartReference};d.eventContext={context:f,userContext:f,mouseover:"mouseover",mousemove:"mousemove",mouseout:"mouseout",click:"click"};b.push(d);d=this.objectMap[e.id];d.eventParameter={x:c.x,y:c.y,dataPoint:f,dataSeries:e._options,dataPointIndex:g,dataSeriesIndex:e.index,chart:this.chart._publicChartReference};d.eventContext={context:e,userContext:e._options,
+mouseover:"mouseover",mousemove:"mousemove",mouseout:"mouseout",click:"click"};b.push(this.objectMap[e.id])}else"legendItem"===d.objectType&&(e=this.chart.data[d.dataSeriesIndex],f=null!==d.dataPointIndex?e.dataPoints[d.dataPointIndex]:null,d.eventParameter={x:c.x,y:c.y,dataSeries:e._options,dataPoint:f,dataPointIndex:d.dataPointIndex,dataSeriesIndex:d.dataSeriesIndex,chart:this.chart._publicChartReference},d.eventContext={context:this.chart.legend,userContext:this.chart.legend._options,mouseover:"itemmouseover",
+mousemove:"itemmousemove",mouseout:"itemmouseout",click:"itemclick"},b.push(d));e=[];for(c=0;c<this.mouseoveredObjectMaps.length;c++){f=!0;for(d=0;d<b.length;d++)if(b[d].id===this.mouseoveredObjectMaps[c].id){f=!1;break}f?this.fireEvent(this.mouseoveredObjectMaps[c],"mouseout",a):e.push(this.mouseoveredObjectMaps[c])}this.mouseoveredObjectMaps=e;for(c=0;c<b.length;c++){e=!1;for(d=0;d<this.mouseoveredObjectMaps.length;d++)if(b[c].id===this.mouseoveredObjectMaps[d].id){e=!0;break}e||(this.fireEvent(b[c],
+"mouseover",a),this.mouseoveredObjectMaps.push(b[c]));"click"===a.type?this.fireEvent(b[c],"click",a):"mousemove"===a.type&&this.fireEvent(b[c],"mousemove",a)}}};$.prototype.fireEvent=function(a,b,c){if(a&&b){var d=a.eventParameter,e=a.eventContext,f=a.eventContext.userContext;f&&(e&&f[e[b]])&&f[e[b]].call(f,d);"mouseout"!==b?f.cursor&&f.cursor!==c.target.style.cursor&&(c.target.style.cursor=f.cursor):(c.target.style.cursor=this.chart._defaultCursor,delete a.eventParameter,delete a.eventContext);
+"click"===b&&("dataPoint"===a.objectType&&this.chart.pieDoughnutClickHandler)&&this.chart.pieDoughnutClickHandler.call(this.chart.data[a.dataSeriesIndex],d)}};O(xa,L);oa.prototype.animate=function(a,b,c,d,e){var f=this;this.chart.isAnimating=!0;e=e||y.easing.linear;c&&this.animations.push({startTime:(new Date).getTime()+(a?a:0),duration:b,animationCallback:c,onComplete:d});for(a=[];0<this.animations.length;)if(b=this.animations.shift(),c=(new Date).getTime(),d=0,b.startTime<=c&&(d=e(Math.min(c-b.startTime,
+b.duration),0,1,b.duration),d=Math.min(d,1),isNaN(d)||!isFinite(d))&&(d=1),1>d&&a.push(b),b.animationCallback(d),1<=d&&b.onComplete)b.onComplete();this.animations=a;0<this.animations.length?this.animationRequestId=this.chart.requestAnimFrame.call(window,function(){f.animate.call(f)}):this.chart.isAnimating=!1};oa.prototype.cancelAllAnimations=function(){this.animations=[];this.animationRequestId&&this.chart.cancelRequestAnimFrame.call(window,this.animationRequestId);this.animationRequestId=null;this.chart.isAnimating=
+!1};var y={yScaleAnimation:function(a,b){if(0!==a){var c=b.dest,d=b.source.canvas,e=b.animationBase;c.drawImage(d,0,0,d.width,d.height,0,e-e*a,c.canvas.width/H,a*c.canvas.height/H)}},xScaleAnimation:function(a,b){if(0!==a){var c=b.dest,d=b.source.canvas,e=b.animationBase;c.drawImage(d,0,0,d.width,d.height,e-e*a,0,a*c.canvas.width/H,c.canvas.height/H)}},xClipAnimation:function(a,b){if(0!==a){var c=b.dest,d=b.source.canvas;c.save();0<a&&c.drawImage(d,0,0,d.width*a,d.height,0,0,d.width*a/H,d.height/
+H);c.restore()}},fadeInAnimation:function(a,b){if(0!==a){var c=b.dest,d=b.source.canvas;c.save();c.globalAlpha=a;c.drawImage(d,0,0,d.width,d.height,0,0,c.canvas.width/H,c.canvas.height/H);c.restore()}},easing:{linear:function(a,b,c,d){return c*a/d+b},easeOutQuad:function(a,b,c,d){return-c*(a/=d)*(a-2)+b},easeOutQuart:function(a,b,c,d){return-c*((a=a/d-1)*a*a*a-1)+b},easeInQuad:function(a,b,c,d){return c*(a/=d)*a+b},easeInQuart:function(a,b,c,d){return c*(a/=d)*a*a*a+b}}},J={drawMarker:function(a,
+b,c,d,e,f,g,k){if(c){var p=1;c.fillStyle=f?f:"#000000";c.strokeStyle=g?g:"#000000";c.lineWidth=k?k:0;"circle"===d?(c.moveTo(a,b),c.beginPath(),c.arc(a,b,e/2,0,2*Math.PI,!1),f&&c.fill(),k&&(g?c.stroke():(p=c.globalAlpha,c.globalAlpha=0.15,c.strokeStyle="black",c.stroke(),c.globalAlpha=p))):"square"===d?(c.beginPath(),c.rect(a-e/2,b-e/2,e,e),f&&c.fill(),k&&(g?c.stroke():(p=c.globalAlpha,c.globalAlpha=0.15,c.strokeStyle="black",c.stroke(),c.globalAlpha=p))):"triangle"===d?(c.beginPath(),c.moveTo(a-e/
+2,b+e/2),c.lineTo(a+e/2,b+e/2),c.lineTo(a,b-e/2),c.closePath(),f&&c.fill(),k&&(g?c.stroke():(p=c.globalAlpha,c.globalAlpha=0.15,c.strokeStyle="black",c.stroke(),c.globalAlpha=p)),c.beginPath()):"cross"===d&&(c.strokeStyle=f,c.lineWidth=e/4,c.beginPath(),c.moveTo(a-e/2,b-e/2),c.lineTo(a+e/2,b+e/2),c.stroke(),c.moveTo(a+e/2,b-e/2),c.lineTo(a-e/2,b+e/2),c.stroke())}},drawMarkers:function(a){for(var b=0;b<a.length;b++){var c=a[b];J.drawMarker(c.x,c.y,c.ctx,c.type,c.size,c.color,c.borderColor,c.borderThickness)}}},
+za={Chart:function(a,b){var c=new v(a,b,this);this.render=function(){c.render(this.options)};this.options=c._options},addColorSet:function(a,b){V[a]=b},addCultureInfo:function(a,b){na[a]=b}};za.Chart.version="v1.6.1 GA";window.CanvasJS=za})();
+/*
+ excanvas is used to support IE678 which do not implement HTML5 Canvas Element. You can safely remove the following excanvas code if you don't need to support older browsers.
+
+ Copyright 2006 Google Inc. https://code.google.com/p/explorercanvas/
+ Licensed under the Apache License, Version 2.0
+*/
+document.createElement("canvas").getContext||function(){function V(){return this.context_||(this.context_=new C(this))}function W(a,b,c){var g=M.call(arguments,2);return function(){return a.apply(b,g.concat(M.call(arguments)))}}function N(a){return String(a).replace(/&/g,"&").replace(/"/g,""")}function O(a){a.namespaces.g_vml_||a.namespaces.add("g_vml_","urn:schemas-microsoft-com:vml","#default#VML");a.namespaces.g_o_||a.namespaces.add("g_o_","urn:schemas-microsoft-com:office:office","#default#VML");
+a.styleSheets.ex_canvas_||(a=a.createStyleSheet(),a.owningElement.id="ex_canvas_",a.cssText="canvas{display:inline-block;overflow:hidden;text-align:left;width:300px;height:150px}")}function X(a){var b=a.srcElement;switch(a.propertyName){case "width":b.getContext().clearRect();b.style.width=b.attributes.width.nodeValue+"px";b.firstChild.style.width=b.clientWidth+"px";break;case "height":b.getContext().clearRect(),b.style.height=b.attributes.height.nodeValue+"px",b.firstChild.style.height=b.clientHeight+
+"px"}}function Y(a){a=a.srcElement;a.firstChild&&(a.firstChild.style.width=a.clientWidth+"px",a.firstChild.style.height=a.clientHeight+"px")}function D(){return[[1,0,0],[0,1,0],[0,0,1]]}function t(a,b){for(var c=D(),g=0;3>g;g++)for(var e=0;3>e;e++){for(var f=0,d=0;3>d;d++)f+=a[g][d]*b[d][e];c[g][e]=f}return c}function P(a,b){b.fillStyle=a.fillStyle;b.lineCap=a.lineCap;b.lineJoin=a.lineJoin;b.lineWidth=a.lineWidth;b.miterLimit=a.miterLimit;b.shadowBlur=a.shadowBlur;b.shadowColor=a.shadowColor;b.shadowOffsetX=
+a.shadowOffsetX;b.shadowOffsetY=a.shadowOffsetY;b.strokeStyle=a.strokeStyle;b.globalAlpha=a.globalAlpha;b.font=a.font;b.textAlign=a.textAlign;b.textBaseline=a.textBaseline;b.arcScaleX_=a.arcScaleX_;b.arcScaleY_=a.arcScaleY_;b.lineScale_=a.lineScale_}function Q(a){var b=a.indexOf("(",3),c=a.indexOf(")",b+1),b=a.substring(b+1,c).split(",");if(4!=b.length||"a"!=a.charAt(3))b[3]=1;return b}function E(a,b,c){return Math.min(c,Math.max(b,a))}function F(a,b,c){0>c&&c++;1<c&&c--;return 1>6*c?a+6*(b-a)*c:
+1>2*c?b:2>3*c?a+6*(b-a)*(2/3-c):a}function G(a){if(a in H)return H[a];var b,c=1;a=String(a);if("#"==a.charAt(0))b=a;else if(/^rgb/.test(a)){c=Q(a);b="#";for(var g,e=0;3>e;e++)g=-1!=c[e].indexOf("%")?Math.floor(255*(parseFloat(c[e])/100)):+c[e],b+=v[E(g,0,255)];c=+c[3]}else if(/^hsl/.test(a)){e=c=Q(a);b=parseFloat(e[0])/360%360;0>b&&b++;g=E(parseFloat(e[1])/100,0,1);e=E(parseFloat(e[2])/100,0,1);if(0==g)g=e=b=e;else{var f=0.5>e?e*(1+g):e+g-e*g,d=2*e-f;g=F(d,f,b+1/3);e=F(d,f,b);b=F(d,f,b-1/3)}b="#"+
+v[Math.floor(255*g)]+v[Math.floor(255*e)]+v[Math.floor(255*b)];c=c[3]}else b=Z[a]||a;return H[a]={color:b,alpha:c}}function C(a){this.m_=D();this.mStack_=[];this.aStack_=[];this.currentPath_=[];this.fillStyle=this.strokeStyle="#000";this.lineWidth=1;this.lineJoin="miter";this.lineCap="butt";this.miterLimit=1*q;this.globalAlpha=1;this.font="10px sans-serif";this.textAlign="left";this.textBaseline="alphabetic";this.canvas=a;var b="width:"+a.clientWidth+"px;height:"+a.clientHeight+"px;overflow:hidden;position:absolute",
+c=a.ownerDocument.createElement("div");c.style.cssText=b;a.appendChild(c);b=c.cloneNode(!1);b.style.backgroundColor="red";b.style.filter="alpha(opacity=0)";a.appendChild(b);this.element_=c;this.lineScale_=this.arcScaleY_=this.arcScaleX_=1}function R(a,b,c,g){a.currentPath_.push({type:"bezierCurveTo",cp1x:b.x,cp1y:b.y,cp2x:c.x,cp2y:c.y,x:g.x,y:g.y});a.currentX_=g.x;a.currentY_=g.y}function S(a,b){var c=G(a.strokeStyle),g=c.color,c=c.alpha*a.globalAlpha,e=a.lineScale_*a.lineWidth;1>e&&(c*=e);b.push("<g_vml_:stroke",
+' opacity="',c,'"',' joinstyle="',a.lineJoin,'"',' miterlimit="',a.miterLimit,'"',' endcap="',$[a.lineCap]||"square",'"',' weight="',e,'px"',' color="',g,'" />')}function T(a,b,c,g){var e=a.fillStyle,f=a.arcScaleX_,d=a.arcScaleY_,k=g.x-c.x,n=g.y-c.y;if(e instanceof w){var h=0,l=g=0,u=0,m=1;if("gradient"==e.type_){h=e.x1_/f;c=e.y1_/d;var p=s(a,e.x0_/f,e.y0_/d),h=s(a,h,c),h=180*Math.atan2(h.x-p.x,h.y-p.y)/Math.PI;0>h&&(h+=360);1E-6>h&&(h=0)}else p=s(a,e.x0_,e.y0_),g=(p.x-c.x)/k,l=(p.y-c.y)/n,k/=f*q,
+n/=d*q,m=x.max(k,n),u=2*e.r0_/m,m=2*e.r1_/m-u;f=e.colors_;f.sort(function(a,b){return a.offset-b.offset});d=f.length;p=f[0].color;c=f[d-1].color;k=f[0].alpha*a.globalAlpha;a=f[d-1].alpha*a.globalAlpha;for(var n=[],r=0;r<d;r++){var t=f[r];n.push(t.offset*m+u+" "+t.color)}b.push('<g_vml_:fill type="',e.type_,'"',' method="none" focus="100%"',' color="',p,'"',' color2="',c,'"',' colors="',n.join(","),'"',' opacity="',a,'"',' g_o_:opacity2="',k,'"',' angle="',h,'"',' focusposition="',g,",",l,'" />')}else e instanceof
+I?k&&n&&b.push("<g_vml_:fill",' position="',-c.x/k*f*f,",",-c.y/n*d*d,'"',' type="tile"',' src="',e.src_,'" />'):(e=G(a.fillStyle),b.push('<g_vml_:fill color="',e.color,'" opacity="',e.alpha*a.globalAlpha,'" />'))}function s(a,b,c){a=a.m_;return{x:q*(b*a[0][0]+c*a[1][0]+a[2][0])-r,y:q*(b*a[0][1]+c*a[1][1]+a[2][1])-r}}function z(a,b,c){isFinite(b[0][0])&&(isFinite(b[0][1])&&isFinite(b[1][0])&&isFinite(b[1][1])&&isFinite(b[2][0])&&isFinite(b[2][1]))&&(a.m_=b,c&&(a.lineScale_=aa(ba(b[0][0]*b[1][1]-b[0][1]*
+b[1][0]))))}function w(a){this.type_=a;this.r1_=this.y1_=this.x1_=this.r0_=this.y0_=this.x0_=0;this.colors_=[]}function I(a,b){if(!a||1!=a.nodeType||"IMG"!=a.tagName)throw new A("TYPE_MISMATCH_ERR");if("complete"!=a.readyState)throw new A("INVALID_STATE_ERR");switch(b){case "repeat":case null:case "":this.repetition_="repeat";break;case "repeat-x":case "repeat-y":case "no-repeat":this.repetition_=b;break;default:throw new A("SYNTAX_ERR");}this.src_=a.src;this.width_=a.width;this.height_=a.height}
+function A(a){this.code=this[a];this.message=a+": DOM Exception "+this.code}var x=Math,k=x.round,J=x.sin,K=x.cos,ba=x.abs,aa=x.sqrt,q=10,r=q/2;navigator.userAgent.match(/MSIE ([\d.]+)?/);var M=Array.prototype.slice;O(document);var U={init:function(a){a=a||document;a.createElement("canvas");a.attachEvent("onreadystatechange",W(this.init_,this,a))},init_:function(a){a=a.getElementsByTagName("canvas");for(var b=0;b<a.length;b++)this.initElement(a[b])},initElement:function(a){if(!a.getContext){a.getContext=
+V;O(a.ownerDocument);a.innerHTML="";a.attachEvent("onpropertychange",X);a.attachEvent("onresize",Y);var b=a.attributes;b.width&&b.width.specified?a.style.width=b.width.nodeValue+"px":a.width=a.clientWidth;b.height&&b.height.specified?a.style.height=b.height.nodeValue+"px":a.height=a.clientHeight}return a}};U.init();for(var v=[],d=0;16>d;d++)for(var B=0;16>B;B++)v[16*d+B]=d.toString(16)+B.toString(16);var Z={aliceblue:"#F0F8FF",antiquewhite:"#FAEBD7",aquamarine:"#7FFFD4",azure:"#F0FFFF",beige:"#F5F5DC",
+bisque:"#FFE4C4",black:"#000000",blanchedalmond:"#FFEBCD",blueviolet:"#8A2BE2",brown:"#A52A2A",burlywood:"#DEB887",cadetblue:"#5F9EA0",chartreuse:"#7FFF00",chocolate:"#D2691E",coral:"#FF7F50",cornflowerblue:"#6495ED",cornsilk:"#FFF8DC",crimson:"#DC143C",cyan:"#00FFFF",darkblue:"#00008B",darkcyan:"#008B8B",darkgoldenrod:"#B8860B",darkgray:"#A9A9A9",darkgreen:"#006400",darkgrey:"#A9A9A9",darkkhaki:"#BDB76B",darkmagenta:"#8B008B",darkolivegreen:"#556B2F",darkorange:"#FF8C00",darkorchid:"#9932CC",darkred:"#8B0000",
+darksalmon:"#E9967A",darkseagreen:"#8FBC8F",darkslateblue:"#483D8B",darkslategray:"#2F4F4F",darkslategrey:"#2F4F4F",darkturquoise:"#00CED1",darkviolet:"#9400D3",deeppink:"#FF1493",deepskyblue:"#00BFFF",dimgray:"#696969",dimgrey:"#696969",dodgerblue:"#1E90FF",firebrick:"#B22222",floralwhite:"#FFFAF0",forestgreen:"#228B22",gainsboro:"#DCDCDC",ghostwhite:"#F8F8FF",gold:"#FFD700",goldenrod:"#DAA520",grey:"#808080",greenyellow:"#ADFF2F",honeydew:"#F0FFF0",hotpink:"#FF69B4",indianred:"#CD5C5C",indigo:"#4B0082",
+ivory:"#FFFFF0",khaki:"#F0E68C",lavender:"#E6E6FA",lavenderblush:"#FFF0F5",lawngreen:"#7CFC00",lemonchiffon:"#FFFACD",lightblue:"#ADD8E6",lightcoral:"#F08080",lightcyan:"#E0FFFF",lightgoldenrodyellow:"#FAFAD2",lightgreen:"#90EE90",lightgrey:"#D3D3D3",lightpink:"#FFB6C1",lightsalmon:"#FFA07A",lightseagreen:"#20B2AA",lightskyblue:"#87CEFA",lightslategray:"#778899",lightslategrey:"#778899",lightsteelblue:"#B0C4DE",lightyellow:"#FFFFE0",limegreen:"#32CD32",linen:"#FAF0E6",magenta:"#FF00FF",mediumaquamarine:"#66CDAA",
+mediumblue:"#0000CD",mediumorchid:"#BA55D3",mediumpurple:"#9370DB",mediumseagreen:"#3CB371",mediumslateblue:"#7B68EE",mediumspringgreen:"#00FA9A",mediumturquoise:"#48D1CC",mediumvioletred:"#C71585",midnightblue:"#191970",mintcream:"#F5FFFA",mistyrose:"#FFE4E1",moccasin:"#FFE4B5",navajowhite:"#FFDEAD",oldlace:"#FDF5E6",olivedrab:"#6B8E23",orange:"#FFA500",orangered:"#FF4500",orchid:"#DA70D6",palegoldenrod:"#EEE8AA",palegreen:"#98FB98",paleturquoise:"#AFEEEE",palevioletred:"#DB7093",papayawhip:"#FFEFD5",
+peachpuff:"#FFDAB9",peru:"#CD853F",pink:"#FFC0CB",plum:"#DDA0DD",powderblue:"#B0E0E6",rosybrown:"#BC8F8F",royalblue:"#4169E1",saddlebrown:"#8B4513",salmon:"#FA8072",sandybrown:"#F4A460",seagreen:"#2E8B57",seashell:"#FFF5EE",sienna:"#A0522D",skyblue:"#87CEEB",slateblue:"#6A5ACD",slategray:"#708090",slategrey:"#708090",snow:"#FFFAFA",springgreen:"#00FF7F",steelblue:"#4682B4",tan:"#D2B48C",thistle:"#D8BFD8",tomato:"#FF6347",turquoise:"#40E0D0",violet:"#EE82EE",wheat:"#F5DEB3",whitesmoke:"#F5F5F5",yellowgreen:"#9ACD32"},
+H={},L={},$={butt:"flat",round:"round"},d=C.prototype;d.clearRect=function(){this.textMeasureEl_&&(this.textMeasureEl_.removeNode(!0),this.textMeasureEl_=null);this.element_.innerHTML=""};d.beginPath=function(){this.currentPath_=[]};d.moveTo=function(a,b){var c=s(this,a,b);this.currentPath_.push({type:"moveTo",x:c.x,y:c.y});this.currentX_=c.x;this.currentY_=c.y};d.lineTo=function(a,b){var c=s(this,a,b);this.currentPath_.push({type:"lineTo",x:c.x,y:c.y});this.currentX_=c.x;this.currentY_=c.y};d.bezierCurveTo=
+function(a,b,c,g,e,f){e=s(this,e,f);a=s(this,a,b);c=s(this,c,g);R(this,a,c,e)};d.quadraticCurveTo=function(a,b,c,g){a=s(this,a,b);c=s(this,c,g);g={x:this.currentX_+2/3*(a.x-this.currentX_),y:this.currentY_+2/3*(a.y-this.currentY_)};R(this,g,{x:g.x+(c.x-this.currentX_)/3,y:g.y+(c.y-this.currentY_)/3},c)};d.arc=function(a,b,c,g,e,f){c*=q;var d=f?"at":"wa",k=a+K(g)*c-r,n=b+J(g)*c-r;g=a+K(e)*c-r;e=b+J(e)*c-r;k!=g||f||(k+=0.125);a=s(this,a,b);k=s(this,k,n);g=s(this,g,e);this.currentPath_.push({type:d,
+x:a.x,y:a.y,radius:c,xStart:k.x,yStart:k.y,xEnd:g.x,yEnd:g.y})};d.rect=function(a,b,c,g){this.moveTo(a,b);this.lineTo(a+c,b);this.lineTo(a+c,b+g);this.lineTo(a,b+g);this.closePath()};d.strokeRect=function(a,b,c,g){var e=this.currentPath_;this.beginPath();this.moveTo(a,b);this.lineTo(a+c,b);this.lineTo(a+c,b+g);this.lineTo(a,b+g);this.closePath();this.stroke();this.currentPath_=e};d.fillRect=function(a,b,c,g){var e=this.currentPath_;this.beginPath();this.moveTo(a,b);this.lineTo(a+c,b);this.lineTo(a+
+c,b+g);this.lineTo(a,b+g);this.closePath();this.fill();this.currentPath_=e};d.createLinearGradient=function(a,b,c,g){var e=new w("gradient");e.x0_=a;e.y0_=b;e.x1_=c;e.y1_=g;return e};d.createRadialGradient=function(a,b,c,g,e,f){var d=new w("gradientradial");d.x0_=a;d.y0_=b;d.r0_=c;d.x1_=g;d.y1_=e;d.r1_=f;return d};d.drawImage=function(a,b){var c,g,e,d,r,y,n,h;e=a.runtimeStyle.width;d=a.runtimeStyle.height;a.runtimeStyle.width="auto";a.runtimeStyle.height="auto";var l=a.width,u=a.height;a.runtimeStyle.width=
+e;a.runtimeStyle.height=d;if(3==arguments.length)c=arguments[1],g=arguments[2],r=y=0,n=e=l,h=d=u;else if(5==arguments.length)c=arguments[1],g=arguments[2],e=arguments[3],d=arguments[4],r=y=0,n=l,h=u;else if(9==arguments.length)r=arguments[1],y=arguments[2],n=arguments[3],h=arguments[4],c=arguments[5],g=arguments[6],e=arguments[7],d=arguments[8];else throw Error("Invalid number of arguments");var m=s(this,c,g),p=[];p.push(" <g_vml_:group",' coordsize="',10*q,",",10*q,'"',' coordorigin="0,0"',' style="width:',
+10,"px;height:",10,"px;position:absolute;");if(1!=this.m_[0][0]||this.m_[0][1]||1!=this.m_[1][1]||this.m_[1][0]){var t=[];t.push("M11=",this.m_[0][0],",","M12=",this.m_[1][0],",","M21=",this.m_[0][1],",","M22=",this.m_[1][1],",","Dx=",k(m.x/q),",","Dy=",k(m.y/q),"");var v=s(this,c+e,g),w=s(this,c,g+d);c=s(this,c+e,g+d);m.x=x.max(m.x,v.x,w.x,c.x);m.y=x.max(m.y,v.y,w.y,c.y);p.push("padding:0 ",k(m.x/q),"px ",k(m.y/q),"px 0;filter:progid:DXImageTransform.Microsoft.Matrix(",t.join(""),", sizingmethod='clip');")}else p.push("top:",
+k(m.y/q),"px;left:",k(m.x/q),"px;");p.push(' ">','<g_vml_:image src="',a.src,'"',' style="width:',q*e,"px;"," height:",q*d,'px"',' cropleft="',r/l,'"',' croptop="',y/u,'"',' cropright="',(l-r-n)/l,'"',' cropbottom="',(u-y-h)/u,'"'," />","</g_vml_:group>");this.element_.insertAdjacentHTML("BeforeEnd",p.join(""))};d.stroke=function(a){var b=[];b.push("<g_vml_:shape",' filled="',!!a,'"',' style="position:absolute;width:',10,"px;height:",10,'px;"',' coordorigin="0,0"',' coordsize="',10*q,",",10*q,'"',
+' stroked="',!a,'"',' path="');for(var c={x:null,y:null},d={x:null,y:null},e=0;e<this.currentPath_.length;e++){var f=this.currentPath_[e];switch(f.type){case "moveTo":b.push(" m ",k(f.x),",",k(f.y));break;case "lineTo":b.push(" l ",k(f.x),",",k(f.y));break;case "close":b.push(" x ");f=null;break;case "bezierCurveTo":b.push(" c ",k(f.cp1x),",",k(f.cp1y),",",k(f.cp2x),",",k(f.cp2y),",",k(f.x),",",k(f.y));break;case "at":case "wa":b.push(" ",f.type," ",k(f.x-this.arcScaleX_*f.radius),",",k(f.y-this.arcScaleY_*
+f.radius)," ",k(f.x+this.arcScaleX_*f.radius),",",k(f.y+this.arcScaleY_*f.radius)," ",k(f.xStart),",",k(f.yStart)," ",k(f.xEnd),",",k(f.yEnd))}if(f){if(null==c.x||f.x<c.x)c.x=f.x;if(null==d.x||f.x>d.x)d.x=f.x;if(null==c.y||f.y<c.y)c.y=f.y;if(null==d.y||f.y>d.y)d.y=f.y}}b.push(' ">');a?T(this,b,c,d):S(this,b);b.push("</g_vml_:shape>");this.element_.insertAdjacentHTML("beforeEnd",b.join(""))};d.fill=function(){this.stroke(!0)};d.closePath=function(){this.currentPath_.push({type:"close"})};d.save=function(){var a=
+{};P(this,a);this.aStack_.push(a);this.mStack_.push(this.m_);this.m_=t(D(),this.m_)};d.restore=function(){this.aStack_.length&&(P(this.aStack_.pop(),this),this.m_=this.mStack_.pop())};d.translate=function(a,b){z(this,t([[1,0,0],[0,1,0],[a,b,1]],this.m_),!1)};d.rotate=function(a){var b=K(a);a=J(a);z(this,t([[b,a,0],[-a,b,0],[0,0,1]],this.m_),!1)};d.scale=function(a,b){this.arcScaleX_*=a;this.arcScaleY_*=b;z(this,t([[a,0,0],[0,b,0],[0,0,1]],this.m_),!0)};d.transform=function(a,b,c,d,e,f){z(this,t([[a,
+b,0],[c,d,0],[e,f,1]],this.m_),!0)};d.setTransform=function(a,b,c,d,e,f){z(this,[[a,b,0],[c,d,0],[e,f,1]],!0)};d.drawText_=function(a,b,c,d,e){var f=this.m_;d=0;var r=1E3,t=0,n=[],h;h=this.font;if(L[h])h=L[h];else{var l=document.createElement("div").style;try{l.font=h}catch(u){}h=L[h]={style:l.fontStyle||"normal",variant:l.fontVariant||"normal",weight:l.fontWeight||"normal",size:l.fontSize||10,family:l.fontFamily||"sans-serif"}}var l=h,m=this.element_;h={};for(var p in l)h[p]=l[p];p=parseFloat(m.currentStyle.fontSize);
+m=parseFloat(l.size);"number"==typeof l.size?h.size=l.size:-1!=l.size.indexOf("px")?h.size=m:-1!=l.size.indexOf("em")?h.size=p*m:-1!=l.size.indexOf("%")?h.size=p/100*m:-1!=l.size.indexOf("pt")?h.size=m/0.75:h.size=p;h.size*=0.981;p=h.style+" "+h.variant+" "+h.weight+" "+h.size+"px "+h.family;m=this.element_.currentStyle;l=this.textAlign.toLowerCase();switch(l){case "left":case "center":case "right":break;case "end":l="ltr"==m.direction?"right":"left";break;case "start":l="rtl"==m.direction?"right":
+"left";break;default:l="left"}switch(this.textBaseline){case "hanging":case "top":t=h.size/1.75;break;case "middle":break;default:case null:case "alphabetic":case "ideographic":case "bottom":t=-h.size/2.25}switch(l){case "right":d=1E3;r=0.05;break;case "center":d=r=500}b=s(this,b+0,c+t);n.push('<g_vml_:line from="',-d,' 0" to="',r,' 0.05" ',' coordsize="100 100" coordorigin="0 0"',' filled="',!e,'" stroked="',!!e,'" style="position:absolute;width:1px;height:1px;">');e?S(this,n):T(this,n,{x:-d,y:0},
+{x:r,y:h.size});e=f[0][0].toFixed(3)+","+f[1][0].toFixed(3)+","+f[0][1].toFixed(3)+","+f[1][1].toFixed(3)+",0,0";b=k(b.x/q)+","+k(b.y/q);n.push('<g_vml_:skew on="t" matrix="',e,'" ',' offset="',b,'" origin="',d,' 0" />','<g_vml_:path textpathok="true" />','<g_vml_:textpath on="true" string="',N(a),'" style="v-text-align:',l,";font:",N(p),'" /></g_vml_:line>');this.element_.insertAdjacentHTML("beforeEnd",n.join(""))};d.fillText=function(a,b,c,d){this.drawText_(a,b,c,d,!1)};d.strokeText=function(a,
+b,c,d){this.drawText_(a,b,c,d,!0)};d.measureText=function(a){this.textMeasureEl_||(this.element_.insertAdjacentHTML("beforeEnd",'<span style="position:absolute;top:-20000px;left:0;padding:0;margin:0;border:none;white-space:pre;"></span>'),this.textMeasureEl_=this.element_.lastChild);var b=this.element_.ownerDocument;this.textMeasureEl_.innerHTML="";this.textMeasureEl_.style.font=this.font;this.textMeasureEl_.appendChild(b.createTextNode(a));return{width:this.textMeasureEl_.offsetWidth}};d.clip=function(){};
+d.arcTo=function(){};d.createPattern=function(a,b){return new I(a,b)};w.prototype.addColorStop=function(a,b){b=G(b);this.colors_.push({offset:a,color:b.color,alpha:b.alpha})};d=A.prototype=Error();d.INDEX_SIZE_ERR=1;d.DOMSTRING_SIZE_ERR=2;d.HIERARCHY_REQUEST_ERR=3;d.WRONG_DOCUMENT_ERR=4;d.INVALID_CHARACTER_ERR=5;d.NO_DATA_ALLOWED_ERR=6;d.NO_MODIFICATION_ALLOWED_ERR=7;d.NOT_FOUND_ERR=8;d.NOT_SUPPORTED_ERR=9;d.INUSE_ATTRIBUTE_ERR=10;d.INVALID_STATE_ERR=11;d.SYNTAX_ERR=12;d.INVALID_MODIFICATION_ERR=
+13;d.NAMESPACE_ERR=14;d.INVALID_ACCESS_ERR=15;d.VALIDATION_ERR=16;d.TYPE_MISMATCH_ERR=17;G_vmlCanvasManager=U;CanvasRenderingContext2D=C;CanvasGradient=w;CanvasPattern=I;DOMException=A}();
diff --git a/y2015/http_status/www/lib/jquery-1.4.4.js b/y2015/http_status/www/lib/jquery-1.4.4.js
new file mode 100644
index 0000000..a4f1145
--- /dev/null
+++ b/y2015/http_status/www/lib/jquery-1.4.4.js
@@ -0,0 +1,7179 @@
+/*!
+ * jQuery JavaScript Library v1.4.4
+ * http://jquery.com/
+ *
+ * Copyright 2010, John Resig
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * Includes Sizzle.js
+ * http://sizzlejs.com/
+ * Copyright 2010, The Dojo Foundation
+ * Released under the MIT, BSD, and GPL Licenses.
+ *
+ * Date: Thu Nov 11 19:04:53 2010 -0500
+ */
+(function( window, undefined ) {
+
+// Use the correct document accordingly with window argument (sandbox)
+var document = window.document;
+var jQuery = (function() {
+
+// Define a local copy of jQuery
+var jQuery = function( selector, context ) {
+ // The jQuery object is actually just the init constructor 'enhanced'
+ return new jQuery.fn.init( selector, context );
+ },
+
+ // Map over jQuery in case of overwrite
+ _jQuery = window.jQuery,
+
+ // Map over the $ in case of overwrite
+ _$ = window.$,
+
+ // A central reference to the root jQuery(document)
+ rootjQuery,
+
+ // A simple way to check for HTML strings or ID strings
+ // (both of which we optimize for)
+ quickExpr = /^(?:[^<]*(<[\w\W]+>)[^>]*$|#([\w\-]+)$)/,
+
+ // Is it a simple selector
+ isSimple = /^.[^:#\[\.,]*$/,
+
+ // Check if a string has a non-whitespace character in it
+ rnotwhite = /\S/,
+ rwhite = /\s/,
+
+ // Used for trimming whitespace
+ trimLeft = /^\s+/,
+ trimRight = /\s+$/,
+
+ // Check for non-word characters
+ rnonword = /\W/,
+
+ // Check for digits
+ rdigit = /\d/,
+
+ // Match a standalone tag
+ rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>)?$/,
+
+ // JSON RegExp
+ rvalidchars = /^[\],:{}\s]*$/,
+ rvalidescape = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,
+ rvalidtokens = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,
+ rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g,
+
+ // Useragent RegExp
+ rwebkit = /(webkit)[ \/]([\w.]+)/,
+ ropera = /(opera)(?:.*version)?[ \/]([\w.]+)/,
+ rmsie = /(msie) ([\w.]+)/,
+ rmozilla = /(mozilla)(?:.*? rv:([\w.]+))?/,
+
+ // Keep a UserAgent string for use with jQuery.browser
+ userAgent = navigator.userAgent,
+
+ // For matching the engine and version of the browser
+ browserMatch,
+
+ // Has the ready events already been bound?
+ readyBound = false,
+
+ // The functions to execute on DOM ready
+ readyList = [],
+
+ // The ready event handler
+ DOMContentLoaded,
+
+ // Save a reference to some core methods
+ toString = Object.prototype.toString,
+ hasOwn = Object.prototype.hasOwnProperty,
+ push = Array.prototype.push,
+ slice = Array.prototype.slice,
+ trim = String.prototype.trim,
+ indexOf = Array.prototype.indexOf,
+
+ // [[Class]] -> type pairs
+ class2type = {};
+
+jQuery.fn = jQuery.prototype = {
+ init: function( selector, context ) {
+ var match, elem, ret, doc;
+
+ // Handle $(""), $(null), or $(undefined)
+ if ( !selector ) {
+ return this;
+ }
+
+ // Handle $(DOMElement)
+ if ( selector.nodeType ) {
+ this.context = this[0] = selector;
+ this.length = 1;
+ return this;
+ }
+
+ // The body element only exists once, optimize finding it
+ if ( selector === "body" && !context && document.body ) {
+ this.context = document;
+ this[0] = document.body;
+ this.selector = "body";
+ this.length = 1;
+ return this;
+ }
+
+ // Handle HTML strings
+ if ( typeof selector === "string" ) {
+ // Are we dealing with HTML string or an ID?
+ match = quickExpr.exec( selector );
+
+ // Verify a match, and that no context was specified for #id
+ if ( match && (match[1] || !context) ) {
+
+ // HANDLE: $(html) -> $(array)
+ if ( match[1] ) {
+ doc = (context ? context.ownerDocument || context : document);
+
+ // If a single string is passed in and it's a single tag
+ // just do a createElement and skip the rest
+ ret = rsingleTag.exec( selector );
+
+ if ( ret ) {
+ if ( jQuery.isPlainObject( context ) ) {
+ selector = [ document.createElement( ret[1] ) ];
+ jQuery.fn.attr.call( selector, context, true );
+
+ } else {
+ selector = [ doc.createElement( ret[1] ) ];
+ }
+
+ } else {
+ ret = jQuery.buildFragment( [ match[1] ], [ doc ] );
+ selector = (ret.cacheable ? ret.fragment.cloneNode(true) : ret.fragment).childNodes;
+ }
+
+ return jQuery.merge( this, selector );
+
+ // HANDLE: $("#id")
+ } else {
+ elem = document.getElementById( match[2] );
+
+ // Check parentNode to catch when Blackberry 4.6 returns
+ // nodes that are no longer in the document #6963
+ if ( elem && elem.parentNode ) {
+ // Handle the case where IE and Opera return items
+ // by name instead of ID
+ if ( elem.id !== match[2] ) {
+ return rootjQuery.find( selector );
+ }
+
+ // Otherwise, we inject the element directly into the jQuery object
+ this.length = 1;
+ this[0] = elem;
+ }
+
+ this.context = document;
+ this.selector = selector;
+ return this;
+ }
+
+ // HANDLE: $("TAG")
+ } else if ( !context && !rnonword.test( selector ) ) {
+ this.selector = selector;
+ this.context = document;
+ selector = document.getElementsByTagName( selector );
+ return jQuery.merge( this, selector );
+
+ // HANDLE: $(expr, $(...))
+ } else if ( !context || context.jquery ) {
+ return (context || rootjQuery).find( selector );
+
+ // HANDLE: $(expr, context)
+ // (which is just equivalent to: $(context).find(expr)
+ } else {
+ return jQuery( context ).find( selector );
+ }
+
+ // HANDLE: $(function)
+ // Shortcut for document ready
+ } else if ( jQuery.isFunction( selector ) ) {
+ return rootjQuery.ready( selector );
+ }
+
+ if (selector.selector !== undefined) {
+ this.selector = selector.selector;
+ this.context = selector.context;
+ }
+
+ return jQuery.makeArray( selector, this );
+ },
+
+ // Start with an empty selector
+ selector: "",
+
+ // The current version of jQuery being used
+ jquery: "1.4.4",
+
+ // The default length of a jQuery object is 0
+ length: 0,
+
+ // The number of elements contained in the matched element set
+ size: function() {
+ return this.length;
+ },
+
+ toArray: function() {
+ return slice.call( this, 0 );
+ },
+
+ // Get the Nth element in the matched element set OR
+ // Get the whole matched element set as a clean array
+ get: function( num ) {
+ return num == null ?
+
+ // Return a 'clean' array
+ this.toArray() :
+
+ // Return just the object
+ ( num < 0 ? this.slice(num)[ 0 ] : this[ num ] );
+ },
+
+ // Take an array of elements and push it onto the stack
+ // (returning the new matched element set)
+ pushStack: function( elems, name, selector ) {
+ // Build a new jQuery matched element set
+ var ret = jQuery();
+
+ if ( jQuery.isArray( elems ) ) {
+ push.apply( ret, elems );
+
+ } else {
+ jQuery.merge( ret, elems );
+ }
+
+ // Add the old object onto the stack (as a reference)
+ ret.prevObject = this;
+
+ ret.context = this.context;
+
+ if ( name === "find" ) {
+ ret.selector = this.selector + (this.selector ? " " : "") + selector;
+ } else if ( name ) {
+ ret.selector = this.selector + "." + name + "(" + selector + ")";
+ }
+
+ // Return the newly-formed element set
+ return ret;
+ },
+
+ // Execute a callback for every element in the matched set.
+ // (You can seed the arguments with an array of args, but this is
+ // only used internally.)
+ each: function( callback, args ) {
+ return jQuery.each( this, callback, args );
+ },
+
+ ready: function( fn ) {
+ // Attach the listeners
+ jQuery.bindReady();
+
+ // If the DOM is already ready
+ if ( jQuery.isReady ) {
+ // Execute the function immediately
+ fn.call( document, jQuery );
+
+ // Otherwise, remember the function for later
+ } else if ( readyList ) {
+ // Add the function to the wait list
+ readyList.push( fn );
+ }
+
+ return this;
+ },
+
+ eq: function( i ) {
+ return i === -1 ?
+ this.slice( i ) :
+ this.slice( i, +i + 1 );
+ },
+
+ first: function() {
+ return this.eq( 0 );
+ },
+
+ last: function() {
+ return this.eq( -1 );
+ },
+
+ slice: function() {
+ return this.pushStack( slice.apply( this, arguments ),
+ "slice", slice.call(arguments).join(",") );
+ },
+
+ map: function( callback ) {
+ return this.pushStack( jQuery.map(this, function( elem, i ) {
+ return callback.call( elem, i, elem );
+ }));
+ },
+
+ end: function() {
+ return this.prevObject || jQuery(null);
+ },
+
+ // For internal use only.
+ // Behaves like an Array's method, not like a jQuery method.
+ push: push,
+ sort: [].sort,
+ splice: [].splice
+};
+
+// Give the init function the jQuery prototype for later instantiation
+jQuery.fn.init.prototype = jQuery.fn;
+
+jQuery.extend = jQuery.fn.extend = function() {
+ var options, name, src, copy, copyIsArray, clone,
+ target = arguments[0] || {},
+ i = 1,
+ length = arguments.length,
+ deep = false;
+
+ // Handle a deep copy situation
+ if ( typeof target === "boolean" ) {
+ deep = target;
+ target = arguments[1] || {};
+ // skip the boolean and the target
+ i = 2;
+ }
+
+ // Handle case when target is a string or something (possible in deep copy)
+ if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
+ target = {};
+ }
+
+ // extend jQuery itself if only one argument is passed
+ if ( length === i ) {
+ target = this;
+ --i;
+ }
+
+ for ( ; i < length; i++ ) {
+ // Only deal with non-null/undefined values
+ if ( (options = arguments[ i ]) != null ) {
+ // Extend the base object
+ for ( name in options ) {
+ src = target[ name ];
+ copy = options[ name ];
+
+ // Prevent never-ending loop
+ if ( target === copy ) {
+ continue;
+ }
+
+ // Recurse if we're merging plain objects or arrays
+ if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {
+ if ( copyIsArray ) {
+ copyIsArray = false;
+ clone = src && jQuery.isArray(src) ? src : [];
+
+ } else {
+ clone = src && jQuery.isPlainObject(src) ? src : {};
+ }
+
+ // Never move original objects, clone them
+ target[ name ] = jQuery.extend( deep, clone, copy );
+
+ // Don't bring in undefined values
+ } else if ( copy !== undefined ) {
+ target[ name ] = copy;
+ }
+ }
+ }
+ }
+
+ // Return the modified object
+ return target;
+};
+
+jQuery.extend({
+ noConflict: function( deep ) {
+ window.$ = _$;
+
+ if ( deep ) {
+ window.jQuery = _jQuery;
+ }
+
+ return jQuery;
+ },
+
+ // Is the DOM ready to be used? Set to true once it occurs.
+ isReady: false,
+
+ // A counter to track how many items to wait for before
+ // the ready event fires. See #6781
+ readyWait: 1,
+
+ // Handle when the DOM is ready
+ ready: function( wait ) {
+ // A third-party is pushing the ready event forwards
+ if ( wait === true ) {
+ jQuery.readyWait--;
+ }
+
+ // Make sure that the DOM is not already loaded
+ if ( !jQuery.readyWait || (wait !== true && !jQuery.isReady) ) {
+ // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
+ if ( !document.body ) {
+ return setTimeout( jQuery.ready, 1 );
+ }
+
+ // Remember that the DOM is ready
+ jQuery.isReady = true;
+
+ // If a normal DOM Ready event fired, decrement, and wait if need be
+ if ( wait !== true && --jQuery.readyWait > 0 ) {
+ return;
+ }
+
+ // If there are functions bound, to execute
+ if ( readyList ) {
+ // Execute all of them
+ var fn,
+ i = 0,
+ ready = readyList;
+
+ // Reset the list of functions
+ readyList = null;
+
+ while ( (fn = ready[ i++ ]) ) {
+ fn.call( document, jQuery );
+ }
+
+ // Trigger any bound ready events
+ if ( jQuery.fn.trigger ) {
+ jQuery( document ).trigger( "ready" ).unbind( "ready" );
+ }
+ }
+ }
+ },
+
+ bindReady: function() {
+ if ( readyBound ) {
+ return;
+ }
+
+ readyBound = true;
+
+ // Catch cases where $(document).ready() is called after the
+ // browser event has already occurred.
+ if ( document.readyState === "complete" ) {
+ // Handle it asynchronously to allow scripts the opportunity to delay ready
+ return setTimeout( jQuery.ready, 1 );
+ }
+
+ // Mozilla, Opera and webkit nightlies currently support this event
+ if ( document.addEventListener ) {
+ // Use the handy event callback
+ document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false );
+
+ // A fallback to window.onload, that will always work
+ window.addEventListener( "load", jQuery.ready, false );
+
+ // If IE event model is used
+ } else if ( document.attachEvent ) {
+ // ensure firing before onload,
+ // maybe late but safe also for iframes
+ document.attachEvent("onreadystatechange", DOMContentLoaded);
+
+ // A fallback to window.onload, that will always work
+ window.attachEvent( "onload", jQuery.ready );
+
+ // If IE and not a frame
+ // continually check to see if the document is ready
+ var toplevel = false;
+
+ try {
+ toplevel = window.frameElement == null;
+ } catch(e) {}
+
+ if ( document.documentElement.doScroll && toplevel ) {
+ doScrollCheck();
+ }
+ }
+ },
+
+ // See test/unit/core.js for details concerning isFunction.
+ // Since version 1.3, DOM methods and functions like alert
+ // aren't supported. They return false on IE (#2968).
+ isFunction: function( obj ) {
+ return jQuery.type(obj) === "function";
+ },
+
+ isArray: Array.isArray || function( obj ) {
+ return jQuery.type(obj) === "array";
+ },
+
+ // A crude way of determining if an object is a window
+ isWindow: function( obj ) {
+ return obj && typeof obj === "object" && "setInterval" in obj;
+ },
+
+ isNaN: function( obj ) {
+ return obj == null || !rdigit.test( obj ) || isNaN( obj );
+ },
+
+ type: function( obj ) {
+ return obj == null ?
+ String( obj ) :
+ class2type[ toString.call(obj) ] || "object";
+ },
+
+ isPlainObject: function( obj ) {
+ // Must be an Object.
+ // Because of IE, we also have to check the presence of the constructor property.
+ // Make sure that DOM nodes and window objects don't pass through, as well
+ if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) {
+ return false;
+ }
+
+ // Not own constructor property must be Object
+ if ( obj.constructor &&
+ !hasOwn.call(obj, "constructor") &&
+ !hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) {
+ return false;
+ }
+
+ // Own properties are enumerated firstly, so to speed up,
+ // if last one is own, then all properties are own.
+
+ var key;
+ for ( key in obj ) {}
+
+ return key === undefined || hasOwn.call( obj, key );
+ },
+
+ isEmptyObject: function( obj ) {
+ for ( var name in obj ) {
+ return false;
+ }
+ return true;
+ },
+
+ error: function( msg ) {
+ throw msg;
+ },
+
+ parseJSON: function( data ) {
+ if ( typeof data !== "string" || !data ) {
+ return null;
+ }
+
+ // Make sure leading/trailing whitespace is removed (IE can't handle it)
+ data = jQuery.trim( data );
+
+ // Make sure the incoming data is actual JSON
+ // Logic borrowed from http://json.org/json2.js
+ if ( rvalidchars.test(data.replace(rvalidescape, "@")
+ .replace(rvalidtokens, "]")
+ .replace(rvalidbraces, "")) ) {
+
+ // Try to use the native JSON parser first
+ return window.JSON && window.JSON.parse ?
+ window.JSON.parse( data ) :
+ (new Function("return " + data))();
+
+ } else {
+ jQuery.error( "Invalid JSON: " + data );
+ }
+ },
+
+ noop: function() {},
+
+ // Evalulates a script in a global context
+ globalEval: function( data ) {
+ if ( data && rnotwhite.test(data) ) {
+ // Inspired by code by Andrea Giammarchi
+ // http://webreflection.blogspot.com/2007/08/global-scope-evaluation-and-dom.html
+ var head = document.getElementsByTagName("head")[0] || document.documentElement,
+ script = document.createElement("script");
+
+ script.type = "text/javascript";
+
+ if ( jQuery.support.scriptEval ) {
+ script.appendChild( document.createTextNode( data ) );
+ } else {
+ script.text = data;
+ }
+
+ // Use insertBefore instead of appendChild to circumvent an IE6 bug.
+ // This arises when a base node is used (#2709).
+ head.insertBefore( script, head.firstChild );
+ head.removeChild( script );
+ }
+ },
+
+ nodeName: function( elem, name ) {
+ return elem.nodeName && elem.nodeName.toUpperCase() === name.toUpperCase();
+ },
+
+ // args is for internal usage only
+ each: function( object, callback, args ) {
+ var name, i = 0,
+ length = object.length,
+ isObj = length === undefined || jQuery.isFunction(object);
+
+ if ( args ) {
+ if ( isObj ) {
+ for ( name in object ) {
+ if ( callback.apply( object[ name ], args ) === false ) {
+ break;
+ }
+ }
+ } else {
+ for ( ; i < length; ) {
+ if ( callback.apply( object[ i++ ], args ) === false ) {
+ break;
+ }
+ }
+ }
+
+ // A special, fast, case for the most common use of each
+ } else {
+ if ( isObj ) {
+ for ( name in object ) {
+ if ( callback.call( object[ name ], name, object[ name ] ) === false ) {
+ break;
+ }
+ }
+ } else {
+ for ( var value = object[0];
+ i < length && callback.call( value, i, value ) !== false; value = object[++i] ) {}
+ }
+ }
+
+ return object;
+ },
+
+ // Use native String.trim function wherever possible
+ trim: trim ?
+ function( text ) {
+ return text == null ?
+ "" :
+ trim.call( text );
+ } :
+
+ // Otherwise use our own trimming functionality
+ function( text ) {
+ return text == null ?
+ "" :
+ text.toString().replace( trimLeft, "" ).replace( trimRight, "" );
+ },
+
+ // results is for internal usage only
+ makeArray: function( array, results ) {
+ var ret = results || [];
+
+ if ( array != null ) {
+ // The window, strings (and functions) also have 'length'
+ // The extra typeof function check is to prevent crashes
+ // in Safari 2 (See: #3039)
+ // Tweaked logic slightly to handle Blackberry 4.7 RegExp issues #6930
+ var type = jQuery.type(array);
+
+ if ( array.length == null || type === "string" || type === "function" || type === "regexp" || jQuery.isWindow( array ) ) {
+ push.call( ret, array );
+ } else {
+ jQuery.merge( ret, array );
+ }
+ }
+
+ return ret;
+ },
+
+ inArray: function( elem, array ) {
+ if ( array.indexOf ) {
+ return array.indexOf( elem );
+ }
+
+ for ( var i = 0, length = array.length; i < length; i++ ) {
+ if ( array[ i ] === elem ) {
+ return i;
+ }
+ }
+
+ return -1;
+ },
+
+ merge: function( first, second ) {
+ var i = first.length,
+ j = 0;
+
+ if ( typeof second.length === "number" ) {
+ for ( var l = second.length; j < l; j++ ) {
+ first[ i++ ] = second[ j ];
+ }
+
+ } else {
+ while ( second[j] !== undefined ) {
+ first[ i++ ] = second[ j++ ];
+ }
+ }
+
+ first.length = i;
+
+ return first;
+ },
+
+ grep: function( elems, callback, inv ) {
+ var ret = [], retVal;
+ inv = !!inv;
+
+ // Go through the array, only saving the items
+ // that pass the validator function
+ for ( var i = 0, length = elems.length; i < length; i++ ) {
+ retVal = !!callback( elems[ i ], i );
+ if ( inv !== retVal ) {
+ ret.push( elems[ i ] );
+ }
+ }
+
+ return ret;
+ },
+
+ // arg is for internal usage only
+ map: function( elems, callback, arg ) {
+ var ret = [], value;
+
+ // Go through the array, translating each of the items to their
+ // new value (or values).
+ for ( var i = 0, length = elems.length; i < length; i++ ) {
+ value = callback( elems[ i ], i, arg );
+
+ if ( value != null ) {
+ ret[ ret.length ] = value;
+ }
+ }
+
+ return ret.concat.apply( [], ret );
+ },
+
+ // A global GUID counter for objects
+ guid: 1,
+
+ proxy: function( fn, proxy, thisObject ) {
+ if ( arguments.length === 2 ) {
+ if ( typeof proxy === "string" ) {
+ thisObject = fn;
+ fn = thisObject[ proxy ];
+ proxy = undefined;
+
+ } else if ( proxy && !jQuery.isFunction( proxy ) ) {
+ thisObject = proxy;
+ proxy = undefined;
+ }
+ }
+
+ if ( !proxy && fn ) {
+ proxy = function() {
+ return fn.apply( thisObject || this, arguments );
+ };
+ }
+
+ // Set the guid of unique handler to the same of original handler, so it can be removed
+ if ( fn ) {
+ proxy.guid = fn.guid = fn.guid || proxy.guid || jQuery.guid++;
+ }
+
+ // So proxy can be declared as an argument
+ return proxy;
+ },
+
+ // Mutifunctional method to get and set values to a collection
+ // The value/s can be optionally by executed if its a function
+ access: function( elems, key, value, exec, fn, pass ) {
+ var length = elems.length;
+
+ // Setting many attributes
+ if ( typeof key === "object" ) {
+ for ( var k in key ) {
+ jQuery.access( elems, k, key[k], exec, fn, value );
+ }
+ return elems;
+ }
+
+ // Setting one attribute
+ if ( value !== undefined ) {
+ // Optionally, function values get executed if exec is true
+ exec = !pass && exec && jQuery.isFunction(value);
+
+ for ( var i = 0; i < length; i++ ) {
+ fn( elems[i], key, exec ? value.call( elems[i], i, fn( elems[i], key ) ) : value, pass );
+ }
+
+ return elems;
+ }
+
+ // Getting an attribute
+ return length ? fn( elems[0], key ) : undefined;
+ },
+
+ now: function() {
+ return (new Date()).getTime();
+ },
+
+ // Use of jQuery.browser is frowned upon.
+ // More details: http://docs.jquery.com/Utilities/jQuery.browser
+ uaMatch: function( ua ) {
+ ua = ua.toLowerCase();
+
+ var match = rwebkit.exec( ua ) ||
+ ropera.exec( ua ) ||
+ rmsie.exec( ua ) ||
+ ua.indexOf("compatible") < 0 && rmozilla.exec( ua ) ||
+ [];
+
+ return { browser: match[1] || "", version: match[2] || "0" };
+ },
+
+ browser: {}
+});
+
+// Populate the class2type map
+jQuery.each("Boolean Number String Function Array Date RegExp Object".split(" "), function(i, name) {
+ class2type[ "[object " + name + "]" ] = name.toLowerCase();
+});
+
+browserMatch = jQuery.uaMatch( userAgent );
+if ( browserMatch.browser ) {
+ jQuery.browser[ browserMatch.browser ] = true;
+ jQuery.browser.version = browserMatch.version;
+}
+
+// Deprecated, use jQuery.browser.webkit instead
+if ( jQuery.browser.webkit ) {
+ jQuery.browser.safari = true;
+}
+
+if ( indexOf ) {
+ jQuery.inArray = function( elem, array ) {
+ return indexOf.call( array, elem );
+ };
+}
+
+// Verify that \s matches non-breaking spaces
+// (IE fails on this test)
+if ( !rwhite.test( "\xA0" ) ) {
+ trimLeft = /^[\s\xA0]+/;
+ trimRight = /[\s\xA0]+$/;
+}
+
+// All jQuery objects should point back to these
+rootjQuery = jQuery(document);
+
+// Cleanup functions for the document ready method
+if ( document.addEventListener ) {
+ DOMContentLoaded = function() {
+ document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false );
+ jQuery.ready();
+ };
+
+} else if ( document.attachEvent ) {
+ DOMContentLoaded = function() {
+ // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
+ if ( document.readyState === "complete" ) {
+ document.detachEvent( "onreadystatechange", DOMContentLoaded );
+ jQuery.ready();
+ }
+ };
+}
+
+// The DOM ready check for Internet Explorer
+function doScrollCheck() {
+ if ( jQuery.isReady ) {
+ return;
+ }
+
+ try {
+ // If IE is used, use the trick by Diego Perini
+ // http://javascript.nwbox.com/IEContentLoaded/
+ document.documentElement.doScroll("left");
+ } catch(e) {
+ setTimeout( doScrollCheck, 1 );
+ return;
+ }
+
+ // and execute any waiting functions
+ jQuery.ready();
+}
+
+// Expose jQuery to the global object
+return (window.jQuery = window.$ = jQuery);
+
+})();
+
+
+(function() {
+
+ jQuery.support = {};
+
+ var root = document.documentElement,
+ script = document.createElement("script"),
+ div = document.createElement("div"),
+ id = "script" + jQuery.now();
+
+ div.style.display = "none";
+ div.innerHTML = " <link/><table></table><a href='/a' style='color:red;float:left;opacity:.55;'>a</a><input type='checkbox'/>";
+
+ var all = div.getElementsByTagName("*"),
+ a = div.getElementsByTagName("a")[0],
+ select = document.createElement("select"),
+ opt = select.appendChild( document.createElement("option") );
+
+ // Can't get basic test support
+ if ( !all || !all.length || !a ) {
+ return;
+ }
+
+ jQuery.support = {
+ // IE strips leading whitespace when .innerHTML is used
+ leadingWhitespace: div.firstChild.nodeType === 3,
+
+ // Make sure that tbody elements aren't automatically inserted
+ // IE will insert them into empty tables
+ tbody: !div.getElementsByTagName("tbody").length,
+
+ // Make sure that link elements get serialized correctly by innerHTML
+ // This requires a wrapper element in IE
+ htmlSerialize: !!div.getElementsByTagName("link").length,
+
+ // Get the style information from getAttribute
+ // (IE uses .cssText insted)
+ style: /red/.test( a.getAttribute("style") ),
+
+ // Make sure that URLs aren't manipulated
+ // (IE normalizes it by default)
+ hrefNormalized: a.getAttribute("href") === "/a",
+
+ // Make sure that element opacity exists
+ // (IE uses filter instead)
+ // Use a regex to work around a WebKit issue. See #5145
+ opacity: /^0.55$/.test( a.style.opacity ),
+
+ // Verify style float existence
+ // (IE uses styleFloat instead of cssFloat)
+ cssFloat: !!a.style.cssFloat,
+
+ // Make sure that if no value is specified for a checkbox
+ // that it defaults to "on".
+ // (WebKit defaults to "" instead)
+ checkOn: div.getElementsByTagName("input")[0].value === "on",
+
+ // Make sure that a selected-by-default option has a working selected property.
+ // (WebKit defaults to false instead of true, IE too, if it's in an optgroup)
+ optSelected: opt.selected,
+
+ // Will be defined later
+ deleteExpando: true,
+ optDisabled: false,
+ checkClone: false,
+ scriptEval: false,
+ noCloneEvent: true,
+ boxModel: null,
+ inlineBlockNeedsLayout: false,
+ shrinkWrapBlocks: false,
+ reliableHiddenOffsets: true
+ };
+
+ // Make sure that the options inside disabled selects aren't marked as disabled
+ // (WebKit marks them as diabled)
+ select.disabled = true;
+ jQuery.support.optDisabled = !opt.disabled;
+
+ script.type = "text/javascript";
+ try {
+ script.appendChild( document.createTextNode( "window." + id + "=1;" ) );
+ } catch(e) {}
+
+ root.insertBefore( script, root.firstChild );
+
+ // Make sure that the execution of code works by injecting a script
+ // tag with appendChild/createTextNode
+ // (IE doesn't support this, fails, and uses .text instead)
+ if ( window[ id ] ) {
+ jQuery.support.scriptEval = true;
+ delete window[ id ];
+ }
+
+ // Test to see if it's possible to delete an expando from an element
+ // Fails in Internet Explorer
+ try {
+ delete script.test;
+
+ } catch(e) {
+ jQuery.support.deleteExpando = false;
+ }
+
+ root.removeChild( script );
+
+ if ( div.attachEvent && div.fireEvent ) {
+ div.attachEvent("onclick", function click() {
+ // Cloning a node shouldn't copy over any
+ // bound event handlers (IE does this)
+ jQuery.support.noCloneEvent = false;
+ div.detachEvent("onclick", click);
+ });
+ div.cloneNode(true).fireEvent("onclick");
+ }
+
+ div = document.createElement("div");
+ div.innerHTML = "<input type='radio' name='radiotest' checked='checked'/>";
+
+ var fragment = document.createDocumentFragment();
+ fragment.appendChild( div.firstChild );
+
+ // WebKit doesn't clone checked state correctly in fragments
+ jQuery.support.checkClone = fragment.cloneNode(true).cloneNode(true).lastChild.checked;
+
+ // Figure out if the W3C box model works as expected
+ // document.body must exist before we can do this
+ jQuery(function() {
+ var div = document.createElement("div");
+ div.style.width = div.style.paddingLeft = "1px";
+
+ document.body.appendChild( div );
+ jQuery.boxModel = jQuery.support.boxModel = div.offsetWidth === 2;
+
+ if ( "zoom" in div.style ) {
+ // Check if natively block-level elements act like inline-block
+ // elements when setting their display to 'inline' and giving
+ // them layout
+ // (IE < 8 does this)
+ div.style.display = "inline";
+ div.style.zoom = 1;
+ jQuery.support.inlineBlockNeedsLayout = div.offsetWidth === 2;
+
+ // Check if elements with layout shrink-wrap their children
+ // (IE 6 does this)
+ div.style.display = "";
+ div.innerHTML = "<div style='width:4px;'></div>";
+ jQuery.support.shrinkWrapBlocks = div.offsetWidth !== 2;
+ }
+
+ div.innerHTML = "<table><tr><td style='padding:0;display:none'></td><td>t</td></tr></table>";
+ var tds = div.getElementsByTagName("td");
+
+ // Check if table cells still have offsetWidth/Height when they are set
+ // to display:none and there are still other visible table cells in a
+ // table row; if so, offsetWidth/Height are not reliable for use when
+ // determining if an element has been hidden directly using
+ // display:none (it is still safe to use offsets if a parent element is
+ // hidden; don safety goggles and see bug #4512 for more information).
+ // (only IE 8 fails this test)
+ jQuery.support.reliableHiddenOffsets = tds[0].offsetHeight === 0;
+
+ tds[0].style.display = "";
+ tds[1].style.display = "none";
+
+ // Check if empty table cells still have offsetWidth/Height
+ // (IE < 8 fail this test)
+ jQuery.support.reliableHiddenOffsets = jQuery.support.reliableHiddenOffsets && tds[0].offsetHeight === 0;
+ div.innerHTML = "";
+
+ document.body.removeChild( div ).style.display = "none";
+ div = tds = null;
+ });
+
+ // Technique from Juriy Zaytsev
+ // http://thinkweb2.com/projects/prototype/detecting-event-support-without-browser-sniffing/
+ var eventSupported = function( eventName ) {
+ var el = document.createElement("div");
+ eventName = "on" + eventName;
+
+ var isSupported = (eventName in el);
+ if ( !isSupported ) {
+ el.setAttribute(eventName, "return;");
+ isSupported = typeof el[eventName] === "function";
+ }
+ el = null;
+
+ return isSupported;
+ };
+
+ jQuery.support.submitBubbles = eventSupported("submit");
+ jQuery.support.changeBubbles = eventSupported("change");
+
+ // release memory in IE
+ root = script = div = all = a = null;
+})();
+
+
+
+var windowData = {},
+ rbrace = /^(?:\{.*\}|\[.*\])$/;
+
+jQuery.extend({
+ cache: {},
+
+ // Please use with caution
+ uuid: 0,
+
+ // Unique for each copy of jQuery on the page
+ expando: "jQuery" + jQuery.now(),
+
+ // The following elements throw uncatchable exceptions if you
+ // attempt to add expando properties to them.
+ noData: {
+ "embed": true,
+ // Ban all objects except for Flash (which handle expandos)
+ "object": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",
+ "applet": true
+ },
+
+ data: function( elem, name, data ) {
+ if ( !jQuery.acceptData( elem ) ) {
+ return;
+ }
+
+ elem = elem == window ?
+ windowData :
+ elem;
+
+ var isNode = elem.nodeType,
+ id = isNode ? elem[ jQuery.expando ] : null,
+ cache = jQuery.cache, thisCache;
+
+ if ( isNode && !id && typeof name === "string" && data === undefined ) {
+ return;
+ }
+
+ // Get the data from the object directly
+ if ( !isNode ) {
+ cache = elem;
+
+ // Compute a unique ID for the element
+ } else if ( !id ) {
+ elem[ jQuery.expando ] = id = ++jQuery.uuid;
+ }
+
+ // Avoid generating a new cache unless none exists and we
+ // want to manipulate it.
+ if ( typeof name === "object" ) {
+ if ( isNode ) {
+ cache[ id ] = jQuery.extend(cache[ id ], name);
+
+ } else {
+ jQuery.extend( cache, name );
+ }
+
+ } else if ( isNode && !cache[ id ] ) {
+ cache[ id ] = {};
+ }
+
+ thisCache = isNode ? cache[ id ] : cache;
+
+ // Prevent overriding the named cache with undefined values
+ if ( data !== undefined ) {
+ thisCache[ name ] = data;
+ }
+
+ return typeof name === "string" ? thisCache[ name ] : thisCache;
+ },
+
+ removeData: function( elem, name ) {
+ if ( !jQuery.acceptData( elem ) ) {
+ return;
+ }
+
+ elem = elem == window ?
+ windowData :
+ elem;
+
+ var isNode = elem.nodeType,
+ id = isNode ? elem[ jQuery.expando ] : elem,
+ cache = jQuery.cache,
+ thisCache = isNode ? cache[ id ] : id;
+
+ // If we want to remove a specific section of the element's data
+ if ( name ) {
+ if ( thisCache ) {
+ // Remove the section of cache data
+ delete thisCache[ name ];
+
+ // If we've removed all the data, remove the element's cache
+ if ( isNode && jQuery.isEmptyObject(thisCache) ) {
+ jQuery.removeData( elem );
+ }
+ }
+
+ // Otherwise, we want to remove all of the element's data
+ } else {
+ if ( isNode && jQuery.support.deleteExpando ) {
+ delete elem[ jQuery.expando ];
+
+ } else if ( elem.removeAttribute ) {
+ elem.removeAttribute( jQuery.expando );
+
+ // Completely remove the data cache
+ } else if ( isNode ) {
+ delete cache[ id ];
+
+ // Remove all fields from the object
+ } else {
+ for ( var n in elem ) {
+ delete elem[ n ];
+ }
+ }
+ }
+ },
+
+ // A method for determining if a DOM node can handle the data expando
+ acceptData: function( elem ) {
+ if ( elem.nodeName ) {
+ var match = jQuery.noData[ elem.nodeName.toLowerCase() ];
+
+ if ( match ) {
+ return !(match === true || elem.getAttribute("classid") !== match);
+ }
+ }
+
+ return true;
+ }
+});
+
+jQuery.fn.extend({
+ data: function( key, value ) {
+ var data = null;
+
+ if ( typeof key === "undefined" ) {
+ if ( this.length ) {
+ var attr = this[0].attributes, name;
+ data = jQuery.data( this[0] );
+
+ for ( var i = 0, l = attr.length; i < l; i++ ) {
+ name = attr[i].name;
+
+ if ( name.indexOf( "data-" ) === 0 ) {
+ name = name.substr( 5 );
+ dataAttr( this[0], name, data[ name ] );
+ }
+ }
+ }
+
+ return data;
+
+ } else if ( typeof key === "object" ) {
+ return this.each(function() {
+ jQuery.data( this, key );
+ });
+ }
+
+ var parts = key.split(".");
+ parts[1] = parts[1] ? "." + parts[1] : "";
+
+ if ( value === undefined ) {
+ data = this.triggerHandler("getData" + parts[1] + "!", [parts[0]]);
+
+ // Try to fetch any internally stored data first
+ if ( data === undefined && this.length ) {
+ data = jQuery.data( this[0], key );
+ data = dataAttr( this[0], key, data );
+ }
+
+ return data === undefined && parts[1] ?
+ this.data( parts[0] ) :
+ data;
+
+ } else {
+ return this.each(function() {
+ var $this = jQuery( this ),
+ args = [ parts[0], value ];
+
+ $this.triggerHandler( "setData" + parts[1] + "!", args );
+ jQuery.data( this, key, value );
+ $this.triggerHandler( "changeData" + parts[1] + "!", args );
+ });
+ }
+ },
+
+ removeData: function( key ) {
+ return this.each(function() {
+ jQuery.removeData( this, key );
+ });
+ }
+});
+
+function dataAttr( elem, key, data ) {
+ // If nothing was found internally, try to fetch any
+ // data from the HTML5 data-* attribute
+ if ( data === undefined && elem.nodeType === 1 ) {
+ data = elem.getAttribute( "data-" + key );
+
+ if ( typeof data === "string" ) {
+ try {
+ data = data === "true" ? true :
+ data === "false" ? false :
+ data === "null" ? null :
+ !jQuery.isNaN( data ) ? parseFloat( data ) :
+ rbrace.test( data ) ? jQuery.parseJSON( data ) :
+ data;
+ } catch( e ) {}
+
+ // Make sure we set the data so it isn't changed later
+ jQuery.data( elem, key, data );
+
+ } else {
+ data = undefined;
+ }
+ }
+
+ return data;
+}
+
+
+
+
+jQuery.extend({
+ queue: function( elem, type, data ) {
+ if ( !elem ) {
+ return;
+ }
+
+ type = (type || "fx") + "queue";
+ var q = jQuery.data( elem, type );
+
+ // Speed up dequeue by getting out quickly if this is just a lookup
+ if ( !data ) {
+ return q || [];
+ }
+
+ if ( !q || jQuery.isArray(data) ) {
+ q = jQuery.data( elem, type, jQuery.makeArray(data) );
+
+ } else {
+ q.push( data );
+ }
+
+ return q;
+ },
+
+ dequeue: function( elem, type ) {
+ type = type || "fx";
+
+ var queue = jQuery.queue( elem, type ),
+ fn = queue.shift();
+
+ // If the fx queue is dequeued, always remove the progress sentinel
+ if ( fn === "inprogress" ) {
+ fn = queue.shift();
+ }
+
+ if ( fn ) {
+ // Add a progress sentinel to prevent the fx queue from being
+ // automatically dequeued
+ if ( type === "fx" ) {
+ queue.unshift("inprogress");
+ }
+
+ fn.call(elem, function() {
+ jQuery.dequeue(elem, type);
+ });
+ }
+ }
+});
+
+jQuery.fn.extend({
+ queue: function( type, data ) {
+ if ( typeof type !== "string" ) {
+ data = type;
+ type = "fx";
+ }
+
+ if ( data === undefined ) {
+ return jQuery.queue( this[0], type );
+ }
+ return this.each(function( i ) {
+ var queue = jQuery.queue( this, type, data );
+
+ if ( type === "fx" && queue[0] !== "inprogress" ) {
+ jQuery.dequeue( this, type );
+ }
+ });
+ },
+ dequeue: function( type ) {
+ return this.each(function() {
+ jQuery.dequeue( this, type );
+ });
+ },
+
+ // Based off of the plugin by Clint Helfers, with permission.
+ // http://blindsignals.com/index.php/2009/07/jquery-delay/
+ delay: function( time, type ) {
+ time = jQuery.fx ? jQuery.fx.speeds[time] || time : time;
+ type = type || "fx";
+
+ return this.queue( type, function() {
+ var elem = this;
+ setTimeout(function() {
+ jQuery.dequeue( elem, type );
+ }, time );
+ });
+ },
+
+ clearQueue: function( type ) {
+ return this.queue( type || "fx", [] );
+ }
+});
+
+
+
+
+var rclass = /[\n\t]/g,
+ rspaces = /\s+/,
+ rreturn = /\r/g,
+ rspecialurl = /^(?:href|src|style)$/,
+ rtype = /^(?:button|input)$/i,
+ rfocusable = /^(?:button|input|object|select|textarea)$/i,
+ rclickable = /^a(?:rea)?$/i,
+ rradiocheck = /^(?:radio|checkbox)$/i;
+
+jQuery.props = {
+ "for": "htmlFor",
+ "class": "className",
+ readonly: "readOnly",
+ maxlength: "maxLength",
+ cellspacing: "cellSpacing",
+ rowspan: "rowSpan",
+ colspan: "colSpan",
+ tabindex: "tabIndex",
+ usemap: "useMap",
+ frameborder: "frameBorder"
+};
+
+jQuery.fn.extend({
+ attr: function( name, value ) {
+ return jQuery.access( this, name, value, true, jQuery.attr );
+ },
+
+ removeAttr: function( name, fn ) {
+ return this.each(function(){
+ jQuery.attr( this, name, "" );
+ if ( this.nodeType === 1 ) {
+ this.removeAttribute( name );
+ }
+ });
+ },
+
+ addClass: function( value ) {
+ if ( jQuery.isFunction(value) ) {
+ return this.each(function(i) {
+ var self = jQuery(this);
+ self.addClass( value.call(this, i, self.attr("class")) );
+ });
+ }
+
+ if ( value && typeof value === "string" ) {
+ var classNames = (value || "").split( rspaces );
+
+ for ( var i = 0, l = this.length; i < l; i++ ) {
+ var elem = this[i];
+
+ if ( elem.nodeType === 1 ) {
+ if ( !elem.className ) {
+ elem.className = value;
+
+ } else {
+ var className = " " + elem.className + " ",
+ setClass = elem.className;
+
+ for ( var c = 0, cl = classNames.length; c < cl; c++ ) {
+ if ( className.indexOf( " " + classNames[c] + " " ) < 0 ) {
+ setClass += " " + classNames[c];
+ }
+ }
+ elem.className = jQuery.trim( setClass );
+ }
+ }
+ }
+ }
+
+ return this;
+ },
+
+ removeClass: function( value ) {
+ if ( jQuery.isFunction(value) ) {
+ return this.each(function(i) {
+ var self = jQuery(this);
+ self.removeClass( value.call(this, i, self.attr("class")) );
+ });
+ }
+
+ if ( (value && typeof value === "string") || value === undefined ) {
+ var classNames = (value || "").split( rspaces );
+
+ for ( var i = 0, l = this.length; i < l; i++ ) {
+ var elem = this[i];
+
+ if ( elem.nodeType === 1 && elem.className ) {
+ if ( value ) {
+ var className = (" " + elem.className + " ").replace(rclass, " ");
+ for ( var c = 0, cl = classNames.length; c < cl; c++ ) {
+ className = className.replace(" " + classNames[c] + " ", " ");
+ }
+ elem.className = jQuery.trim( className );
+
+ } else {
+ elem.className = "";
+ }
+ }
+ }
+ }
+
+ return this;
+ },
+
+ toggleClass: function( value, stateVal ) {
+ var type = typeof value,
+ isBool = typeof stateVal === "boolean";
+
+ if ( jQuery.isFunction( value ) ) {
+ return this.each(function(i) {
+ var self = jQuery(this);
+ self.toggleClass( value.call(this, i, self.attr("class"), stateVal), stateVal );
+ });
+ }
+
+ return this.each(function() {
+ if ( type === "string" ) {
+ // toggle individual class names
+ var className,
+ i = 0,
+ self = jQuery( this ),
+ state = stateVal,
+ classNames = value.split( rspaces );
+
+ while ( (className = classNames[ i++ ]) ) {
+ // check each className given, space seperated list
+ state = isBool ? state : !self.hasClass( className );
+ self[ state ? "addClass" : "removeClass" ]( className );
+ }
+
+ } else if ( type === "undefined" || type === "boolean" ) {
+ if ( this.className ) {
+ // store className if set
+ jQuery.data( this, "__className__", this.className );
+ }
+
+ // toggle whole className
+ this.className = this.className || value === false ? "" : jQuery.data( this, "__className__" ) || "";
+ }
+ });
+ },
+
+ hasClass: function( selector ) {
+ var className = " " + selector + " ";
+ for ( var i = 0, l = this.length; i < l; i++ ) {
+ if ( (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) > -1 ) {
+ return true;
+ }
+ }
+
+ return false;
+ },
+
+ val: function( value ) {
+ if ( !arguments.length ) {
+ var elem = this[0];
+
+ if ( elem ) {
+ if ( jQuery.nodeName( elem, "option" ) ) {
+ // attributes.value is undefined in Blackberry 4.7 but
+ // uses .value. See #6932
+ var val = elem.attributes.value;
+ return !val || val.specified ? elem.value : elem.text;
+ }
+
+ // We need to handle select boxes special
+ if ( jQuery.nodeName( elem, "select" ) ) {
+ var index = elem.selectedIndex,
+ values = [],
+ options = elem.options,
+ one = elem.type === "select-one";
+
+ // Nothing was selected
+ if ( index < 0 ) {
+ return null;
+ }
+
+ // Loop through all the selected options
+ for ( var i = one ? index : 0, max = one ? index + 1 : options.length; i < max; i++ ) {
+ var option = options[ i ];
+
+ // Don't return options that are disabled or in a disabled optgroup
+ if ( option.selected && (jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null) &&
+ (!option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" )) ) {
+
+ // Get the specific value for the option
+ value = jQuery(option).val();
+
+ // We don't need an array for one selects
+ if ( one ) {
+ return value;
+ }
+
+ // Multi-Selects return an array
+ values.push( value );
+ }
+ }
+
+ return values;
+ }
+
+ // Handle the case where in Webkit "" is returned instead of "on" if a value isn't specified
+ if ( rradiocheck.test( elem.type ) && !jQuery.support.checkOn ) {
+ return elem.getAttribute("value") === null ? "on" : elem.value;
+ }
+
+
+ // Everything else, we just grab the value
+ return (elem.value || "").replace(rreturn, "");
+
+ }
+
+ return undefined;
+ }
+
+ var isFunction = jQuery.isFunction(value);
+
+ return this.each(function(i) {
+ var self = jQuery(this), val = value;
+
+ if ( this.nodeType !== 1 ) {
+ return;
+ }
+
+ if ( isFunction ) {
+ val = value.call(this, i, self.val());
+ }
+
+ // Treat null/undefined as ""; convert numbers to string
+ if ( val == null ) {
+ val = "";
+ } else if ( typeof val === "number" ) {
+ val += "";
+ } else if ( jQuery.isArray(val) ) {
+ val = jQuery.map(val, function (value) {
+ return value == null ? "" : value + "";
+ });
+ }
+
+ if ( jQuery.isArray(val) && rradiocheck.test( this.type ) ) {
+ this.checked = jQuery.inArray( self.val(), val ) >= 0;
+
+ } else if ( jQuery.nodeName( this, "select" ) ) {
+ var values = jQuery.makeArray(val);
+
+ jQuery( "option", this ).each(function() {
+ this.selected = jQuery.inArray( jQuery(this).val(), values ) >= 0;
+ });
+
+ if ( !values.length ) {
+ this.selectedIndex = -1;
+ }
+
+ } else {
+ this.value = val;
+ }
+ });
+ }
+});
+
+jQuery.extend({
+ attrFn: {
+ val: true,
+ css: true,
+ html: true,
+ text: true,
+ data: true,
+ width: true,
+ height: true,
+ offset: true
+ },
+
+ attr: function( elem, name, value, pass ) {
+ // don't set attributes on text and comment nodes
+ if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 ) {
+ return undefined;
+ }
+
+ if ( pass && name in jQuery.attrFn ) {
+ return jQuery(elem)[name](value);
+ }
+
+ var notxml = elem.nodeType !== 1 || !jQuery.isXMLDoc( elem ),
+ // Whether we are setting (or getting)
+ set = value !== undefined;
+
+ // Try to normalize/fix the name
+ name = notxml && jQuery.props[ name ] || name;
+
+ // These attributes require special treatment
+ var special = rspecialurl.test( name );
+
+ // Safari mis-reports the default selected property of an option
+ // Accessing the parent's selectedIndex property fixes it
+ if ( name === "selected" && !jQuery.support.optSelected ) {
+ var parent = elem.parentNode;
+ if ( parent ) {
+ parent.selectedIndex;
+
+ // Make sure that it also works with optgroups, see #5701
+ if ( parent.parentNode ) {
+ parent.parentNode.selectedIndex;
+ }
+ }
+ }
+
+ // If applicable, access the attribute via the DOM 0 way
+ // 'in' checks fail in Blackberry 4.7 #6931
+ if ( (name in elem || elem[ name ] !== undefined) && notxml && !special ) {
+ if ( set ) {
+ // We can't allow the type property to be changed (since it causes problems in IE)
+ if ( name === "type" && rtype.test( elem.nodeName ) && elem.parentNode ) {
+ jQuery.error( "type property can't be changed" );
+ }
+
+ if ( value === null ) {
+ if ( elem.nodeType === 1 ) {
+ elem.removeAttribute( name );
+ }
+
+ } else {
+ elem[ name ] = value;
+ }
+ }
+
+ // browsers index elements by id/name on forms, give priority to attributes.
+ if ( jQuery.nodeName( elem, "form" ) && elem.getAttributeNode(name) ) {
+ return elem.getAttributeNode( name ).nodeValue;
+ }
+
+ // elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set
+ // http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/
+ if ( name === "tabIndex" ) {
+ var attributeNode = elem.getAttributeNode( "tabIndex" );
+
+ return attributeNode && attributeNode.specified ?
+ attributeNode.value :
+ rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ?
+ 0 :
+ undefined;
+ }
+
+ return elem[ name ];
+ }
+
+ if ( !jQuery.support.style && notxml && name === "style" ) {
+ if ( set ) {
+ elem.style.cssText = "" + value;
+ }
+
+ return elem.style.cssText;
+ }
+
+ if ( set ) {
+ // convert the value to a string (all browsers do this but IE) see #1070
+ elem.setAttribute( name, "" + value );
+ }
+
+ // Ensure that missing attributes return undefined
+ // Blackberry 4.7 returns "" from getAttribute #6938
+ if ( !elem.attributes[ name ] && (elem.hasAttribute && !elem.hasAttribute( name )) ) {
+ return undefined;
+ }
+
+ var attr = !jQuery.support.hrefNormalized && notxml && special ?
+ // Some attributes require a special call on IE
+ elem.getAttribute( name, 2 ) :
+ elem.getAttribute( name );
+
+ // Non-existent attributes return null, we normalize to undefined
+ return attr === null ? undefined : attr;
+ }
+});
+
+
+
+
+var rnamespaces = /\.(.*)$/,
+ rformElems = /^(?:textarea|input|select)$/i,
+ rperiod = /\./g,
+ rspace = / /g,
+ rescape = /[^\w\s.|`]/g,
+ fcleanup = function( nm ) {
+ return nm.replace(rescape, "\\$&");
+ },
+ focusCounts = { focusin: 0, focusout: 0 };
+
+/*
+ * A number of helper functions used for managing events.
+ * Many of the ideas behind this code originated from
+ * Dean Edwards' addEvent library.
+ */
+jQuery.event = {
+
+ // Bind an event to an element
+ // Original by Dean Edwards
+ add: function( elem, types, handler, data ) {
+ if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
+ return;
+ }
+
+ // For whatever reason, IE has trouble passing the window object
+ // around, causing it to be cloned in the process
+ if ( jQuery.isWindow( elem ) && ( elem !== window && !elem.frameElement ) ) {
+ elem = window;
+ }
+
+ if ( handler === false ) {
+ handler = returnFalse;
+ } else if ( !handler ) {
+ // Fixes bug #7229. Fix recommended by jdalton
+ return;
+ }
+
+ var handleObjIn, handleObj;
+
+ if ( handler.handler ) {
+ handleObjIn = handler;
+ handler = handleObjIn.handler;
+ }
+
+ // Make sure that the function being executed has a unique ID
+ if ( !handler.guid ) {
+ handler.guid = jQuery.guid++;
+ }
+
+ // Init the element's event structure
+ var elemData = jQuery.data( elem );
+
+ // If no elemData is found then we must be trying to bind to one of the
+ // banned noData elements
+ if ( !elemData ) {
+ return;
+ }
+
+ // Use a key less likely to result in collisions for plain JS objects.
+ // Fixes bug #7150.
+ var eventKey = elem.nodeType ? "events" : "__events__",
+ events = elemData[ eventKey ],
+ eventHandle = elemData.handle;
+
+ if ( typeof events === "function" ) {
+ // On plain objects events is a fn that holds the the data
+ // which prevents this data from being JSON serialized
+ // the function does not need to be called, it just contains the data
+ eventHandle = events.handle;
+ events = events.events;
+
+ } else if ( !events ) {
+ if ( !elem.nodeType ) {
+ // On plain objects, create a fn that acts as the holder
+ // of the values to avoid JSON serialization of event data
+ elemData[ eventKey ] = elemData = function(){};
+ }
+
+ elemData.events = events = {};
+ }
+
+ if ( !eventHandle ) {
+ elemData.handle = eventHandle = function() {
+ // Handle the second event of a trigger and when
+ // an event is called after a page has unloaded
+ return typeof jQuery !== "undefined" && !jQuery.event.triggered ?
+ jQuery.event.handle.apply( eventHandle.elem, arguments ) :
+ undefined;
+ };
+ }
+
+ // Add elem as a property of the handle function
+ // This is to prevent a memory leak with non-native events in IE.
+ eventHandle.elem = elem;
+
+ // Handle multiple events separated by a space
+ // jQuery(...).bind("mouseover mouseout", fn);
+ types = types.split(" ");
+
+ var type, i = 0, namespaces;
+
+ while ( (type = types[ i++ ]) ) {
+ handleObj = handleObjIn ?
+ jQuery.extend({}, handleObjIn) :
+ { handler: handler, data: data };
+
+ // Namespaced event handlers
+ if ( type.indexOf(".") > -1 ) {
+ namespaces = type.split(".");
+ type = namespaces.shift();
+ handleObj.namespace = namespaces.slice(0).sort().join(".");
+
+ } else {
+ namespaces = [];
+ handleObj.namespace = "";
+ }
+
+ handleObj.type = type;
+ if ( !handleObj.guid ) {
+ handleObj.guid = handler.guid;
+ }
+
+ // Get the current list of functions bound to this event
+ var handlers = events[ type ],
+ special = jQuery.event.special[ type ] || {};
+
+ // Init the event handler queue
+ if ( !handlers ) {
+ handlers = events[ type ] = [];
+
+ // Check for a special event handler
+ // Only use addEventListener/attachEvent if the special
+ // events handler returns false
+ if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) {
+ // Bind the global event handler to the element
+ if ( elem.addEventListener ) {
+ elem.addEventListener( type, eventHandle, false );
+
+ } else if ( elem.attachEvent ) {
+ elem.attachEvent( "on" + type, eventHandle );
+ }
+ }
+ }
+
+ if ( special.add ) {
+ special.add.call( elem, handleObj );
+
+ if ( !handleObj.handler.guid ) {
+ handleObj.handler.guid = handler.guid;
+ }
+ }
+
+ // Add the function to the element's handler list
+ handlers.push( handleObj );
+
+ // Keep track of which events have been used, for global triggering
+ jQuery.event.global[ type ] = true;
+ }
+
+ // Nullify elem to prevent memory leaks in IE
+ elem = null;
+ },
+
+ global: {},
+
+ // Detach an event or set of events from an element
+ remove: function( elem, types, handler, pos ) {
+ // don't do events on text and comment nodes
+ if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
+ return;
+ }
+
+ if ( handler === false ) {
+ handler = returnFalse;
+ }
+
+ var ret, type, fn, j, i = 0, all, namespaces, namespace, special, eventType, handleObj, origType,
+ eventKey = elem.nodeType ? "events" : "__events__",
+ elemData = jQuery.data( elem ),
+ events = elemData && elemData[ eventKey ];
+
+ if ( !elemData || !events ) {
+ return;
+ }
+
+ if ( typeof events === "function" ) {
+ elemData = events;
+ events = events.events;
+ }
+
+ // types is actually an event object here
+ if ( types && types.type ) {
+ handler = types.handler;
+ types = types.type;
+ }
+
+ // Unbind all events for the element
+ if ( !types || typeof types === "string" && types.charAt(0) === "." ) {
+ types = types || "";
+
+ for ( type in events ) {
+ jQuery.event.remove( elem, type + types );
+ }
+
+ return;
+ }
+
+ // Handle multiple events separated by a space
+ // jQuery(...).unbind("mouseover mouseout", fn);
+ types = types.split(" ");
+
+ while ( (type = types[ i++ ]) ) {
+ origType = type;
+ handleObj = null;
+ all = type.indexOf(".") < 0;
+ namespaces = [];
+
+ if ( !all ) {
+ // Namespaced event handlers
+ namespaces = type.split(".");
+ type = namespaces.shift();
+
+ namespace = new RegExp("(^|\\.)" +
+ jQuery.map( namespaces.slice(0).sort(), fcleanup ).join("\\.(?:.*\\.)?") + "(\\.|$)");
+ }
+
+ eventType = events[ type ];
+
+ if ( !eventType ) {
+ continue;
+ }
+
+ if ( !handler ) {
+ for ( j = 0; j < eventType.length; j++ ) {
+ handleObj = eventType[ j ];
+
+ if ( all || namespace.test( handleObj.namespace ) ) {
+ jQuery.event.remove( elem, origType, handleObj.handler, j );
+ eventType.splice( j--, 1 );
+ }
+ }
+
+ continue;
+ }
+
+ special = jQuery.event.special[ type ] || {};
+
+ for ( j = pos || 0; j < eventType.length; j++ ) {
+ handleObj = eventType[ j ];
+
+ if ( handler.guid === handleObj.guid ) {
+ // remove the given handler for the given type
+ if ( all || namespace.test( handleObj.namespace ) ) {
+ if ( pos == null ) {
+ eventType.splice( j--, 1 );
+ }
+
+ if ( special.remove ) {
+ special.remove.call( elem, handleObj );
+ }
+ }
+
+ if ( pos != null ) {
+ break;
+ }
+ }
+ }
+
+ // remove generic event handler if no more handlers exist
+ if ( eventType.length === 0 || pos != null && eventType.length === 1 ) {
+ if ( !special.teardown || special.teardown.call( elem, namespaces ) === false ) {
+ jQuery.removeEvent( elem, type, elemData.handle );
+ }
+
+ ret = null;
+ delete events[ type ];
+ }
+ }
+
+ // Remove the expando if it's no longer used
+ if ( jQuery.isEmptyObject( events ) ) {
+ var handle = elemData.handle;
+ if ( handle ) {
+ handle.elem = null;
+ }
+
+ delete elemData.events;
+ delete elemData.handle;
+
+ if ( typeof elemData === "function" ) {
+ jQuery.removeData( elem, eventKey );
+
+ } else if ( jQuery.isEmptyObject( elemData ) ) {
+ jQuery.removeData( elem );
+ }
+ }
+ },
+
+ // bubbling is internal
+ trigger: function( event, data, elem /*, bubbling */ ) {
+ // Event object or event type
+ var type = event.type || event,
+ bubbling = arguments[3];
+
+ if ( !bubbling ) {
+ event = typeof event === "object" ?
+ // jQuery.Event object
+ event[ jQuery.expando ] ? event :
+ // Object literal
+ jQuery.extend( jQuery.Event(type), event ) :
+ // Just the event type (string)
+ jQuery.Event(type);
+
+ if ( type.indexOf("!") >= 0 ) {
+ event.type = type = type.slice(0, -1);
+ event.exclusive = true;
+ }
+
+ // Handle a global trigger
+ if ( !elem ) {
+ // Don't bubble custom events when global (to avoid too much overhead)
+ event.stopPropagation();
+
+ // Only trigger if we've ever bound an event for it
+ if ( jQuery.event.global[ type ] ) {
+ jQuery.each( jQuery.cache, function() {
+ if ( this.events && this.events[type] ) {
+ jQuery.event.trigger( event, data, this.handle.elem );
+ }
+ });
+ }
+ }
+
+ // Handle triggering a single element
+
+ // don't do events on text and comment nodes
+ if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 ) {
+ return undefined;
+ }
+
+ // Clean up in case it is reused
+ event.result = undefined;
+ event.target = elem;
+
+ // Clone the incoming data, if any
+ data = jQuery.makeArray( data );
+ data.unshift( event );
+ }
+
+ event.currentTarget = elem;
+
+ // Trigger the event, it is assumed that "handle" is a function
+ var handle = elem.nodeType ?
+ jQuery.data( elem, "handle" ) :
+ (jQuery.data( elem, "__events__" ) || {}).handle;
+
+ if ( handle ) {
+ handle.apply( elem, data );
+ }
+
+ var parent = elem.parentNode || elem.ownerDocument;
+
+ // Trigger an inline bound script
+ try {
+ if ( !(elem && elem.nodeName && jQuery.noData[elem.nodeName.toLowerCase()]) ) {
+ if ( elem[ "on" + type ] && elem[ "on" + type ].apply( elem, data ) === false ) {
+ event.result = false;
+ event.preventDefault();
+ }
+ }
+
+ // prevent IE from throwing an error for some elements with some event types, see #3533
+ } catch (inlineError) {}
+
+ if ( !event.isPropagationStopped() && parent ) {
+ jQuery.event.trigger( event, data, parent, true );
+
+ } else if ( !event.isDefaultPrevented() ) {
+ var old,
+ target = event.target,
+ targetType = type.replace( rnamespaces, "" ),
+ isClick = jQuery.nodeName( target, "a" ) && targetType === "click",
+ special = jQuery.event.special[ targetType ] || {};
+
+ if ( (!special._default || special._default.call( elem, event ) === false) &&
+ !isClick && !(target && target.nodeName && jQuery.noData[target.nodeName.toLowerCase()]) ) {
+
+ try {
+ if ( target[ targetType ] ) {
+ // Make sure that we don't accidentally re-trigger the onFOO events
+ old = target[ "on" + targetType ];
+
+ if ( old ) {
+ target[ "on" + targetType ] = null;
+ }
+
+ jQuery.event.triggered = true;
+ target[ targetType ]();
+ }
+
+ // prevent IE from throwing an error for some elements with some event types, see #3533
+ } catch (triggerError) {}
+
+ if ( old ) {
+ target[ "on" + targetType ] = old;
+ }
+
+ jQuery.event.triggered = false;
+ }
+ }
+ },
+
+ handle: function( event ) {
+ var all, handlers, namespaces, namespace_re, events,
+ namespace_sort = [],
+ args = jQuery.makeArray( arguments );
+
+ event = args[0] = jQuery.event.fix( event || window.event );
+ event.currentTarget = this;
+
+ // Namespaced event handlers
+ all = event.type.indexOf(".") < 0 && !event.exclusive;
+
+ if ( !all ) {
+ namespaces = event.type.split(".");
+ event.type = namespaces.shift();
+ namespace_sort = namespaces.slice(0).sort();
+ namespace_re = new RegExp("(^|\\.)" + namespace_sort.join("\\.(?:.*\\.)?") + "(\\.|$)");
+ }
+
+ event.namespace = event.namespace || namespace_sort.join(".");
+
+ events = jQuery.data(this, this.nodeType ? "events" : "__events__");
+
+ if ( typeof events === "function" ) {
+ events = events.events;
+ }
+
+ handlers = (events || {})[ event.type ];
+
+ if ( events && handlers ) {
+ // Clone the handlers to prevent manipulation
+ handlers = handlers.slice(0);
+
+ for ( var j = 0, l = handlers.length; j < l; j++ ) {
+ var handleObj = handlers[ j ];
+
+ // Filter the functions by class
+ if ( all || namespace_re.test( handleObj.namespace ) ) {
+ // Pass in a reference to the handler function itself
+ // So that we can later remove it
+ event.handler = handleObj.handler;
+ event.data = handleObj.data;
+ event.handleObj = handleObj;
+
+ var ret = handleObj.handler.apply( this, args );
+
+ if ( ret !== undefined ) {
+ event.result = ret;
+ if ( ret === false ) {
+ event.preventDefault();
+ event.stopPropagation();
+ }
+ }
+
+ if ( event.isImmediatePropagationStopped() ) {
+ break;
+ }
+ }
+ }
+ }
+
+ return event.result;
+ },
+
+ props: "altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),
+
+ fix: function( event ) {
+ if ( event[ jQuery.expando ] ) {
+ return event;
+ }
+
+ // store a copy of the original event object
+ // and "clone" to set read-only properties
+ var originalEvent = event;
+ event = jQuery.Event( originalEvent );
+
+ for ( var i = this.props.length, prop; i; ) {
+ prop = this.props[ --i ];
+ event[ prop ] = originalEvent[ prop ];
+ }
+
+ // Fix target property, if necessary
+ if ( !event.target ) {
+ // Fixes #1925 where srcElement might not be defined either
+ event.target = event.srcElement || document;
+ }
+
+ // check if target is a textnode (safari)
+ if ( event.target.nodeType === 3 ) {
+ event.target = event.target.parentNode;
+ }
+
+ // Add relatedTarget, if necessary
+ if ( !event.relatedTarget && event.fromElement ) {
+ event.relatedTarget = event.fromElement === event.target ? event.toElement : event.fromElement;
+ }
+
+ // Calculate pageX/Y if missing and clientX/Y available
+ if ( event.pageX == null && event.clientX != null ) {
+ var doc = document.documentElement,
+ body = document.body;
+
+ event.pageX = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0);
+ event.pageY = event.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc && doc.clientTop || body && body.clientTop || 0);
+ }
+
+ // Add which for key events
+ if ( event.which == null && (event.charCode != null || event.keyCode != null) ) {
+ event.which = event.charCode != null ? event.charCode : event.keyCode;
+ }
+
+ // Add metaKey to non-Mac browsers (use ctrl for PC's and Meta for Macs)
+ if ( !event.metaKey && event.ctrlKey ) {
+ event.metaKey = event.ctrlKey;
+ }
+
+ // Add which for click: 1 === left; 2 === middle; 3 === right
+ // Note: button is not normalized, so don't use it
+ if ( !event.which && event.button !== undefined ) {
+ event.which = (event.button & 1 ? 1 : ( event.button & 2 ? 3 : ( event.button & 4 ? 2 : 0 ) ));
+ }
+
+ return event;
+ },
+
+ // Deprecated, use jQuery.guid instead
+ guid: 1E8,
+
+ // Deprecated, use jQuery.proxy instead
+ proxy: jQuery.proxy,
+
+ special: {
+ ready: {
+ // Make sure the ready event is setup
+ setup: jQuery.bindReady,
+ teardown: jQuery.noop
+ },
+
+ live: {
+ add: function( handleObj ) {
+ jQuery.event.add( this,
+ liveConvert( handleObj.origType, handleObj.selector ),
+ jQuery.extend({}, handleObj, {handler: liveHandler, guid: handleObj.handler.guid}) );
+ },
+
+ remove: function( handleObj ) {
+ jQuery.event.remove( this, liveConvert( handleObj.origType, handleObj.selector ), handleObj );
+ }
+ },
+
+ beforeunload: {
+ setup: function( data, namespaces, eventHandle ) {
+ // We only want to do this special case on windows
+ if ( jQuery.isWindow( this ) ) {
+ this.onbeforeunload = eventHandle;
+ }
+ },
+
+ teardown: function( namespaces, eventHandle ) {
+ if ( this.onbeforeunload === eventHandle ) {
+ this.onbeforeunload = null;
+ }
+ }
+ }
+ }
+};
+
+jQuery.removeEvent = document.removeEventListener ?
+ function( elem, type, handle ) {
+ if ( elem.removeEventListener ) {
+ elem.removeEventListener( type, handle, false );
+ }
+ } :
+ function( elem, type, handle ) {
+ if ( elem.detachEvent ) {
+ elem.detachEvent( "on" + type, handle );
+ }
+ };
+
+jQuery.Event = function( src ) {
+ // Allow instantiation without the 'new' keyword
+ if ( !this.preventDefault ) {
+ return new jQuery.Event( src );
+ }
+
+ // Event object
+ if ( src && src.type ) {
+ this.originalEvent = src;
+ this.type = src.type;
+ // Event type
+ } else {
+ this.type = src;
+ }
+
+ // timeStamp is buggy for some events on Firefox(#3843)
+ // So we won't rely on the native value
+ this.timeStamp = jQuery.now();
+
+ // Mark it as fixed
+ this[ jQuery.expando ] = true;
+};
+
+function returnFalse() {
+ return false;
+}
+function returnTrue() {
+ return true;
+}
+
+// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding
+// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
+jQuery.Event.prototype = {
+ preventDefault: function() {
+ this.isDefaultPrevented = returnTrue;
+
+ var e = this.originalEvent;
+ if ( !e ) {
+ return;
+ }
+
+ // if preventDefault exists run it on the original event
+ if ( e.preventDefault ) {
+ e.preventDefault();
+
+ // otherwise set the returnValue property of the original event to false (IE)
+ } else {
+ e.returnValue = false;
+ }
+ },
+ stopPropagation: function() {
+ this.isPropagationStopped = returnTrue;
+
+ var e = this.originalEvent;
+ if ( !e ) {
+ return;
+ }
+ // if stopPropagation exists run it on the original event
+ if ( e.stopPropagation ) {
+ e.stopPropagation();
+ }
+ // otherwise set the cancelBubble property of the original event to true (IE)
+ e.cancelBubble = true;
+ },
+ stopImmediatePropagation: function() {
+ this.isImmediatePropagationStopped = returnTrue;
+ this.stopPropagation();
+ },
+ isDefaultPrevented: returnFalse,
+ isPropagationStopped: returnFalse,
+ isImmediatePropagationStopped: returnFalse
+};
+
+// Checks if an event happened on an element within another element
+// Used in jQuery.event.special.mouseenter and mouseleave handlers
+var withinElement = function( event ) {
+ // Check if mouse(over|out) are still within the same parent element
+ var parent = event.relatedTarget;
+
+ // Firefox sometimes assigns relatedTarget a XUL element
+ // which we cannot access the parentNode property of
+ try {
+ // Traverse up the tree
+ while ( parent && parent !== this ) {
+ parent = parent.parentNode;
+ }
+
+ if ( parent !== this ) {
+ // set the correct event type
+ event.type = event.data;
+
+ // handle event if we actually just moused on to a non sub-element
+ jQuery.event.handle.apply( this, arguments );
+ }
+
+ // assuming we've left the element since we most likely mousedover a xul element
+ } catch(e) { }
+},
+
+// In case of event delegation, we only need to rename the event.type,
+// liveHandler will take care of the rest.
+delegate = function( event ) {
+ event.type = event.data;
+ jQuery.event.handle.apply( this, arguments );
+};
+
+// Create mouseenter and mouseleave events
+jQuery.each({
+ mouseenter: "mouseover",
+ mouseleave: "mouseout"
+}, function( orig, fix ) {
+ jQuery.event.special[ orig ] = {
+ setup: function( data ) {
+ jQuery.event.add( this, fix, data && data.selector ? delegate : withinElement, orig );
+ },
+ teardown: function( data ) {
+ jQuery.event.remove( this, fix, data && data.selector ? delegate : withinElement );
+ }
+ };
+});
+
+// submit delegation
+if ( !jQuery.support.submitBubbles ) {
+
+ jQuery.event.special.submit = {
+ setup: function( data, namespaces ) {
+ if ( this.nodeName.toLowerCase() !== "form" ) {
+ jQuery.event.add(this, "click.specialSubmit", function( e ) {
+ var elem = e.target,
+ type = elem.type;
+
+ if ( (type === "submit" || type === "image") && jQuery( elem ).closest("form").length ) {
+ e.liveFired = undefined;
+ return trigger( "submit", this, arguments );
+ }
+ });
+
+ jQuery.event.add(this, "keypress.specialSubmit", function( e ) {
+ var elem = e.target,
+ type = elem.type;
+
+ if ( (type === "text" || type === "password") && jQuery( elem ).closest("form").length && e.keyCode === 13 ) {
+ e.liveFired = undefined;
+ return trigger( "submit", this, arguments );
+ }
+ });
+
+ } else {
+ return false;
+ }
+ },
+
+ teardown: function( namespaces ) {
+ jQuery.event.remove( this, ".specialSubmit" );
+ }
+ };
+
+}
+
+// change delegation, happens here so we have bind.
+if ( !jQuery.support.changeBubbles ) {
+
+ var changeFilters,
+
+ getVal = function( elem ) {
+ var type = elem.type, val = elem.value;
+
+ if ( type === "radio" || type === "checkbox" ) {
+ val = elem.checked;
+
+ } else if ( type === "select-multiple" ) {
+ val = elem.selectedIndex > -1 ?
+ jQuery.map( elem.options, function( elem ) {
+ return elem.selected;
+ }).join("-") :
+ "";
+
+ } else if ( elem.nodeName.toLowerCase() === "select" ) {
+ val = elem.selectedIndex;
+ }
+
+ return val;
+ },
+
+ testChange = function testChange( e ) {
+ var elem = e.target, data, val;
+
+ if ( !rformElems.test( elem.nodeName ) || elem.readOnly ) {
+ return;
+ }
+
+ data = jQuery.data( elem, "_change_data" );
+ val = getVal(elem);
+
+ // the current data will be also retrieved by beforeactivate
+ if ( e.type !== "focusout" || elem.type !== "radio" ) {
+ jQuery.data( elem, "_change_data", val );
+ }
+
+ if ( data === undefined || val === data ) {
+ return;
+ }
+
+ if ( data != null || val ) {
+ e.type = "change";
+ e.liveFired = undefined;
+ return jQuery.event.trigger( e, arguments[1], elem );
+ }
+ };
+
+ jQuery.event.special.change = {
+ filters: {
+ focusout: testChange,
+
+ beforedeactivate: testChange,
+
+ click: function( e ) {
+ var elem = e.target, type = elem.type;
+
+ if ( type === "radio" || type === "checkbox" || elem.nodeName.toLowerCase() === "select" ) {
+ return testChange.call( this, e );
+ }
+ },
+
+ // Change has to be called before submit
+ // Keydown will be called before keypress, which is used in submit-event delegation
+ keydown: function( e ) {
+ var elem = e.target, type = elem.type;
+
+ if ( (e.keyCode === 13 && elem.nodeName.toLowerCase() !== "textarea") ||
+ (e.keyCode === 32 && (type === "checkbox" || type === "radio")) ||
+ type === "select-multiple" ) {
+ return testChange.call( this, e );
+ }
+ },
+
+ // Beforeactivate happens also before the previous element is blurred
+ // with this event you can't trigger a change event, but you can store
+ // information
+ beforeactivate: function( e ) {
+ var elem = e.target;
+ jQuery.data( elem, "_change_data", getVal(elem) );
+ }
+ },
+
+ setup: function( data, namespaces ) {
+ if ( this.type === "file" ) {
+ return false;
+ }
+
+ for ( var type in changeFilters ) {
+ jQuery.event.add( this, type + ".specialChange", changeFilters[type] );
+ }
+
+ return rformElems.test( this.nodeName );
+ },
+
+ teardown: function( namespaces ) {
+ jQuery.event.remove( this, ".specialChange" );
+
+ return rformElems.test( this.nodeName );
+ }
+ };
+
+ changeFilters = jQuery.event.special.change.filters;
+
+ // Handle when the input is .focus()'d
+ changeFilters.focus = changeFilters.beforeactivate;
+}
+
+function trigger( type, elem, args ) {
+ args[0].type = type;
+ return jQuery.event.handle.apply( elem, args );
+}
+
+// Create "bubbling" focus and blur events
+if ( document.addEventListener ) {
+ jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) {
+ jQuery.event.special[ fix ] = {
+ setup: function() {
+ if ( focusCounts[fix]++ === 0 ) {
+ document.addEventListener( orig, handler, true );
+ }
+ },
+ teardown: function() {
+ if ( --focusCounts[fix] === 0 ) {
+ document.removeEventListener( orig, handler, true );
+ }
+ }
+ };
+
+ function handler( e ) {
+ e = jQuery.event.fix( e );
+ e.type = fix;
+ return jQuery.event.trigger( e, null, e.target );
+ }
+ });
+}
+
+jQuery.each(["bind", "one"], function( i, name ) {
+ jQuery.fn[ name ] = function( type, data, fn ) {
+ // Handle object literals
+ if ( typeof type === "object" ) {
+ for ( var key in type ) {
+ this[ name ](key, data, type[key], fn);
+ }
+ return this;
+ }
+
+ if ( jQuery.isFunction( data ) || data === false ) {
+ fn = data;
+ data = undefined;
+ }
+
+ var handler = name === "one" ? jQuery.proxy( fn, function( event ) {
+ jQuery( this ).unbind( event, handler );
+ return fn.apply( this, arguments );
+ }) : fn;
+
+ if ( type === "unload" && name !== "one" ) {
+ this.one( type, data, fn );
+
+ } else {
+ for ( var i = 0, l = this.length; i < l; i++ ) {
+ jQuery.event.add( this[i], type, handler, data );
+ }
+ }
+
+ return this;
+ };
+});
+
+jQuery.fn.extend({
+ unbind: function( type, fn ) {
+ // Handle object literals
+ if ( typeof type === "object" && !type.preventDefault ) {
+ for ( var key in type ) {
+ this.unbind(key, type[key]);
+ }
+
+ } else {
+ for ( var i = 0, l = this.length; i < l; i++ ) {
+ jQuery.event.remove( this[i], type, fn );
+ }
+ }
+
+ return this;
+ },
+
+ delegate: function( selector, types, data, fn ) {
+ return this.live( types, data, fn, selector );
+ },
+
+ undelegate: function( selector, types, fn ) {
+ if ( arguments.length === 0 ) {
+ return this.unbind( "live" );
+
+ } else {
+ return this.die( types, null, fn, selector );
+ }
+ },
+
+ trigger: function( type, data ) {
+ return this.each(function() {
+ jQuery.event.trigger( type, data, this );
+ });
+ },
+
+ triggerHandler: function( type, data ) {
+ if ( this[0] ) {
+ var event = jQuery.Event( type );
+ event.preventDefault();
+ event.stopPropagation();
+ jQuery.event.trigger( event, data, this[0] );
+ return event.result;
+ }
+ },
+
+ toggle: function( fn ) {
+ // Save reference to arguments for access in closure
+ var args = arguments,
+ i = 1;
+
+ // link all the functions, so any of them can unbind this click handler
+ while ( i < args.length ) {
+ jQuery.proxy( fn, args[ i++ ] );
+ }
+
+ return this.click( jQuery.proxy( fn, function( event ) {
+ // Figure out which function to execute
+ var lastToggle = ( jQuery.data( this, "lastToggle" + fn.guid ) || 0 ) % i;
+ jQuery.data( this, "lastToggle" + fn.guid, lastToggle + 1 );
+
+ // Make sure that clicks stop
+ event.preventDefault();
+
+ // and execute the function
+ return args[ lastToggle ].apply( this, arguments ) || false;
+ }));
+ },
+
+ hover: function( fnOver, fnOut ) {
+ return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
+ }
+});
+
+var liveMap = {
+ focus: "focusin",
+ blur: "focusout",
+ mouseenter: "mouseover",
+ mouseleave: "mouseout"
+};
+
+jQuery.each(["live", "die"], function( i, name ) {
+ jQuery.fn[ name ] = function( types, data, fn, origSelector /* Internal Use Only */ ) {
+ var type, i = 0, match, namespaces, preType,
+ selector = origSelector || this.selector,
+ context = origSelector ? this : jQuery( this.context );
+
+ if ( typeof types === "object" && !types.preventDefault ) {
+ for ( var key in types ) {
+ context[ name ]( key, data, types[key], selector );
+ }
+
+ return this;
+ }
+
+ if ( jQuery.isFunction( data ) ) {
+ fn = data;
+ data = undefined;
+ }
+
+ types = (types || "").split(" ");
+
+ while ( (type = types[ i++ ]) != null ) {
+ match = rnamespaces.exec( type );
+ namespaces = "";
+
+ if ( match ) {
+ namespaces = match[0];
+ type = type.replace( rnamespaces, "" );
+ }
+
+ if ( type === "hover" ) {
+ types.push( "mouseenter" + namespaces, "mouseleave" + namespaces );
+ continue;
+ }
+
+ preType = type;
+
+ if ( type === "focus" || type === "blur" ) {
+ types.push( liveMap[ type ] + namespaces );
+ type = type + namespaces;
+
+ } else {
+ type = (liveMap[ type ] || type) + namespaces;
+ }
+
+ if ( name === "live" ) {
+ // bind live handler
+ for ( var j = 0, l = context.length; j < l; j++ ) {
+ jQuery.event.add( context[j], "live." + liveConvert( type, selector ),
+ { data: data, selector: selector, handler: fn, origType: type, origHandler: fn, preType: preType } );
+ }
+
+ } else {
+ // unbind live handler
+ context.unbind( "live." + liveConvert( type, selector ), fn );
+ }
+ }
+
+ return this;
+ };
+});
+
+function liveHandler( event ) {
+ var stop, maxLevel, related, match, handleObj, elem, j, i, l, data, close, namespace, ret,
+ elems = [],
+ selectors = [],
+ events = jQuery.data( this, this.nodeType ? "events" : "__events__" );
+
+ if ( typeof events === "function" ) {
+ events = events.events;
+ }
+
+ // Make sure we avoid non-left-click bubbling in Firefox (#3861)
+ if ( event.liveFired === this || !events || !events.live || event.button && event.type === "click" ) {
+ return;
+ }
+
+ if ( event.namespace ) {
+ namespace = new RegExp("(^|\\.)" + event.namespace.split(".").join("\\.(?:.*\\.)?") + "(\\.|$)");
+ }
+
+ event.liveFired = this;
+
+ var live = events.live.slice(0);
+
+ for ( j = 0; j < live.length; j++ ) {
+ handleObj = live[j];
+
+ if ( handleObj.origType.replace( rnamespaces, "" ) === event.type ) {
+ selectors.push( handleObj.selector );
+
+ } else {
+ live.splice( j--, 1 );
+ }
+ }
+
+ match = jQuery( event.target ).closest( selectors, event.currentTarget );
+
+ for ( i = 0, l = match.length; i < l; i++ ) {
+ close = match[i];
+
+ for ( j = 0; j < live.length; j++ ) {
+ handleObj = live[j];
+
+ if ( close.selector === handleObj.selector && (!namespace || namespace.test( handleObj.namespace )) ) {
+ elem = close.elem;
+ related = null;
+
+ // Those two events require additional checking
+ if ( handleObj.preType === "mouseenter" || handleObj.preType === "mouseleave" ) {
+ event.type = handleObj.preType;
+ related = jQuery( event.relatedTarget ).closest( handleObj.selector )[0];
+ }
+
+ if ( !related || related !== elem ) {
+ elems.push({ elem: elem, handleObj: handleObj, level: close.level });
+ }
+ }
+ }
+ }
+
+ for ( i = 0, l = elems.length; i < l; i++ ) {
+ match = elems[i];
+
+ if ( maxLevel && match.level > maxLevel ) {
+ break;
+ }
+
+ event.currentTarget = match.elem;
+ event.data = match.handleObj.data;
+ event.handleObj = match.handleObj;
+
+ ret = match.handleObj.origHandler.apply( match.elem, arguments );
+
+ if ( ret === false || event.isPropagationStopped() ) {
+ maxLevel = match.level;
+
+ if ( ret === false ) {
+ stop = false;
+ }
+ if ( event.isImmediatePropagationStopped() ) {
+ break;
+ }
+ }
+ }
+
+ return stop;
+}
+
+function liveConvert( type, selector ) {
+ return (type && type !== "*" ? type + "." : "") + selector.replace(rperiod, "`").replace(rspace, "&");
+}
+
+jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " +
+ "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
+ "change select submit keydown keypress keyup error").split(" "), function( i, name ) {
+
+ // Handle event binding
+ jQuery.fn[ name ] = function( data, fn ) {
+ if ( fn == null ) {
+ fn = data;
+ data = null;
+ }
+
+ return arguments.length > 0 ?
+ this.bind( name, data, fn ) :
+ this.trigger( name );
+ };
+
+ if ( jQuery.attrFn ) {
+ jQuery.attrFn[ name ] = true;
+ }
+});
+
+// Prevent memory leaks in IE
+// Window isn't included so as not to unbind existing unload events
+// More info:
+// - http://isaacschlueter.com/2006/10/msie-memory-leaks/
+if ( window.attachEvent && !window.addEventListener ) {
+ jQuery(window).bind("unload", function() {
+ for ( var id in jQuery.cache ) {
+ if ( jQuery.cache[ id ].handle ) {
+ // Try/Catch is to handle iframes being unloaded, see #4280
+ try {
+ jQuery.event.remove( jQuery.cache[ id ].handle.elem );
+ } catch(e) {}
+ }
+ }
+ });
+}
+
+
+/*!
+ * Sizzle CSS Selector Engine - v1.0
+ * Copyright 2009, The Dojo Foundation
+ * Released under the MIT, BSD, and GPL Licenses.
+ * More information: http://sizzlejs.com/
+ */
+(function(){
+
+var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,
+ done = 0,
+ toString = Object.prototype.toString,
+ hasDuplicate = false,
+ baseHasDuplicate = true;
+
+// Here we check if the JavaScript engine is using some sort of
+// optimization where it does not always call our comparision
+// function. If that is the case, discard the hasDuplicate value.
+// Thus far that includes Google Chrome.
+[0, 0].sort(function() {
+ baseHasDuplicate = false;
+ return 0;
+});
+
+var Sizzle = function( selector, context, results, seed ) {
+ results = results || [];
+ context = context || document;
+
+ var origContext = context;
+
+ if ( context.nodeType !== 1 && context.nodeType !== 9 ) {
+ return [];
+ }
+
+ if ( !selector || typeof selector !== "string" ) {
+ return results;
+ }
+
+ var m, set, checkSet, extra, ret, cur, pop, i,
+ prune = true,
+ contextXML = Sizzle.isXML( context ),
+ parts = [],
+ soFar = selector;
+
+ // Reset the position of the chunker regexp (start from head)
+ do {
+ chunker.exec( "" );
+ m = chunker.exec( soFar );
+
+ if ( m ) {
+ soFar = m[3];
+
+ parts.push( m[1] );
+
+ if ( m[2] ) {
+ extra = m[3];
+ break;
+ }
+ }
+ } while ( m );
+
+ if ( parts.length > 1 && origPOS.exec( selector ) ) {
+
+ if ( parts.length === 2 && Expr.relative[ parts[0] ] ) {
+ set = posProcess( parts[0] + parts[1], context );
+
+ } else {
+ set = Expr.relative[ parts[0] ] ?
+ [ context ] :
+ Sizzle( parts.shift(), context );
+
+ while ( parts.length ) {
+ selector = parts.shift();
+
+ if ( Expr.relative[ selector ] ) {
+ selector += parts.shift();
+ }
+
+ set = posProcess( selector, set );
+ }
+ }
+
+ } else {
+ // Take a shortcut and set the context if the root selector is an ID
+ // (but not if it'll be faster if the inner selector is an ID)
+ if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML &&
+ Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) {
+
+ ret = Sizzle.find( parts.shift(), context, contextXML );
+ context = ret.expr ?
+ Sizzle.filter( ret.expr, ret.set )[0] :
+ ret.set[0];
+ }
+
+ if ( context ) {
+ ret = seed ?
+ { expr: parts.pop(), set: makeArray(seed) } :
+ Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML );
+
+ set = ret.expr ?
+ Sizzle.filter( ret.expr, ret.set ) :
+ ret.set;
+
+ if ( parts.length > 0 ) {
+ checkSet = makeArray( set );
+
+ } else {
+ prune = false;
+ }
+
+ while ( parts.length ) {
+ cur = parts.pop();
+ pop = cur;
+
+ if ( !Expr.relative[ cur ] ) {
+ cur = "";
+ } else {
+ pop = parts.pop();
+ }
+
+ if ( pop == null ) {
+ pop = context;
+ }
+
+ Expr.relative[ cur ]( checkSet, pop, contextXML );
+ }
+
+ } else {
+ checkSet = parts = [];
+ }
+ }
+
+ if ( !checkSet ) {
+ checkSet = set;
+ }
+
+ if ( !checkSet ) {
+ Sizzle.error( cur || selector );
+ }
+
+ if ( toString.call(checkSet) === "[object Array]" ) {
+ if ( !prune ) {
+ results.push.apply( results, checkSet );
+
+ } else if ( context && context.nodeType === 1 ) {
+ for ( i = 0; checkSet[i] != null; i++ ) {
+ if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && Sizzle.contains(context, checkSet[i])) ) {
+ results.push( set[i] );
+ }
+ }
+
+ } else {
+ for ( i = 0; checkSet[i] != null; i++ ) {
+ if ( checkSet[i] && checkSet[i].nodeType === 1 ) {
+ results.push( set[i] );
+ }
+ }
+ }
+
+ } else {
+ makeArray( checkSet, results );
+ }
+
+ if ( extra ) {
+ Sizzle( extra, origContext, results, seed );
+ Sizzle.uniqueSort( results );
+ }
+
+ return results;
+};
+
+Sizzle.uniqueSort = function( results ) {
+ if ( sortOrder ) {
+ hasDuplicate = baseHasDuplicate;
+ results.sort( sortOrder );
+
+ if ( hasDuplicate ) {
+ for ( var i = 1; i < results.length; i++ ) {
+ if ( results[i] === results[ i - 1 ] ) {
+ results.splice( i--, 1 );
+ }
+ }
+ }
+ }
+
+ return results;
+};
+
+Sizzle.matches = function( expr, set ) {
+ return Sizzle( expr, null, null, set );
+};
+
+Sizzle.matchesSelector = function( node, expr ) {
+ return Sizzle( expr, null, null, [node] ).length > 0;
+};
+
+Sizzle.find = function( expr, context, isXML ) {
+ var set;
+
+ if ( !expr ) {
+ return [];
+ }
+
+ for ( var i = 0, l = Expr.order.length; i < l; i++ ) {
+ var match,
+ type = Expr.order[i];
+
+ if ( (match = Expr.leftMatch[ type ].exec( expr )) ) {
+ var left = match[1];
+ match.splice( 1, 1 );
+
+ if ( left.substr( left.length - 1 ) !== "\\" ) {
+ match[1] = (match[1] || "").replace(/\\/g, "");
+ set = Expr.find[ type ]( match, context, isXML );
+
+ if ( set != null ) {
+ expr = expr.replace( Expr.match[ type ], "" );
+ break;
+ }
+ }
+ }
+ }
+
+ if ( !set ) {
+ set = context.getElementsByTagName( "*" );
+ }
+
+ return { set: set, expr: expr };
+};
+
+Sizzle.filter = function( expr, set, inplace, not ) {
+ var match, anyFound,
+ old = expr,
+ result = [],
+ curLoop = set,
+ isXMLFilter = set && set[0] && Sizzle.isXML( set[0] );
+
+ while ( expr && set.length ) {
+ for ( var type in Expr.filter ) {
+ if ( (match = Expr.leftMatch[ type ].exec( expr )) != null && match[2] ) {
+ var found, item,
+ filter = Expr.filter[ type ],
+ left = match[1];
+
+ anyFound = false;
+
+ match.splice(1,1);
+
+ if ( left.substr( left.length - 1 ) === "\\" ) {
+ continue;
+ }
+
+ if ( curLoop === result ) {
+ result = [];
+ }
+
+ if ( Expr.preFilter[ type ] ) {
+ match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter );
+
+ if ( !match ) {
+ anyFound = found = true;
+
+ } else if ( match === true ) {
+ continue;
+ }
+ }
+
+ if ( match ) {
+ for ( var i = 0; (item = curLoop[i]) != null; i++ ) {
+ if ( item ) {
+ found = filter( item, match, i, curLoop );
+ var pass = not ^ !!found;
+
+ if ( inplace && found != null ) {
+ if ( pass ) {
+ anyFound = true;
+
+ } else {
+ curLoop[i] = false;
+ }
+
+ } else if ( pass ) {
+ result.push( item );
+ anyFound = true;
+ }
+ }
+ }
+ }
+
+ if ( found !== undefined ) {
+ if ( !inplace ) {
+ curLoop = result;
+ }
+
+ expr = expr.replace( Expr.match[ type ], "" );
+
+ if ( !anyFound ) {
+ return [];
+ }
+
+ break;
+ }
+ }
+ }
+
+ // Improper expression
+ if ( expr === old ) {
+ if ( anyFound == null ) {
+ Sizzle.error( expr );
+
+ } else {
+ break;
+ }
+ }
+
+ old = expr;
+ }
+
+ return curLoop;
+};
+
+Sizzle.error = function( msg ) {
+ throw "Syntax error, unrecognized expression: " + msg;
+};
+
+var Expr = Sizzle.selectors = {
+ order: [ "ID", "NAME", "TAG" ],
+
+ match: {
+ ID: /#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,
+ CLASS: /\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,
+ NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/,
+ ATTR: /\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,
+ TAG: /^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/,
+ CHILD: /:(only|nth|last|first)-child(?:\((even|odd|[\dn+\-]*)\))?/,
+ POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/,
+ PSEUDO: /:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/
+ },
+
+ leftMatch: {},
+
+ attrMap: {
+ "class": "className",
+ "for": "htmlFor"
+ },
+
+ attrHandle: {
+ href: function( elem ) {
+ return elem.getAttribute( "href" );
+ }
+ },
+
+ relative: {
+ "+": function(checkSet, part){
+ var isPartStr = typeof part === "string",
+ isTag = isPartStr && !/\W/.test( part ),
+ isPartStrNotTag = isPartStr && !isTag;
+
+ if ( isTag ) {
+ part = part.toLowerCase();
+ }
+
+ for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) {
+ if ( (elem = checkSet[i]) ) {
+ while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {}
+
+ checkSet[i] = isPartStrNotTag || elem && elem.nodeName.toLowerCase() === part ?
+ elem || false :
+ elem === part;
+ }
+ }
+
+ if ( isPartStrNotTag ) {
+ Sizzle.filter( part, checkSet, true );
+ }
+ },
+
+ ">": function( checkSet, part ) {
+ var elem,
+ isPartStr = typeof part === "string",
+ i = 0,
+ l = checkSet.length;
+
+ if ( isPartStr && !/\W/.test( part ) ) {
+ part = part.toLowerCase();
+
+ for ( ; i < l; i++ ) {
+ elem = checkSet[i];
+
+ if ( elem ) {
+ var parent = elem.parentNode;
+ checkSet[i] = parent.nodeName.toLowerCase() === part ? parent : false;
+ }
+ }
+
+ } else {
+ for ( ; i < l; i++ ) {
+ elem = checkSet[i];
+
+ if ( elem ) {
+ checkSet[i] = isPartStr ?
+ elem.parentNode :
+ elem.parentNode === part;
+ }
+ }
+
+ if ( isPartStr ) {
+ Sizzle.filter( part, checkSet, true );
+ }
+ }
+ },
+
+ "": function(checkSet, part, isXML){
+ var nodeCheck,
+ doneName = done++,
+ checkFn = dirCheck;
+
+ if ( typeof part === "string" && !/\W/.test(part) ) {
+ part = part.toLowerCase();
+ nodeCheck = part;
+ checkFn = dirNodeCheck;
+ }
+
+ checkFn( "parentNode", part, doneName, checkSet, nodeCheck, isXML );
+ },
+
+ "~": function( checkSet, part, isXML ) {
+ var nodeCheck,
+ doneName = done++,
+ checkFn = dirCheck;
+
+ if ( typeof part === "string" && !/\W/.test( part ) ) {
+ part = part.toLowerCase();
+ nodeCheck = part;
+ checkFn = dirNodeCheck;
+ }
+
+ checkFn( "previousSibling", part, doneName, checkSet, nodeCheck, isXML );
+ }
+ },
+
+ find: {
+ ID: function( match, context, isXML ) {
+ if ( typeof context.getElementById !== "undefined" && !isXML ) {
+ var m = context.getElementById(match[1]);
+ // Check parentNode to catch when Blackberry 4.6 returns
+ // nodes that are no longer in the document #6963
+ return m && m.parentNode ? [m] : [];
+ }
+ },
+
+ NAME: function( match, context ) {
+ if ( typeof context.getElementsByName !== "undefined" ) {
+ var ret = [],
+ results = context.getElementsByName( match[1] );
+
+ for ( var i = 0, l = results.length; i < l; i++ ) {
+ if ( results[i].getAttribute("name") === match[1] ) {
+ ret.push( results[i] );
+ }
+ }
+
+ return ret.length === 0 ? null : ret;
+ }
+ },
+
+ TAG: function( match, context ) {
+ return context.getElementsByTagName( match[1] );
+ }
+ },
+ preFilter: {
+ CLASS: function( match, curLoop, inplace, result, not, isXML ) {
+ match = " " + match[1].replace(/\\/g, "") + " ";
+
+ if ( isXML ) {
+ return match;
+ }
+
+ for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) {
+ if ( elem ) {
+ if ( not ^ (elem.className && (" " + elem.className + " ").replace(/[\t\n]/g, " ").indexOf(match) >= 0) ) {
+ if ( !inplace ) {
+ result.push( elem );
+ }
+
+ } else if ( inplace ) {
+ curLoop[i] = false;
+ }
+ }
+ }
+
+ return false;
+ },
+
+ ID: function( match ) {
+ return match[1].replace(/\\/g, "");
+ },
+
+ TAG: function( match, curLoop ) {
+ return match[1].toLowerCase();
+ },
+
+ CHILD: function( match ) {
+ if ( match[1] === "nth" ) {
+ // parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6'
+ var test = /(-?)(\d*)n((?:\+|-)?\d*)/.exec(
+ match[2] === "even" && "2n" || match[2] === "odd" && "2n+1" ||
+ !/\D/.test( match[2] ) && "0n+" + match[2] || match[2]);
+
+ // calculate the numbers (first)n+(last) including if they are negative
+ match[2] = (test[1] + (test[2] || 1)) - 0;
+ match[3] = test[3] - 0;
+ }
+
+ // TODO: Move to normal caching system
+ match[0] = done++;
+
+ return match;
+ },
+
+ ATTR: function( match, curLoop, inplace, result, not, isXML ) {
+ var name = match[1].replace(/\\/g, "");
+
+ if ( !isXML && Expr.attrMap[name] ) {
+ match[1] = Expr.attrMap[name];
+ }
+
+ if ( match[2] === "~=" ) {
+ match[4] = " " + match[4] + " ";
+ }
+
+ return match;
+ },
+
+ PSEUDO: function( match, curLoop, inplace, result, not ) {
+ if ( match[1] === "not" ) {
+ // If we're dealing with a complex expression, or a simple one
+ if ( ( chunker.exec(match[3]) || "" ).length > 1 || /^\w/.test(match[3]) ) {
+ match[3] = Sizzle(match[3], null, null, curLoop);
+
+ } else {
+ var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not);
+
+ if ( !inplace ) {
+ result.push.apply( result, ret );
+ }
+
+ return false;
+ }
+
+ } else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) {
+ return true;
+ }
+
+ return match;
+ },
+
+ POS: function( match ) {
+ match.unshift( true );
+
+ return match;
+ }
+ },
+
+ filters: {
+ enabled: function( elem ) {
+ return elem.disabled === false && elem.type !== "hidden";
+ },
+
+ disabled: function( elem ) {
+ return elem.disabled === true;
+ },
+
+ checked: function( elem ) {
+ return elem.checked === true;
+ },
+
+ selected: function( elem ) {
+ // Accessing this property makes selected-by-default
+ // options in Safari work properly
+ elem.parentNode.selectedIndex;
+
+ return elem.selected === true;
+ },
+
+ parent: function( elem ) {
+ return !!elem.firstChild;
+ },
+
+ empty: function( elem ) {
+ return !elem.firstChild;
+ },
+
+ has: function( elem, i, match ) {
+ return !!Sizzle( match[3], elem ).length;
+ },
+
+ header: function( elem ) {
+ return (/h\d/i).test( elem.nodeName );
+ },
+
+ text: function( elem ) {
+ return "text" === elem.type;
+ },
+ radio: function( elem ) {
+ return "radio" === elem.type;
+ },
+
+ checkbox: function( elem ) {
+ return "checkbox" === elem.type;
+ },
+
+ file: function( elem ) {
+ return "file" === elem.type;
+ },
+ password: function( elem ) {
+ return "password" === elem.type;
+ },
+
+ submit: function( elem ) {
+ return "submit" === elem.type;
+ },
+
+ image: function( elem ) {
+ return "image" === elem.type;
+ },
+
+ reset: function( elem ) {
+ return "reset" === elem.type;
+ },
+
+ button: function( elem ) {
+ return "button" === elem.type || elem.nodeName.toLowerCase() === "button";
+ },
+
+ input: function( elem ) {
+ return (/input|select|textarea|button/i).test( elem.nodeName );
+ }
+ },
+ setFilters: {
+ first: function( elem, i ) {
+ return i === 0;
+ },
+
+ last: function( elem, i, match, array ) {
+ return i === array.length - 1;
+ },
+
+ even: function( elem, i ) {
+ return i % 2 === 0;
+ },
+
+ odd: function( elem, i ) {
+ return i % 2 === 1;
+ },
+
+ lt: function( elem, i, match ) {
+ return i < match[3] - 0;
+ },
+
+ gt: function( elem, i, match ) {
+ return i > match[3] - 0;
+ },
+
+ nth: function( elem, i, match ) {
+ return match[3] - 0 === i;
+ },
+
+ eq: function( elem, i, match ) {
+ return match[3] - 0 === i;
+ }
+ },
+ filter: {
+ PSEUDO: function( elem, match, i, array ) {
+ var name = match[1],
+ filter = Expr.filters[ name ];
+
+ if ( filter ) {
+ return filter( elem, i, match, array );
+
+ } else if ( name === "contains" ) {
+ return (elem.textContent || elem.innerText || Sizzle.getText([ elem ]) || "").indexOf(match[3]) >= 0;
+
+ } else if ( name === "not" ) {
+ var not = match[3];
+
+ for ( var j = 0, l = not.length; j < l; j++ ) {
+ if ( not[j] === elem ) {
+ return false;
+ }
+ }
+
+ return true;
+
+ } else {
+ Sizzle.error( "Syntax error, unrecognized expression: " + name );
+ }
+ },
+
+ CHILD: function( elem, match ) {
+ var type = match[1],
+ node = elem;
+
+ switch ( type ) {
+ case "only":
+ case "first":
+ while ( (node = node.previousSibling) ) {
+ if ( node.nodeType === 1 ) {
+ return false;
+ }
+ }
+
+ if ( type === "first" ) {
+ return true;
+ }
+
+ node = elem;
+
+ case "last":
+ while ( (node = node.nextSibling) ) {
+ if ( node.nodeType === 1 ) {
+ return false;
+ }
+ }
+
+ return true;
+
+ case "nth":
+ var first = match[2],
+ last = match[3];
+
+ if ( first === 1 && last === 0 ) {
+ return true;
+ }
+
+ var doneName = match[0],
+ parent = elem.parentNode;
+
+ if ( parent && (parent.sizcache !== doneName || !elem.nodeIndex) ) {
+ var count = 0;
+
+ for ( node = parent.firstChild; node; node = node.nextSibling ) {
+ if ( node.nodeType === 1 ) {
+ node.nodeIndex = ++count;
+ }
+ }
+
+ parent.sizcache = doneName;
+ }
+
+ var diff = elem.nodeIndex - last;
+
+ if ( first === 0 ) {
+ return diff === 0;
+
+ } else {
+ return ( diff % first === 0 && diff / first >= 0 );
+ }
+ }
+ },
+
+ ID: function( elem, match ) {
+ return elem.nodeType === 1 && elem.getAttribute("id") === match;
+ },
+
+ TAG: function( elem, match ) {
+ return (match === "*" && elem.nodeType === 1) || elem.nodeName.toLowerCase() === match;
+ },
+
+ CLASS: function( elem, match ) {
+ return (" " + (elem.className || elem.getAttribute("class")) + " ")
+ .indexOf( match ) > -1;
+ },
+
+ ATTR: function( elem, match ) {
+ var name = match[1],
+ result = Expr.attrHandle[ name ] ?
+ Expr.attrHandle[ name ]( elem ) :
+ elem[ name ] != null ?
+ elem[ name ] :
+ elem.getAttribute( name ),
+ value = result + "",
+ type = match[2],
+ check = match[4];
+
+ return result == null ?
+ type === "!=" :
+ type === "=" ?
+ value === check :
+ type === "*=" ?
+ value.indexOf(check) >= 0 :
+ type === "~=" ?
+ (" " + value + " ").indexOf(check) >= 0 :
+ !check ?
+ value && result !== false :
+ type === "!=" ?
+ value !== check :
+ type === "^=" ?
+ value.indexOf(check) === 0 :
+ type === "$=" ?
+ value.substr(value.length - check.length) === check :
+ type === "|=" ?
+ value === check || value.substr(0, check.length + 1) === check + "-" :
+ false;
+ },
+
+ POS: function( elem, match, i, array ) {
+ var name = match[2],
+ filter = Expr.setFilters[ name ];
+
+ if ( filter ) {
+ return filter( elem, i, match, array );
+ }
+ }
+ }
+};
+
+var origPOS = Expr.match.POS,
+ fescape = function(all, num){
+ return "\\" + (num - 0 + 1);
+ };
+
+for ( var type in Expr.match ) {
+ Expr.match[ type ] = new RegExp( Expr.match[ type ].source + (/(?![^\[]*\])(?![^\(]*\))/.source) );
+ Expr.leftMatch[ type ] = new RegExp( /(^(?:.|\r|\n)*?)/.source + Expr.match[ type ].source.replace(/\\(\d+)/g, fescape) );
+}
+
+var makeArray = function( array, results ) {
+ array = Array.prototype.slice.call( array, 0 );
+
+ if ( results ) {
+ results.push.apply( results, array );
+ return results;
+ }
+
+ return array;
+};
+
+// Perform a simple check to determine if the browser is capable of
+// converting a NodeList to an array using builtin methods.
+// Also verifies that the returned array holds DOM nodes
+// (which is not the case in the Blackberry browser)
+try {
+ Array.prototype.slice.call( document.documentElement.childNodes, 0 )[0].nodeType;
+
+// Provide a fallback method if it does not work
+} catch( e ) {
+ makeArray = function( array, results ) {
+ var i = 0,
+ ret = results || [];
+
+ if ( toString.call(array) === "[object Array]" ) {
+ Array.prototype.push.apply( ret, array );
+
+ } else {
+ if ( typeof array.length === "number" ) {
+ for ( var l = array.length; i < l; i++ ) {
+ ret.push( array[i] );
+ }
+
+ } else {
+ for ( ; array[i]; i++ ) {
+ ret.push( array[i] );
+ }
+ }
+ }
+
+ return ret;
+ };
+}
+
+var sortOrder, siblingCheck;
+
+if ( document.documentElement.compareDocumentPosition ) {
+ sortOrder = function( a, b ) {
+ if ( a === b ) {
+ hasDuplicate = true;
+ return 0;
+ }
+
+ if ( !a.compareDocumentPosition || !b.compareDocumentPosition ) {
+ return a.compareDocumentPosition ? -1 : 1;
+ }
+
+ return a.compareDocumentPosition(b) & 4 ? -1 : 1;
+ };
+
+} else {
+ sortOrder = function( a, b ) {
+ var al, bl,
+ ap = [],
+ bp = [],
+ aup = a.parentNode,
+ bup = b.parentNode,
+ cur = aup;
+
+ // The nodes are identical, we can exit early
+ if ( a === b ) {
+ hasDuplicate = true;
+ return 0;
+
+ // If the nodes are siblings (or identical) we can do a quick check
+ } else if ( aup === bup ) {
+ return siblingCheck( a, b );
+
+ // If no parents were found then the nodes are disconnected
+ } else if ( !aup ) {
+ return -1;
+
+ } else if ( !bup ) {
+ return 1;
+ }
+
+ // Otherwise they're somewhere else in the tree so we need
+ // to build up a full list of the parentNodes for comparison
+ while ( cur ) {
+ ap.unshift( cur );
+ cur = cur.parentNode;
+ }
+
+ cur = bup;
+
+ while ( cur ) {
+ bp.unshift( cur );
+ cur = cur.parentNode;
+ }
+
+ al = ap.length;
+ bl = bp.length;
+
+ // Start walking down the tree looking for a discrepancy
+ for ( var i = 0; i < al && i < bl; i++ ) {
+ if ( ap[i] !== bp[i] ) {
+ return siblingCheck( ap[i], bp[i] );
+ }
+ }
+
+ // We ended someplace up the tree so do a sibling check
+ return i === al ?
+ siblingCheck( a, bp[i], -1 ) :
+ siblingCheck( ap[i], b, 1 );
+ };
+
+ siblingCheck = function( a, b, ret ) {
+ if ( a === b ) {
+ return ret;
+ }
+
+ var cur = a.nextSibling;
+
+ while ( cur ) {
+ if ( cur === b ) {
+ return -1;
+ }
+
+ cur = cur.nextSibling;
+ }
+
+ return 1;
+ };
+}
+
+// Utility function for retreiving the text value of an array of DOM nodes
+Sizzle.getText = function( elems ) {
+ var ret = "", elem;
+
+ for ( var i = 0; elems[i]; i++ ) {
+ elem = elems[i];
+
+ // Get the text from text nodes and CDATA nodes
+ if ( elem.nodeType === 3 || elem.nodeType === 4 ) {
+ ret += elem.nodeValue;
+
+ // Traverse everything else, except comment nodes
+ } else if ( elem.nodeType !== 8 ) {
+ ret += Sizzle.getText( elem.childNodes );
+ }
+ }
+
+ return ret;
+};
+
+// Check to see if the browser returns elements by name when
+// querying by getElementById (and provide a workaround)
+(function(){
+ // We're going to inject a fake input element with a specified name
+ var form = document.createElement("div"),
+ id = "script" + (new Date()).getTime(),
+ root = document.documentElement;
+
+ form.innerHTML = "<a name='" + id + "'/>";
+
+ // Inject it into the root element, check its status, and remove it quickly
+ root.insertBefore( form, root.firstChild );
+
+ // The workaround has to do additional checks after a getElementById
+ // Which slows things down for other browsers (hence the branching)
+ if ( document.getElementById( id ) ) {
+ Expr.find.ID = function( match, context, isXML ) {
+ if ( typeof context.getElementById !== "undefined" && !isXML ) {
+ var m = context.getElementById(match[1]);
+
+ return m ?
+ m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ?
+ [m] :
+ undefined :
+ [];
+ }
+ };
+
+ Expr.filter.ID = function( elem, match ) {
+ var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id");
+
+ return elem.nodeType === 1 && node && node.nodeValue === match;
+ };
+ }
+
+ root.removeChild( form );
+
+ // release memory in IE
+ root = form = null;
+})();
+
+(function(){
+ // Check to see if the browser returns only elements
+ // when doing getElementsByTagName("*")
+
+ // Create a fake element
+ var div = document.createElement("div");
+ div.appendChild( document.createComment("") );
+
+ // Make sure no comments are found
+ if ( div.getElementsByTagName("*").length > 0 ) {
+ Expr.find.TAG = function( match, context ) {
+ var results = context.getElementsByTagName( match[1] );
+
+ // Filter out possible comments
+ if ( match[1] === "*" ) {
+ var tmp = [];
+
+ for ( var i = 0; results[i]; i++ ) {
+ if ( results[i].nodeType === 1 ) {
+ tmp.push( results[i] );
+ }
+ }
+
+ results = tmp;
+ }
+
+ return results;
+ };
+ }
+
+ // Check to see if an attribute returns normalized href attributes
+ div.innerHTML = "<a href='#'></a>";
+
+ if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" &&
+ div.firstChild.getAttribute("href") !== "#" ) {
+
+ Expr.attrHandle.href = function( elem ) {
+ return elem.getAttribute( "href", 2 );
+ };
+ }
+
+ // release memory in IE
+ div = null;
+})();
+
+if ( document.querySelectorAll ) {
+ (function(){
+ var oldSizzle = Sizzle,
+ div = document.createElement("div"),
+ id = "__sizzle__";
+
+ div.innerHTML = "<p class='TEST'></p>";
+
+ // Safari can't handle uppercase or unicode characters when
+ // in quirks mode.
+ if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) {
+ return;
+ }
+
+ Sizzle = function( query, context, extra, seed ) {
+ context = context || document;
+
+ // Make sure that attribute selectors are quoted
+ query = query.replace(/\=\s*([^'"\]]*)\s*\]/g, "='$1']");
+
+ // Only use querySelectorAll on non-XML documents
+ // (ID selectors don't work in non-HTML documents)
+ if ( !seed && !Sizzle.isXML(context) ) {
+ if ( context.nodeType === 9 ) {
+ try {
+ return makeArray( context.querySelectorAll(query), extra );
+ } catch(qsaError) {}
+
+ // qSA works strangely on Element-rooted queries
+ // We can work around this by specifying an extra ID on the root
+ // and working up from there (Thanks to Andrew Dupont for the technique)
+ // IE 8 doesn't work on object elements
+ } else if ( context.nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) {
+ var old = context.getAttribute( "id" ),
+ nid = old || id;
+
+ if ( !old ) {
+ context.setAttribute( "id", nid );
+ }
+
+ try {
+ return makeArray( context.querySelectorAll( "#" + nid + " " + query ), extra );
+
+ } catch(pseudoError) {
+ } finally {
+ if ( !old ) {
+ context.removeAttribute( "id" );
+ }
+ }
+ }
+ }
+
+ return oldSizzle(query, context, extra, seed);
+ };
+
+ for ( var prop in oldSizzle ) {
+ Sizzle[ prop ] = oldSizzle[ prop ];
+ }
+
+ // release memory in IE
+ div = null;
+ })();
+}
+
+(function(){
+ var html = document.documentElement,
+ matches = html.matchesSelector || html.mozMatchesSelector || html.webkitMatchesSelector || html.msMatchesSelector,
+ pseudoWorks = false;
+
+ try {
+ // This should fail with an exception
+ // Gecko does not error, returns false instead
+ matches.call( document.documentElement, "[test!='']:sizzle" );
+
+ } catch( pseudoError ) {
+ pseudoWorks = true;
+ }
+
+ if ( matches ) {
+ Sizzle.matchesSelector = function( node, expr ) {
+ // Make sure that attribute selectors are quoted
+ expr = expr.replace(/\=\s*([^'"\]]*)\s*\]/g, "='$1']");
+
+ if ( !Sizzle.isXML( node ) ) {
+ try {
+ if ( pseudoWorks || !Expr.match.PSEUDO.test( expr ) && !/!=/.test( expr ) ) {
+ return matches.call( node, expr );
+ }
+ } catch(e) {}
+ }
+
+ return Sizzle(expr, null, null, [node]).length > 0;
+ };
+ }
+})();
+
+(function(){
+ var div = document.createElement("div");
+
+ div.innerHTML = "<div class='test e'></div><div class='test'></div>";
+
+ // Opera can't find a second classname (in 9.6)
+ // Also, make sure that getElementsByClassName actually exists
+ if ( !div.getElementsByClassName || div.getElementsByClassName("e").length === 0 ) {
+ return;
+ }
+
+ // Safari caches class attributes, doesn't catch changes (in 3.2)
+ div.lastChild.className = "e";
+
+ if ( div.getElementsByClassName("e").length === 1 ) {
+ return;
+ }
+
+ Expr.order.splice(1, 0, "CLASS");
+ Expr.find.CLASS = function( match, context, isXML ) {
+ if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) {
+ return context.getElementsByClassName(match[1]);
+ }
+ };
+
+ // release memory in IE
+ div = null;
+})();
+
+function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
+ for ( var i = 0, l = checkSet.length; i < l; i++ ) {
+ var elem = checkSet[i];
+
+ if ( elem ) {
+ var match = false;
+
+ elem = elem[dir];
+
+ while ( elem ) {
+ if ( elem.sizcache === doneName ) {
+ match = checkSet[elem.sizset];
+ break;
+ }
+
+ if ( elem.nodeType === 1 && !isXML ){
+ elem.sizcache = doneName;
+ elem.sizset = i;
+ }
+
+ if ( elem.nodeName.toLowerCase() === cur ) {
+ match = elem;
+ break;
+ }
+
+ elem = elem[dir];
+ }
+
+ checkSet[i] = match;
+ }
+ }
+}
+
+function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
+ for ( var i = 0, l = checkSet.length; i < l; i++ ) {
+ var elem = checkSet[i];
+
+ if ( elem ) {
+ var match = false;
+
+ elem = elem[dir];
+
+ while ( elem ) {
+ if ( elem.sizcache === doneName ) {
+ match = checkSet[elem.sizset];
+ break;
+ }
+
+ if ( elem.nodeType === 1 ) {
+ if ( !isXML ) {
+ elem.sizcache = doneName;
+ elem.sizset = i;
+ }
+
+ if ( typeof cur !== "string" ) {
+ if ( elem === cur ) {
+ match = true;
+ break;
+ }
+
+ } else if ( Sizzle.filter( cur, [elem] ).length > 0 ) {
+ match = elem;
+ break;
+ }
+ }
+
+ elem = elem[dir];
+ }
+
+ checkSet[i] = match;
+ }
+ }
+}
+
+if ( document.documentElement.contains ) {
+ Sizzle.contains = function( a, b ) {
+ return a !== b && (a.contains ? a.contains(b) : true);
+ };
+
+} else if ( document.documentElement.compareDocumentPosition ) {
+ Sizzle.contains = function( a, b ) {
+ return !!(a.compareDocumentPosition(b) & 16);
+ };
+
+} else {
+ Sizzle.contains = function() {
+ return false;
+ };
+}
+
+Sizzle.isXML = function( elem ) {
+ // documentElement is verified for cases where it doesn't yet exist
+ // (such as loading iframes in IE - #4833)
+ var documentElement = (elem ? elem.ownerDocument || elem : 0).documentElement;
+
+ return documentElement ? documentElement.nodeName !== "HTML" : false;
+};
+
+var posProcess = function( selector, context ) {
+ var match,
+ tmpSet = [],
+ later = "",
+ root = context.nodeType ? [context] : context;
+
+ // Position selectors must be done after the filter
+ // And so must :not(positional) so we move all PSEUDOs to the end
+ while ( (match = Expr.match.PSEUDO.exec( selector )) ) {
+ later += match[0];
+ selector = selector.replace( Expr.match.PSEUDO, "" );
+ }
+
+ selector = Expr.relative[selector] ? selector + "*" : selector;
+
+ for ( var i = 0, l = root.length; i < l; i++ ) {
+ Sizzle( selector, root[i], tmpSet );
+ }
+
+ return Sizzle.filter( later, tmpSet );
+};
+
+// EXPOSE
+jQuery.find = Sizzle;
+jQuery.expr = Sizzle.selectors;
+jQuery.expr[":"] = jQuery.expr.filters;
+jQuery.unique = Sizzle.uniqueSort;
+jQuery.text = Sizzle.getText;
+jQuery.isXMLDoc = Sizzle.isXML;
+jQuery.contains = Sizzle.contains;
+
+
+})();
+
+
+var runtil = /Until$/,
+ rparentsprev = /^(?:parents|prevUntil|prevAll)/,
+ // Note: This RegExp should be improved, or likely pulled from Sizzle
+ rmultiselector = /,/,
+ isSimple = /^.[^:#\[\.,]*$/,
+ slice = Array.prototype.slice,
+ POS = jQuery.expr.match.POS;
+
+jQuery.fn.extend({
+ find: function( selector ) {
+ var ret = this.pushStack( "", "find", selector ),
+ length = 0;
+
+ for ( var i = 0, l = this.length; i < l; i++ ) {
+ length = ret.length;
+ jQuery.find( selector, this[i], ret );
+
+ if ( i > 0 ) {
+ // Make sure that the results are unique
+ for ( var n = length; n < ret.length; n++ ) {
+ for ( var r = 0; r < length; r++ ) {
+ if ( ret[r] === ret[n] ) {
+ ret.splice(n--, 1);
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ return ret;
+ },
+
+ has: function( target ) {
+ var targets = jQuery( target );
+ return this.filter(function() {
+ for ( var i = 0, l = targets.length; i < l; i++ ) {
+ if ( jQuery.contains( this, targets[i] ) ) {
+ return true;
+ }
+ }
+ });
+ },
+
+ not: function( selector ) {
+ return this.pushStack( winnow(this, selector, false), "not", selector);
+ },
+
+ filter: function( selector ) {
+ return this.pushStack( winnow(this, selector, true), "filter", selector );
+ },
+
+ is: function( selector ) {
+ return !!selector && jQuery.filter( selector, this ).length > 0;
+ },
+
+ closest: function( selectors, context ) {
+ var ret = [], i, l, cur = this[0];
+
+ if ( jQuery.isArray( selectors ) ) {
+ var match, selector,
+ matches = {},
+ level = 1;
+
+ if ( cur && selectors.length ) {
+ for ( i = 0, l = selectors.length; i < l; i++ ) {
+ selector = selectors[i];
+
+ if ( !matches[selector] ) {
+ matches[selector] = jQuery.expr.match.POS.test( selector ) ?
+ jQuery( selector, context || this.context ) :
+ selector;
+ }
+ }
+
+ while ( cur && cur.ownerDocument && cur !== context ) {
+ for ( selector in matches ) {
+ match = matches[selector];
+
+ if ( match.jquery ? match.index(cur) > -1 : jQuery(cur).is(match) ) {
+ ret.push({ selector: selector, elem: cur, level: level });
+ }
+ }
+
+ cur = cur.parentNode;
+ level++;
+ }
+ }
+
+ return ret;
+ }
+
+ var pos = POS.test( selectors ) ?
+ jQuery( selectors, context || this.context ) : null;
+
+ for ( i = 0, l = this.length; i < l; i++ ) {
+ cur = this[i];
+
+ while ( cur ) {
+ if ( pos ? pos.index(cur) > -1 : jQuery.find.matchesSelector(cur, selectors) ) {
+ ret.push( cur );
+ break;
+
+ } else {
+ cur = cur.parentNode;
+ if ( !cur || !cur.ownerDocument || cur === context ) {
+ break;
+ }
+ }
+ }
+ }
+
+ ret = ret.length > 1 ? jQuery.unique(ret) : ret;
+
+ return this.pushStack( ret, "closest", selectors );
+ },
+
+ // Determine the position of an element within
+ // the matched set of elements
+ index: function( elem ) {
+ if ( !elem || typeof elem === "string" ) {
+ return jQuery.inArray( this[0],
+ // If it receives a string, the selector is used
+ // If it receives nothing, the siblings are used
+ elem ? jQuery( elem ) : this.parent().children() );
+ }
+ // Locate the position of the desired element
+ return jQuery.inArray(
+ // If it receives a jQuery object, the first element is used
+ elem.jquery ? elem[0] : elem, this );
+ },
+
+ add: function( selector, context ) {
+ var set = typeof selector === "string" ?
+ jQuery( selector, context || this.context ) :
+ jQuery.makeArray( selector ),
+ all = jQuery.merge( this.get(), set );
+
+ return this.pushStack( isDisconnected( set[0] ) || isDisconnected( all[0] ) ?
+ all :
+ jQuery.unique( all ) );
+ },
+
+ andSelf: function() {
+ return this.add( this.prevObject );
+ }
+});
+
+// A painfully simple check to see if an element is disconnected
+// from a document (should be improved, where feasible).
+function isDisconnected( node ) {
+ return !node || !node.parentNode || node.parentNode.nodeType === 11;
+}
+
+jQuery.each({
+ parent: function( elem ) {
+ var parent = elem.parentNode;
+ return parent && parent.nodeType !== 11 ? parent : null;
+ },
+ parents: function( elem ) {
+ return jQuery.dir( elem, "parentNode" );
+ },
+ parentsUntil: function( elem, i, until ) {
+ return jQuery.dir( elem, "parentNode", until );
+ },
+ next: function( elem ) {
+ return jQuery.nth( elem, 2, "nextSibling" );
+ },
+ prev: function( elem ) {
+ return jQuery.nth( elem, 2, "previousSibling" );
+ },
+ nextAll: function( elem ) {
+ return jQuery.dir( elem, "nextSibling" );
+ },
+ prevAll: function( elem ) {
+ return jQuery.dir( elem, "previousSibling" );
+ },
+ nextUntil: function( elem, i, until ) {
+ return jQuery.dir( elem, "nextSibling", until );
+ },
+ prevUntil: function( elem, i, until ) {
+ return jQuery.dir( elem, "previousSibling", until );
+ },
+ siblings: function( elem ) {
+ return jQuery.sibling( elem.parentNode.firstChild, elem );
+ },
+ children: function( elem ) {
+ return jQuery.sibling( elem.firstChild );
+ },
+ contents: function( elem ) {
+ return jQuery.nodeName( elem, "iframe" ) ?
+ elem.contentDocument || elem.contentWindow.document :
+ jQuery.makeArray( elem.childNodes );
+ }
+}, function( name, fn ) {
+ jQuery.fn[ name ] = function( until, selector ) {
+ var ret = jQuery.map( this, fn, until );
+
+ if ( !runtil.test( name ) ) {
+ selector = until;
+ }
+
+ if ( selector && typeof selector === "string" ) {
+ ret = jQuery.filter( selector, ret );
+ }
+
+ ret = this.length > 1 ? jQuery.unique( ret ) : ret;
+
+ if ( (this.length > 1 || rmultiselector.test( selector )) && rparentsprev.test( name ) ) {
+ ret = ret.reverse();
+ }
+
+ return this.pushStack( ret, name, slice.call(arguments).join(",") );
+ };
+});
+
+jQuery.extend({
+ filter: function( expr, elems, not ) {
+ if ( not ) {
+ expr = ":not(" + expr + ")";
+ }
+
+ return elems.length === 1 ?
+ jQuery.find.matchesSelector(elems[0], expr) ? [ elems[0] ] : [] :
+ jQuery.find.matches(expr, elems);
+ },
+
+ dir: function( elem, dir, until ) {
+ var matched = [],
+ cur = elem[ dir ];
+
+ while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) {
+ if ( cur.nodeType === 1 ) {
+ matched.push( cur );
+ }
+ cur = cur[dir];
+ }
+ return matched;
+ },
+
+ nth: function( cur, result, dir, elem ) {
+ result = result || 1;
+ var num = 0;
+
+ for ( ; cur; cur = cur[dir] ) {
+ if ( cur.nodeType === 1 && ++num === result ) {
+ break;
+ }
+ }
+
+ return cur;
+ },
+
+ sibling: function( n, elem ) {
+ var r = [];
+
+ for ( ; n; n = n.nextSibling ) {
+ if ( n.nodeType === 1 && n !== elem ) {
+ r.push( n );
+ }
+ }
+
+ return r;
+ }
+});
+
+// Implement the identical functionality for filter and not
+function winnow( elements, qualifier, keep ) {
+ if ( jQuery.isFunction( qualifier ) ) {
+ return jQuery.grep(elements, function( elem, i ) {
+ var retVal = !!qualifier.call( elem, i, elem );
+ return retVal === keep;
+ });
+
+ } else if ( qualifier.nodeType ) {
+ return jQuery.grep(elements, function( elem, i ) {
+ return (elem === qualifier) === keep;
+ });
+
+ } else if ( typeof qualifier === "string" ) {
+ var filtered = jQuery.grep(elements, function( elem ) {
+ return elem.nodeType === 1;
+ });
+
+ if ( isSimple.test( qualifier ) ) {
+ return jQuery.filter(qualifier, filtered, !keep);
+ } else {
+ qualifier = jQuery.filter( qualifier, filtered );
+ }
+ }
+
+ return jQuery.grep(elements, function( elem, i ) {
+ return (jQuery.inArray( elem, qualifier ) >= 0) === keep;
+ });
+}
+
+
+
+
+var rinlinejQuery = / jQuery\d+="(?:\d+|null)"/g,
+ rleadingWhitespace = /^\s+/,
+ rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,
+ rtagName = /<([\w:]+)/,
+ rtbody = /<tbody/i,
+ rhtml = /<|&#?\w+;/,
+ rnocache = /<(?:script|object|embed|option|style)/i,
+ // checked="checked" or checked (html5)
+ rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i,
+ raction = /\=([^="'>\s]+\/)>/g,
+ wrapMap = {
+ option: [ 1, "<select multiple='multiple'>", "</select>" ],
+ legend: [ 1, "<fieldset>", "</fieldset>" ],
+ thead: [ 1, "<table>", "</table>" ],
+ tr: [ 2, "<table><tbody>", "</tbody></table>" ],
+ td: [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ],
+ col: [ 2, "<table><tbody></tbody><colgroup>", "</colgroup></table>" ],
+ area: [ 1, "<map>", "</map>" ],
+ _default: [ 0, "", "" ]
+ };
+
+wrapMap.optgroup = wrapMap.option;
+wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
+wrapMap.th = wrapMap.td;
+
+// IE can't serialize <link> and <script> tags normally
+if ( !jQuery.support.htmlSerialize ) {
+ wrapMap._default = [ 1, "div<div>", "</div>" ];
+}
+
+jQuery.fn.extend({
+ text: function( text ) {
+ if ( jQuery.isFunction(text) ) {
+ return this.each(function(i) {
+ var self = jQuery( this );
+
+ self.text( text.call(this, i, self.text()) );
+ });
+ }
+
+ if ( typeof text !== "object" && text !== undefined ) {
+ return this.empty().append( (this[0] && this[0].ownerDocument || document).createTextNode( text ) );
+ }
+
+ return jQuery.text( this );
+ },
+
+ wrapAll: function( html ) {
+ if ( jQuery.isFunction( html ) ) {
+ return this.each(function(i) {
+ jQuery(this).wrapAll( html.call(this, i) );
+ });
+ }
+
+ if ( this[0] ) {
+ // The elements to wrap the target around
+ var wrap = jQuery( html, this[0].ownerDocument ).eq(0).clone(true);
+
+ if ( this[0].parentNode ) {
+ wrap.insertBefore( this[0] );
+ }
+
+ wrap.map(function() {
+ var elem = this;
+
+ while ( elem.firstChild && elem.firstChild.nodeType === 1 ) {
+ elem = elem.firstChild;
+ }
+
+ return elem;
+ }).append(this);
+ }
+
+ return this;
+ },
+
+ wrapInner: function( html ) {
+ if ( jQuery.isFunction( html ) ) {
+ return this.each(function(i) {
+ jQuery(this).wrapInner( html.call(this, i) );
+ });
+ }
+
+ return this.each(function() {
+ var self = jQuery( this ),
+ contents = self.contents();
+
+ if ( contents.length ) {
+ contents.wrapAll( html );
+
+ } else {
+ self.append( html );
+ }
+ });
+ },
+
+ wrap: function( html ) {
+ return this.each(function() {
+ jQuery( this ).wrapAll( html );
+ });
+ },
+
+ unwrap: function() {
+ return this.parent().each(function() {
+ if ( !jQuery.nodeName( this, "body" ) ) {
+ jQuery( this ).replaceWith( this.childNodes );
+ }
+ }).end();
+ },
+
+ append: function() {
+ return this.domManip(arguments, true, function( elem ) {
+ if ( this.nodeType === 1 ) {
+ this.appendChild( elem );
+ }
+ });
+ },
+
+ prepend: function() {
+ return this.domManip(arguments, true, function( elem ) {
+ if ( this.nodeType === 1 ) {
+ this.insertBefore( elem, this.firstChild );
+ }
+ });
+ },
+
+ before: function() {
+ if ( this[0] && this[0].parentNode ) {
+ return this.domManip(arguments, false, function( elem ) {
+ this.parentNode.insertBefore( elem, this );
+ });
+ } else if ( arguments.length ) {
+ var set = jQuery(arguments[0]);
+ set.push.apply( set, this.toArray() );
+ return this.pushStack( set, "before", arguments );
+ }
+ },
+
+ after: function() {
+ if ( this[0] && this[0].parentNode ) {
+ return this.domManip(arguments, false, function( elem ) {
+ this.parentNode.insertBefore( elem, this.nextSibling );
+ });
+ } else if ( arguments.length ) {
+ var set = this.pushStack( this, "after", arguments );
+ set.push.apply( set, jQuery(arguments[0]).toArray() );
+ return set;
+ }
+ },
+
+ // keepData is for internal use only--do not document
+ remove: function( selector, keepData ) {
+ for ( var i = 0, elem; (elem = this[i]) != null; i++ ) {
+ if ( !selector || jQuery.filter( selector, [ elem ] ).length ) {
+ if ( !keepData && elem.nodeType === 1 ) {
+ jQuery.cleanData( elem.getElementsByTagName("*") );
+ jQuery.cleanData( [ elem ] );
+ }
+
+ if ( elem.parentNode ) {
+ elem.parentNode.removeChild( elem );
+ }
+ }
+ }
+
+ return this;
+ },
+
+ empty: function() {
+ for ( var i = 0, elem; (elem = this[i]) != null; i++ ) {
+ // Remove element nodes and prevent memory leaks
+ if ( elem.nodeType === 1 ) {
+ jQuery.cleanData( elem.getElementsByTagName("*") );
+ }
+
+ // Remove any remaining nodes
+ while ( elem.firstChild ) {
+ elem.removeChild( elem.firstChild );
+ }
+ }
+
+ return this;
+ },
+
+ clone: function( events ) {
+ // Do the clone
+ var ret = this.map(function() {
+ if ( !jQuery.support.noCloneEvent && !jQuery.isXMLDoc(this) ) {
+ // IE copies events bound via attachEvent when
+ // using cloneNode. Calling detachEvent on the
+ // clone will also remove the events from the orignal
+ // In order to get around this, we use innerHTML.
+ // Unfortunately, this means some modifications to
+ // attributes in IE that are actually only stored
+ // as properties will not be copied (such as the
+ // the name attribute on an input).
+ var html = this.outerHTML,
+ ownerDocument = this.ownerDocument;
+
+ if ( !html ) {
+ var div = ownerDocument.createElement("div");
+ div.appendChild( this.cloneNode(true) );
+ html = div.innerHTML;
+ }
+
+ return jQuery.clean([html.replace(rinlinejQuery, "")
+ // Handle the case in IE 8 where action=/test/> self-closes a tag
+ .replace(raction, '="$1">')
+ .replace(rleadingWhitespace, "")], ownerDocument)[0];
+ } else {
+ return this.cloneNode(true);
+ }
+ });
+
+ // Copy the events from the original to the clone
+ if ( events === true ) {
+ cloneCopyEvent( this, ret );
+ cloneCopyEvent( this.find("*"), ret.find("*") );
+ }
+
+ // Return the cloned set
+ return ret;
+ },
+
+ html: function( value ) {
+ if ( value === undefined ) {
+ return this[0] && this[0].nodeType === 1 ?
+ this[0].innerHTML.replace(rinlinejQuery, "") :
+ null;
+
+ // See if we can take a shortcut and just use innerHTML
+ } else if ( typeof value === "string" && !rnocache.test( value ) &&
+ (jQuery.support.leadingWhitespace || !rleadingWhitespace.test( value )) &&
+ !wrapMap[ (rtagName.exec( value ) || ["", ""])[1].toLowerCase() ] ) {
+
+ value = value.replace(rxhtmlTag, "<$1></$2>");
+
+ try {
+ for ( var i = 0, l = this.length; i < l; i++ ) {
+ // Remove element nodes and prevent memory leaks
+ if ( this[i].nodeType === 1 ) {
+ jQuery.cleanData( this[i].getElementsByTagName("*") );
+ this[i].innerHTML = value;
+ }
+ }
+
+ // If using innerHTML throws an exception, use the fallback method
+ } catch(e) {
+ this.empty().append( value );
+ }
+
+ } else if ( jQuery.isFunction( value ) ) {
+ this.each(function(i){
+ var self = jQuery( this );
+
+ self.html( value.call(this, i, self.html()) );
+ });
+
+ } else {
+ this.empty().append( value );
+ }
+
+ return this;
+ },
+
+ replaceWith: function( value ) {
+ if ( this[0] && this[0].parentNode ) {
+ // Make sure that the elements are removed from the DOM before they are inserted
+ // this can help fix replacing a parent with child elements
+ if ( jQuery.isFunction( value ) ) {
+ return this.each(function(i) {
+ var self = jQuery(this), old = self.html();
+ self.replaceWith( value.call( this, i, old ) );
+ });
+ }
+
+ if ( typeof value !== "string" ) {
+ value = jQuery( value ).detach();
+ }
+
+ return this.each(function() {
+ var next = this.nextSibling,
+ parent = this.parentNode;
+
+ jQuery( this ).remove();
+
+ if ( next ) {
+ jQuery(next).before( value );
+ } else {
+ jQuery(parent).append( value );
+ }
+ });
+ } else {
+ return this.pushStack( jQuery(jQuery.isFunction(value) ? value() : value), "replaceWith", value );
+ }
+ },
+
+ detach: function( selector ) {
+ return this.remove( selector, true );
+ },
+
+ domManip: function( args, table, callback ) {
+ var results, first, fragment, parent,
+ value = args[0],
+ scripts = [];
+
+ // We can't cloneNode fragments that contain checked, in WebKit
+ if ( !jQuery.support.checkClone && arguments.length === 3 && typeof value === "string" && rchecked.test( value ) ) {
+ return this.each(function() {
+ jQuery(this).domManip( args, table, callback, true );
+ });
+ }
+
+ if ( jQuery.isFunction(value) ) {
+ return this.each(function(i) {
+ var self = jQuery(this);
+ args[0] = value.call(this, i, table ? self.html() : undefined);
+ self.domManip( args, table, callback );
+ });
+ }
+
+ if ( this[0] ) {
+ parent = value && value.parentNode;
+
+ // If we're in a fragment, just use that instead of building a new one
+ if ( jQuery.support.parentNode && parent && parent.nodeType === 11 && parent.childNodes.length === this.length ) {
+ results = { fragment: parent };
+
+ } else {
+ results = jQuery.buildFragment( args, this, scripts );
+ }
+
+ fragment = results.fragment;
+
+ if ( fragment.childNodes.length === 1 ) {
+ first = fragment = fragment.firstChild;
+ } else {
+ first = fragment.firstChild;
+ }
+
+ if ( first ) {
+ table = table && jQuery.nodeName( first, "tr" );
+
+ for ( var i = 0, l = this.length; i < l; i++ ) {
+ callback.call(
+ table ?
+ root(this[i], first) :
+ this[i],
+ i > 0 || results.cacheable || this.length > 1 ?
+ fragment.cloneNode(true) :
+ fragment
+ );
+ }
+ }
+
+ if ( scripts.length ) {
+ jQuery.each( scripts, evalScript );
+ }
+ }
+
+ return this;
+ }
+});
+
+function root( elem, cur ) {
+ return jQuery.nodeName(elem, "table") ?
+ (elem.getElementsByTagName("tbody")[0] ||
+ elem.appendChild(elem.ownerDocument.createElement("tbody"))) :
+ elem;
+}
+
+function cloneCopyEvent(orig, ret) {
+ var i = 0;
+
+ ret.each(function() {
+ if ( this.nodeName !== (orig[i] && orig[i].nodeName) ) {
+ return;
+ }
+
+ var oldData = jQuery.data( orig[i++] ),
+ curData = jQuery.data( this, oldData ),
+ events = oldData && oldData.events;
+
+ if ( events ) {
+ delete curData.handle;
+ curData.events = {};
+
+ for ( var type in events ) {
+ for ( var handler in events[ type ] ) {
+ jQuery.event.add( this, type, events[ type ][ handler ], events[ type ][ handler ].data );
+ }
+ }
+ }
+ });
+}
+
+jQuery.buildFragment = function( args, nodes, scripts ) {
+ var fragment, cacheable, cacheresults,
+ doc = (nodes && nodes[0] ? nodes[0].ownerDocument || nodes[0] : document);
+
+ // Only cache "small" (1/2 KB) strings that are associated with the main document
+ // Cloning options loses the selected state, so don't cache them
+ // IE 6 doesn't like it when you put <object> or <embed> elements in a fragment
+ // Also, WebKit does not clone 'checked' attributes on cloneNode, so don't cache
+ if ( args.length === 1 && typeof args[0] === "string" && args[0].length < 512 && doc === document &&
+ !rnocache.test( args[0] ) && (jQuery.support.checkClone || !rchecked.test( args[0] )) ) {
+
+ cacheable = true;
+ cacheresults = jQuery.fragments[ args[0] ];
+ if ( cacheresults ) {
+ if ( cacheresults !== 1 ) {
+ fragment = cacheresults;
+ }
+ }
+ }
+
+ if ( !fragment ) {
+ fragment = doc.createDocumentFragment();
+ jQuery.clean( args, doc, fragment, scripts );
+ }
+
+ if ( cacheable ) {
+ jQuery.fragments[ args[0] ] = cacheresults ? fragment : 1;
+ }
+
+ return { fragment: fragment, cacheable: cacheable };
+};
+
+jQuery.fragments = {};
+
+jQuery.each({
+ appendTo: "append",
+ prependTo: "prepend",
+ insertBefore: "before",
+ insertAfter: "after",
+ replaceAll: "replaceWith"
+}, function( name, original ) {
+ jQuery.fn[ name ] = function( selector ) {
+ var ret = [],
+ insert = jQuery( selector ),
+ parent = this.length === 1 && this[0].parentNode;
+
+ if ( parent && parent.nodeType === 11 && parent.childNodes.length === 1 && insert.length === 1 ) {
+ insert[ original ]( this[0] );
+ return this;
+
+ } else {
+ for ( var i = 0, l = insert.length; i < l; i++ ) {
+ var elems = (i > 0 ? this.clone(true) : this).get();
+ jQuery( insert[i] )[ original ]( elems );
+ ret = ret.concat( elems );
+ }
+
+ return this.pushStack( ret, name, insert.selector );
+ }
+ };
+});
+
+jQuery.extend({
+ clean: function( elems, context, fragment, scripts ) {
+ context = context || document;
+
+ // !context.createElement fails in IE with an error but returns typeof 'object'
+ if ( typeof context.createElement === "undefined" ) {
+ context = context.ownerDocument || context[0] && context[0].ownerDocument || document;
+ }
+
+ var ret = [];
+
+ for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
+ if ( typeof elem === "number" ) {
+ elem += "";
+ }
+
+ if ( !elem ) {
+ continue;
+ }
+
+ // Convert html string into DOM nodes
+ if ( typeof elem === "string" && !rhtml.test( elem ) ) {
+ elem = context.createTextNode( elem );
+
+ } else if ( typeof elem === "string" ) {
+ // Fix "XHTML"-style tags in all browsers
+ elem = elem.replace(rxhtmlTag, "<$1></$2>");
+
+ // Trim whitespace, otherwise indexOf won't work as expected
+ var tag = (rtagName.exec( elem ) || ["", ""])[1].toLowerCase(),
+ wrap = wrapMap[ tag ] || wrapMap._default,
+ depth = wrap[0],
+ div = context.createElement("div");
+
+ // Go to html and back, then peel off extra wrappers
+ div.innerHTML = wrap[1] + elem + wrap[2];
+
+ // Move to the right depth
+ while ( depth-- ) {
+ div = div.lastChild;
+ }
+
+ // Remove IE's autoinserted <tbody> from table fragments
+ if ( !jQuery.support.tbody ) {
+
+ // String was a <table>, *may* have spurious <tbody>
+ var hasBody = rtbody.test(elem),
+ tbody = tag === "table" && !hasBody ?
+ div.firstChild && div.firstChild.childNodes :
+
+ // String was a bare <thead> or <tfoot>
+ wrap[1] === "<table>" && !hasBody ?
+ div.childNodes :
+ [];
+
+ for ( var j = tbody.length - 1; j >= 0 ; --j ) {
+ if ( jQuery.nodeName( tbody[ j ], "tbody" ) && !tbody[ j ].childNodes.length ) {
+ tbody[ j ].parentNode.removeChild( tbody[ j ] );
+ }
+ }
+
+ }
+
+ // IE completely kills leading whitespace when innerHTML is used
+ if ( !jQuery.support.leadingWhitespace && rleadingWhitespace.test( elem ) ) {
+ div.insertBefore( context.createTextNode( rleadingWhitespace.exec(elem)[0] ), div.firstChild );
+ }
+
+ elem = div.childNodes;
+ }
+
+ if ( elem.nodeType ) {
+ ret.push( elem );
+ } else {
+ ret = jQuery.merge( ret, elem );
+ }
+ }
+
+ if ( fragment ) {
+ for ( i = 0; ret[i]; i++ ) {
+ if ( scripts && jQuery.nodeName( ret[i], "script" ) && (!ret[i].type || ret[i].type.toLowerCase() === "text/javascript") ) {
+ scripts.push( ret[i].parentNode ? ret[i].parentNode.removeChild( ret[i] ) : ret[i] );
+
+ } else {
+ if ( ret[i].nodeType === 1 ) {
+ ret.splice.apply( ret, [i + 1, 0].concat(jQuery.makeArray(ret[i].getElementsByTagName("script"))) );
+ }
+ fragment.appendChild( ret[i] );
+ }
+ }
+ }
+
+ return ret;
+ },
+
+ cleanData: function( elems ) {
+ var data, id, cache = jQuery.cache,
+ special = jQuery.event.special,
+ deleteExpando = jQuery.support.deleteExpando;
+
+ for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
+ if ( elem.nodeName && jQuery.noData[elem.nodeName.toLowerCase()] ) {
+ continue;
+ }
+
+ id = elem[ jQuery.expando ];
+
+ if ( id ) {
+ data = cache[ id ];
+
+ if ( data && data.events ) {
+ for ( var type in data.events ) {
+ if ( special[ type ] ) {
+ jQuery.event.remove( elem, type );
+
+ } else {
+ jQuery.removeEvent( elem, type, data.handle );
+ }
+ }
+ }
+
+ if ( deleteExpando ) {
+ delete elem[ jQuery.expando ];
+
+ } else if ( elem.removeAttribute ) {
+ elem.removeAttribute( jQuery.expando );
+ }
+
+ delete cache[ id ];
+ }
+ }
+ }
+});
+
+function evalScript( i, elem ) {
+ if ( elem.src ) {
+ jQuery.ajax({
+ url: elem.src,
+ async: false,
+ dataType: "script"
+ });
+ } else {
+ jQuery.globalEval( elem.text || elem.textContent || elem.innerHTML || "" );
+ }
+
+ if ( elem.parentNode ) {
+ elem.parentNode.removeChild( elem );
+ }
+}
+
+
+
+
+var ralpha = /alpha\([^)]*\)/i,
+ ropacity = /opacity=([^)]*)/,
+ rdashAlpha = /-([a-z])/ig,
+ rupper = /([A-Z])/g,
+ rnumpx = /^-?\d+(?:px)?$/i,
+ rnum = /^-?\d/,
+
+ cssShow = { position: "absolute", visibility: "hidden", display: "block" },
+ cssWidth = [ "Left", "Right" ],
+ cssHeight = [ "Top", "Bottom" ],
+ curCSS,
+
+ getComputedStyle,
+ currentStyle,
+
+ fcamelCase = function( all, letter ) {
+ return letter.toUpperCase();
+ };
+
+jQuery.fn.css = function( name, value ) {
+ // Setting 'undefined' is a no-op
+ if ( arguments.length === 2 && value === undefined ) {
+ return this;
+ }
+
+ return jQuery.access( this, name, value, true, function( elem, name, value ) {
+ return value !== undefined ?
+ jQuery.style( elem, name, value ) :
+ jQuery.css( elem, name );
+ });
+};
+
+jQuery.extend({
+ // Add in style property hooks for overriding the default
+ // behavior of getting and setting a style property
+ cssHooks: {
+ opacity: {
+ get: function( elem, computed ) {
+ if ( computed ) {
+ // We should always get a number back from opacity
+ var ret = curCSS( elem, "opacity", "opacity" );
+ return ret === "" ? "1" : ret;
+
+ } else {
+ return elem.style.opacity;
+ }
+ }
+ }
+ },
+
+ // Exclude the following css properties to add px
+ cssNumber: {
+ "zIndex": true,
+ "fontWeight": true,
+ "opacity": true,
+ "zoom": true,
+ "lineHeight": true
+ },
+
+ // Add in properties whose names you wish to fix before
+ // setting or getting the value
+ cssProps: {
+ // normalize float css property
+ "float": jQuery.support.cssFloat ? "cssFloat" : "styleFloat"
+ },
+
+ // Get and set the style property on a DOM Node
+ style: function( elem, name, value, extra ) {
+ // Don't set styles on text and comment nodes
+ if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) {
+ return;
+ }
+
+ // Make sure that we're working with the right name
+ var ret, origName = jQuery.camelCase( name ),
+ style = elem.style, hooks = jQuery.cssHooks[ origName ];
+
+ name = jQuery.cssProps[ origName ] || origName;
+
+ // Check if we're setting a value
+ if ( value !== undefined ) {
+ // Make sure that NaN and null values aren't set. See: #7116
+ if ( typeof value === "number" && isNaN( value ) || value == null ) {
+ return;
+ }
+
+ // If a number was passed in, add 'px' to the (except for certain CSS properties)
+ if ( typeof value === "number" && !jQuery.cssNumber[ origName ] ) {
+ value += "px";
+ }
+
+ // If a hook was provided, use that value, otherwise just set the specified value
+ if ( !hooks || !("set" in hooks) || (value = hooks.set( elem, value )) !== undefined ) {
+ // Wrapped to prevent IE from throwing errors when 'invalid' values are provided
+ // Fixes bug #5509
+ try {
+ style[ name ] = value;
+ } catch(e) {}
+ }
+
+ } else {
+ // If a hook was provided get the non-computed value from there
+ if ( hooks && "get" in hooks && (ret = hooks.get( elem, false, extra )) !== undefined ) {
+ return ret;
+ }
+
+ // Otherwise just get the value from the style object
+ return style[ name ];
+ }
+ },
+
+ css: function( elem, name, extra ) {
+ // Make sure that we're working with the right name
+ var ret, origName = jQuery.camelCase( name ),
+ hooks = jQuery.cssHooks[ origName ];
+
+ name = jQuery.cssProps[ origName ] || origName;
+
+ // If a hook was provided get the computed value from there
+ if ( hooks && "get" in hooks && (ret = hooks.get( elem, true, extra )) !== undefined ) {
+ return ret;
+
+ // Otherwise, if a way to get the computed value exists, use that
+ } else if ( curCSS ) {
+ return curCSS( elem, name, origName );
+ }
+ },
+
+ // A method for quickly swapping in/out CSS properties to get correct calculations
+ swap: function( elem, options, callback ) {
+ var old = {};
+
+ // Remember the old values, and insert the new ones
+ for ( var name in options ) {
+ old[ name ] = elem.style[ name ];
+ elem.style[ name ] = options[ name ];
+ }
+
+ callback.call( elem );
+
+ // Revert the old values
+ for ( name in options ) {
+ elem.style[ name ] = old[ name ];
+ }
+ },
+
+ camelCase: function( string ) {
+ return string.replace( rdashAlpha, fcamelCase );
+ }
+});
+
+// DEPRECATED, Use jQuery.css() instead
+jQuery.curCSS = jQuery.css;
+
+jQuery.each(["height", "width"], function( i, name ) {
+ jQuery.cssHooks[ name ] = {
+ get: function( elem, computed, extra ) {
+ var val;
+
+ if ( computed ) {
+ if ( elem.offsetWidth !== 0 ) {
+ val = getWH( elem, name, extra );
+
+ } else {
+ jQuery.swap( elem, cssShow, function() {
+ val = getWH( elem, name, extra );
+ });
+ }
+
+ if ( val <= 0 ) {
+ val = curCSS( elem, name, name );
+
+ if ( val === "0px" && currentStyle ) {
+ val = currentStyle( elem, name, name );
+ }
+
+ if ( val != null ) {
+ // Should return "auto" instead of 0, use 0 for
+ // temporary backwards-compat
+ return val === "" || val === "auto" ? "0px" : val;
+ }
+ }
+
+ if ( val < 0 || val == null ) {
+ val = elem.style[ name ];
+
+ // Should return "auto" instead of 0, use 0 for
+ // temporary backwards-compat
+ return val === "" || val === "auto" ? "0px" : val;
+ }
+
+ return typeof val === "string" ? val : val + "px";
+ }
+ },
+
+ set: function( elem, value ) {
+ if ( rnumpx.test( value ) ) {
+ // ignore negative width and height values #1599
+ value = parseFloat(value);
+
+ if ( value >= 0 ) {
+ return value + "px";
+ }
+
+ } else {
+ return value;
+ }
+ }
+ };
+});
+
+if ( !jQuery.support.opacity ) {
+ jQuery.cssHooks.opacity = {
+ get: function( elem, computed ) {
+ // IE uses filters for opacity
+ return ropacity.test((computed && elem.currentStyle ? elem.currentStyle.filter : elem.style.filter) || "") ?
+ (parseFloat(RegExp.$1) / 100) + "" :
+ computed ? "1" : "";
+ },
+
+ set: function( elem, value ) {
+ var style = elem.style;
+
+ // IE has trouble with opacity if it does not have layout
+ // Force it by setting the zoom level
+ style.zoom = 1;
+
+ // Set the alpha filter to set the opacity
+ var opacity = jQuery.isNaN(value) ?
+ "" :
+ "alpha(opacity=" + value * 100 + ")",
+ filter = style.filter || "";
+
+ style.filter = ralpha.test(filter) ?
+ filter.replace(ralpha, opacity) :
+ style.filter + ' ' + opacity;
+ }
+ };
+}
+
+if ( document.defaultView && document.defaultView.getComputedStyle ) {
+ getComputedStyle = function( elem, newName, name ) {
+ var ret, defaultView, computedStyle;
+
+ name = name.replace( rupper, "-$1" ).toLowerCase();
+
+ if ( !(defaultView = elem.ownerDocument.defaultView) ) {
+ return undefined;
+ }
+
+ if ( (computedStyle = defaultView.getComputedStyle( elem, null )) ) {
+ ret = computedStyle.getPropertyValue( name );
+ if ( ret === "" && !jQuery.contains( elem.ownerDocument.documentElement, elem ) ) {
+ ret = jQuery.style( elem, name );
+ }
+ }
+
+ return ret;
+ };
+}
+
+if ( document.documentElement.currentStyle ) {
+ currentStyle = function( elem, name ) {
+ var left, rsLeft,
+ ret = elem.currentStyle && elem.currentStyle[ name ],
+ style = elem.style;
+
+ // From the awesome hack by Dean Edwards
+ // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291
+
+ // If we're not dealing with a regular pixel number
+ // but a number that has a weird ending, we need to convert it to pixels
+ if ( !rnumpx.test( ret ) && rnum.test( ret ) ) {
+ // Remember the original values
+ left = style.left;
+ rsLeft = elem.runtimeStyle.left;
+
+ // Put in the new values to get a computed value out
+ elem.runtimeStyle.left = elem.currentStyle.left;
+ style.left = name === "fontSize" ? "1em" : (ret || 0);
+ ret = style.pixelLeft + "px";
+
+ // Revert the changed values
+ style.left = left;
+ elem.runtimeStyle.left = rsLeft;
+ }
+
+ return ret === "" ? "auto" : ret;
+ };
+}
+
+curCSS = getComputedStyle || currentStyle;
+
+function getWH( elem, name, extra ) {
+ var which = name === "width" ? cssWidth : cssHeight,
+ val = name === "width" ? elem.offsetWidth : elem.offsetHeight;
+
+ if ( extra === "border" ) {
+ return val;
+ }
+
+ jQuery.each( which, function() {
+ if ( !extra ) {
+ val -= parseFloat(jQuery.css( elem, "padding" + this )) || 0;
+ }
+
+ if ( extra === "margin" ) {
+ val += parseFloat(jQuery.css( elem, "margin" + this )) || 0;
+
+ } else {
+ val -= parseFloat(jQuery.css( elem, "border" + this + "Width" )) || 0;
+ }
+ });
+
+ return val;
+}
+
+if ( jQuery.expr && jQuery.expr.filters ) {
+ jQuery.expr.filters.hidden = function( elem ) {
+ var width = elem.offsetWidth,
+ height = elem.offsetHeight;
+
+ return (width === 0 && height === 0) || (!jQuery.support.reliableHiddenOffsets && (elem.style.display || jQuery.css( elem, "display" )) === "none");
+ };
+
+ jQuery.expr.filters.visible = function( elem ) {
+ return !jQuery.expr.filters.hidden( elem );
+ };
+}
+
+
+
+
+var jsc = jQuery.now(),
+ rscript = /<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,
+ rselectTextarea = /^(?:select|textarea)/i,
+ rinput = /^(?:color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,
+ rnoContent = /^(?:GET|HEAD)$/,
+ rbracket = /\[\]$/,
+ jsre = /\=\?(&|$)/,
+ rquery = /\?/,
+ rts = /([?&])_=[^&]*/,
+ rurl = /^(\w+:)?\/\/([^\/?#]+)/,
+ r20 = /%20/g,
+ rhash = /#.*$/,
+
+ // Keep a copy of the old load method
+ _load = jQuery.fn.load;
+
+jQuery.fn.extend({
+ load: function( url, params, callback ) {
+ if ( typeof url !== "string" && _load ) {
+ return _load.apply( this, arguments );
+
+ // Don't do a request if no elements are being requested
+ } else if ( !this.length ) {
+ return this;
+ }
+
+ var off = url.indexOf(" ");
+ if ( off >= 0 ) {
+ var selector = url.slice(off, url.length);
+ url = url.slice(0, off);
+ }
+
+ // Default to a GET request
+ var type = "GET";
+
+ // If the second parameter was provided
+ if ( params ) {
+ // If it's a function
+ if ( jQuery.isFunction( params ) ) {
+ // We assume that it's the callback
+ callback = params;
+ params = null;
+
+ // Otherwise, build a param string
+ } else if ( typeof params === "object" ) {
+ params = jQuery.param( params, jQuery.ajaxSettings.traditional );
+ type = "POST";
+ }
+ }
+
+ var self = this;
+
+ // Request the remote document
+ jQuery.ajax({
+ url: url,
+ type: type,
+ dataType: "html",
+ data: params,
+ complete: function( res, status ) {
+ // If successful, inject the HTML into all the matched elements
+ if ( status === "success" || status === "notmodified" ) {
+ // See if a selector was specified
+ self.html( selector ?
+ // Create a dummy div to hold the results
+ jQuery("<div>")
+ // inject the contents of the document in, removing the scripts
+ // to avoid any 'Permission Denied' errors in IE
+ .append(res.responseText.replace(rscript, ""))
+
+ // Locate the specified elements
+ .find(selector) :
+
+ // If not, just inject the full result
+ res.responseText );
+ }
+
+ if ( callback ) {
+ self.each( callback, [res.responseText, status, res] );
+ }
+ }
+ });
+
+ return this;
+ },
+
+ serialize: function() {
+ return jQuery.param(this.serializeArray());
+ },
+
+ serializeArray: function() {
+ return this.map(function() {
+ return this.elements ? jQuery.makeArray(this.elements) : this;
+ })
+ .filter(function() {
+ return this.name && !this.disabled &&
+ (this.checked || rselectTextarea.test(this.nodeName) ||
+ rinput.test(this.type));
+ })
+ .map(function( i, elem ) {
+ var val = jQuery(this).val();
+
+ return val == null ?
+ null :
+ jQuery.isArray(val) ?
+ jQuery.map( val, function( val, i ) {
+ return { name: elem.name, value: val };
+ }) :
+ { name: elem.name, value: val };
+ }).get();
+ }
+});
+
+// Attach a bunch of functions for handling common AJAX events
+jQuery.each( "ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "), function( i, o ) {
+ jQuery.fn[o] = function( f ) {
+ return this.bind(o, f);
+ };
+});
+
+jQuery.extend({
+ get: function( url, data, callback, type ) {
+ // shift arguments if data argument was omited
+ if ( jQuery.isFunction( data ) ) {
+ type = type || callback;
+ callback = data;
+ data = null;
+ }
+
+ return jQuery.ajax({
+ type: "GET",
+ url: url,
+ data: data,
+ success: callback,
+ dataType: type
+ });
+ },
+
+ getScript: function( url, callback ) {
+ return jQuery.get(url, null, callback, "script");
+ },
+
+ getJSON: function( url, data, callback ) {
+ return jQuery.get(url, data, callback, "json");
+ },
+
+ post: function( url, data, callback, type ) {
+ // shift arguments if data argument was omited
+ if ( jQuery.isFunction( data ) ) {
+ type = type || callback;
+ callback = data;
+ data = {};
+ }
+
+ return jQuery.ajax({
+ type: "POST",
+ url: url,
+ data: data,
+ success: callback,
+ dataType: type
+ });
+ },
+
+ ajaxSetup: function( settings ) {
+ jQuery.extend( jQuery.ajaxSettings, settings );
+ },
+
+ ajaxSettings: {
+ url: location.href,
+ global: true,
+ type: "GET",
+ contentType: "application/x-www-form-urlencoded",
+ processData: true,
+ async: true,
+ /*
+ timeout: 0,
+ data: null,
+ username: null,
+ password: null,
+ traditional: false,
+ */
+ // This function can be overriden by calling jQuery.ajaxSetup
+ xhr: function() {
+ return new window.XMLHttpRequest();
+ },
+ accepts: {
+ xml: "application/xml, text/xml",
+ html: "text/html",
+ script: "text/javascript, application/javascript",
+ json: "application/json, text/javascript",
+ text: "text/plain",
+ _default: "*/*"
+ }
+ },
+
+ ajax: function( origSettings ) {
+ var s = jQuery.extend(true, {}, jQuery.ajaxSettings, origSettings),
+ jsonp, status, data, type = s.type.toUpperCase(), noContent = rnoContent.test(type);
+
+ s.url = s.url.replace( rhash, "" );
+
+ // Use original (not extended) context object if it was provided
+ s.context = origSettings && origSettings.context != null ? origSettings.context : s;
+
+ // convert data if not already a string
+ if ( s.data && s.processData && typeof s.data !== "string" ) {
+ s.data = jQuery.param( s.data, s.traditional );
+ }
+
+ // Handle JSONP Parameter Callbacks
+ if ( s.dataType === "jsonp" ) {
+ if ( type === "GET" ) {
+ if ( !jsre.test( s.url ) ) {
+ s.url += (rquery.test( s.url ) ? "&" : "?") + (s.jsonp || "callback") + "=?";
+ }
+ } else if ( !s.data || !jsre.test(s.data) ) {
+ s.data = (s.data ? s.data + "&" : "") + (s.jsonp || "callback") + "=?";
+ }
+ s.dataType = "json";
+ }
+
+ // Build temporary JSONP function
+ if ( s.dataType === "json" && (s.data && jsre.test(s.data) || jsre.test(s.url)) ) {
+ jsonp = s.jsonpCallback || ("jsonp" + jsc++);
+
+ // Replace the =? sequence both in the query string and the data
+ if ( s.data ) {
+ s.data = (s.data + "").replace(jsre, "=" + jsonp + "$1");
+ }
+
+ s.url = s.url.replace(jsre, "=" + jsonp + "$1");
+
+ // We need to make sure
+ // that a JSONP style response is executed properly
+ s.dataType = "script";
+
+ // Handle JSONP-style loading
+ var customJsonp = window[ jsonp ];
+
+ window[ jsonp ] = function( tmp ) {
+ if ( jQuery.isFunction( customJsonp ) ) {
+ customJsonp( tmp );
+
+ } else {
+ // Garbage collect
+ window[ jsonp ] = undefined;
+
+ try {
+ delete window[ jsonp ];
+ } catch( jsonpError ) {}
+ }
+
+ data = tmp;
+ jQuery.handleSuccess( s, xhr, status, data );
+ jQuery.handleComplete( s, xhr, status, data );
+
+ if ( head ) {
+ head.removeChild( script );
+ }
+ };
+ }
+
+ if ( s.dataType === "script" && s.cache === null ) {
+ s.cache = false;
+ }
+
+ if ( s.cache === false && noContent ) {
+ var ts = jQuery.now();
+
+ // try replacing _= if it is there
+ var ret = s.url.replace(rts, "$1_=" + ts);
+
+ // if nothing was replaced, add timestamp to the end
+ s.url = ret + ((ret === s.url) ? (rquery.test(s.url) ? "&" : "?") + "_=" + ts : "");
+ }
+
+ // If data is available, append data to url for GET/HEAD requests
+ if ( s.data && noContent ) {
+ s.url += (rquery.test(s.url) ? "&" : "?") + s.data;
+ }
+
+ // Watch for a new set of requests
+ if ( s.global && jQuery.active++ === 0 ) {
+ jQuery.event.trigger( "ajaxStart" );
+ }
+
+ // Matches an absolute URL, and saves the domain
+ var parts = rurl.exec( s.url ),
+ remote = parts && (parts[1] && parts[1].toLowerCase() !== location.protocol || parts[2].toLowerCase() !== location.host);
+
+ // If we're requesting a remote document
+ // and trying to load JSON or Script with a GET
+ if ( s.dataType === "script" && type === "GET" && remote ) {
+ var head = document.getElementsByTagName("head")[0] || document.documentElement;
+ var script = document.createElement("script");
+ if ( s.scriptCharset ) {
+ script.charset = s.scriptCharset;
+ }
+ script.src = s.url;
+
+ // Handle Script loading
+ if ( !jsonp ) {
+ var done = false;
+
+ // Attach handlers for all browsers
+ script.onload = script.onreadystatechange = function() {
+ if ( !done && (!this.readyState ||
+ this.readyState === "loaded" || this.readyState === "complete") ) {
+ done = true;
+ jQuery.handleSuccess( s, xhr, status, data );
+ jQuery.handleComplete( s, xhr, status, data );
+
+ // Handle memory leak in IE
+ script.onload = script.onreadystatechange = null;
+ if ( head && script.parentNode ) {
+ head.removeChild( script );
+ }
+ }
+ };
+ }
+
+ // Use insertBefore instead of appendChild to circumvent an IE6 bug.
+ // This arises when a base node is used (#2709 and #4378).
+ head.insertBefore( script, head.firstChild );
+
+ // We handle everything using the script element injection
+ return undefined;
+ }
+
+ var requestDone = false;
+
+ // Create the request object
+ var xhr = s.xhr();
+
+ if ( !xhr ) {
+ return;
+ }
+
+ // Open the socket
+ // Passing null username, generates a login popup on Opera (#2865)
+ if ( s.username ) {
+ xhr.open(type, s.url, s.async, s.username, s.password);
+ } else {
+ xhr.open(type, s.url, s.async);
+ }
+
+ // Need an extra try/catch for cross domain requests in Firefox 3
+ try {
+ // Set content-type if data specified and content-body is valid for this type
+ if ( (s.data != null && !noContent) || (origSettings && origSettings.contentType) ) {
+ xhr.setRequestHeader("Content-Type", s.contentType);
+ }
+
+ // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
+ if ( s.ifModified ) {
+ if ( jQuery.lastModified[s.url] ) {
+ xhr.setRequestHeader("If-Modified-Since", jQuery.lastModified[s.url]);
+ }
+
+ if ( jQuery.etag[s.url] ) {
+ xhr.setRequestHeader("If-None-Match", jQuery.etag[s.url]);
+ }
+ }
+
+ // Set header so the called script knows that it's an XMLHttpRequest
+ // Only send the header if it's not a remote XHR
+ if ( !remote ) {
+ xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
+ }
+
+ // Set the Accepts header for the server, depending on the dataType
+ xhr.setRequestHeader("Accept", s.dataType && s.accepts[ s.dataType ] ?
+ s.accepts[ s.dataType ] + ", */*; q=0.01" :
+ s.accepts._default );
+ } catch( headerError ) {}
+
+ // Allow custom headers/mimetypes and early abort
+ if ( s.beforeSend && s.beforeSend.call(s.context, xhr, s) === false ) {
+ // Handle the global AJAX counter
+ if ( s.global && jQuery.active-- === 1 ) {
+ jQuery.event.trigger( "ajaxStop" );
+ }
+
+ // close opended socket
+ xhr.abort();
+ return false;
+ }
+
+ if ( s.global ) {
+ jQuery.triggerGlobal( s, "ajaxSend", [xhr, s] );
+ }
+
+ // Wait for a response to come back
+ var onreadystatechange = xhr.onreadystatechange = function( isTimeout ) {
+ // The request was aborted
+ if ( !xhr || xhr.readyState === 0 || isTimeout === "abort" ) {
+ // Opera doesn't call onreadystatechange before this point
+ // so we simulate the call
+ if ( !requestDone ) {
+ jQuery.handleComplete( s, xhr, status, data );
+ }
+
+ requestDone = true;
+ if ( xhr ) {
+ xhr.onreadystatechange = jQuery.noop;
+ }
+
+ // The transfer is complete and the data is available, or the request timed out
+ } else if ( !requestDone && xhr && (xhr.readyState === 4 || isTimeout === "timeout") ) {
+ requestDone = true;
+ xhr.onreadystatechange = jQuery.noop;
+
+ status = isTimeout === "timeout" ?
+ "timeout" :
+ !jQuery.httpSuccess( xhr ) ?
+ "error" :
+ s.ifModified && jQuery.httpNotModified( xhr, s.url ) ?
+ "notmodified" :
+ "success";
+
+ var errMsg;
+
+ if ( status === "success" ) {
+ // Watch for, and catch, XML document parse errors
+ try {
+ // process the data (runs the xml through httpData regardless of callback)
+ data = jQuery.httpData( xhr, s.dataType, s );
+ } catch( parserError ) {
+ status = "parsererror";
+ errMsg = parserError;
+ }
+ }
+
+ // Make sure that the request was successful or notmodified
+ if ( status === "success" || status === "notmodified" ) {
+ // JSONP handles its own success callback
+ if ( !jsonp ) {
+ jQuery.handleSuccess( s, xhr, status, data );
+ }
+ } else {
+ jQuery.handleError( s, xhr, status, errMsg );
+ }
+
+ // Fire the complete handlers
+ if ( !jsonp ) {
+ jQuery.handleComplete( s, xhr, status, data );
+ }
+
+ if ( isTimeout === "timeout" ) {
+ xhr.abort();
+ }
+
+ // Stop memory leaks
+ if ( s.async ) {
+ xhr = null;
+ }
+ }
+ };
+
+ // Override the abort handler, if we can (IE 6 doesn't allow it, but that's OK)
+ // Opera doesn't fire onreadystatechange at all on abort
+ try {
+ var oldAbort = xhr.abort;
+ xhr.abort = function() {
+ if ( xhr ) {
+ // oldAbort has no call property in IE7 so
+ // just do it this way, which works in all
+ // browsers
+ Function.prototype.call.call( oldAbort, xhr );
+ }
+
+ onreadystatechange( "abort" );
+ };
+ } catch( abortError ) {}
+
+ // Timeout checker
+ if ( s.async && s.timeout > 0 ) {
+ setTimeout(function() {
+ // Check to see if the request is still happening
+ if ( xhr && !requestDone ) {
+ onreadystatechange( "timeout" );
+ }
+ }, s.timeout);
+ }
+
+ // Send the data
+ try {
+ xhr.send( noContent || s.data == null ? null : s.data );
+
+ } catch( sendError ) {
+ jQuery.handleError( s, xhr, null, sendError );
+
+ // Fire the complete handlers
+ jQuery.handleComplete( s, xhr, status, data );
+ }
+
+ // firefox 1.5 doesn't fire statechange for sync requests
+ if ( !s.async ) {
+ onreadystatechange();
+ }
+
+ // return XMLHttpRequest to allow aborting the request etc.
+ return xhr;
+ },
+
+ // Serialize an array of form elements or a set of
+ // key/values into a query string
+ param: function( a, traditional ) {
+ var s = [],
+ add = function( key, value ) {
+ // If value is a function, invoke it and return its value
+ value = jQuery.isFunction(value) ? value() : value;
+ s[ s.length ] = encodeURIComponent(key) + "=" + encodeURIComponent(value);
+ };
+
+ // Set traditional to true for jQuery <= 1.3.2 behavior.
+ if ( traditional === undefined ) {
+ traditional = jQuery.ajaxSettings.traditional;
+ }
+
+ // If an array was passed in, assume that it is an array of form elements.
+ if ( jQuery.isArray(a) || a.jquery ) {
+ // Serialize the form elements
+ jQuery.each( a, function() {
+ add( this.name, this.value );
+ });
+
+ } else {
+ // If traditional, encode the "old" way (the way 1.3.2 or older
+ // did it), otherwise encode params recursively.
+ for ( var prefix in a ) {
+ buildParams( prefix, a[prefix], traditional, add );
+ }
+ }
+
+ // Return the resulting serialization
+ return s.join("&").replace(r20, "+");
+ }
+});
+
+function buildParams( prefix, obj, traditional, add ) {
+ if ( jQuery.isArray(obj) && obj.length ) {
+ // Serialize array item.
+ jQuery.each( obj, function( i, v ) {
+ if ( traditional || rbracket.test( prefix ) ) {
+ // Treat each array item as a scalar.
+ add( prefix, v );
+
+ } else {
+ // If array item is non-scalar (array or object), encode its
+ // numeric index to resolve deserialization ambiguity issues.
+ // Note that rack (as of 1.0.0) can't currently deserialize
+ // nested arrays properly, and attempting to do so may cause
+ // a server error. Possible fixes are to modify rack's
+ // deserialization algorithm or to provide an option or flag
+ // to force array serialization to be shallow.
+ buildParams( prefix + "[" + ( typeof v === "object" || jQuery.isArray(v) ? i : "" ) + "]", v, traditional, add );
+ }
+ });
+
+ } else if ( !traditional && obj != null && typeof obj === "object" ) {
+ if ( jQuery.isEmptyObject( obj ) ) {
+ add( prefix, "" );
+
+ // Serialize object item.
+ } else {
+ jQuery.each( obj, function( k, v ) {
+ buildParams( prefix + "[" + k + "]", v, traditional, add );
+ });
+ }
+
+ } else {
+ // Serialize scalar item.
+ add( prefix, obj );
+ }
+}
+
+// This is still on the jQuery object... for now
+// Want to move this to jQuery.ajax some day
+jQuery.extend({
+
+ // Counter for holding the number of active queries
+ active: 0,
+
+ // Last-Modified header cache for next request
+ lastModified: {},
+ etag: {},
+
+ handleError: function( s, xhr, status, e ) {
+ // If a local callback was specified, fire it
+ if ( s.error ) {
+ s.error.call( s.context, xhr, status, e );
+ }
+
+ // Fire the global callback
+ if ( s.global ) {
+ jQuery.triggerGlobal( s, "ajaxError", [xhr, s, e] );
+ }
+ },
+
+ handleSuccess: function( s, xhr, status, data ) {
+ // If a local callback was specified, fire it and pass it the data
+ if ( s.success ) {
+ s.success.call( s.context, data, status, xhr );
+ }
+
+ // Fire the global callback
+ if ( s.global ) {
+ jQuery.triggerGlobal( s, "ajaxSuccess", [xhr, s] );
+ }
+ },
+
+ handleComplete: function( s, xhr, status ) {
+ // Process result
+ if ( s.complete ) {
+ s.complete.call( s.context, xhr, status );
+ }
+
+ // The request was completed
+ if ( s.global ) {
+ jQuery.triggerGlobal( s, "ajaxComplete", [xhr, s] );
+ }
+
+ // Handle the global AJAX counter
+ if ( s.global && jQuery.active-- === 1 ) {
+ jQuery.event.trigger( "ajaxStop" );
+ }
+ },
+
+ triggerGlobal: function( s, type, args ) {
+ (s.context && s.context.url == null ? jQuery(s.context) : jQuery.event).trigger(type, args);
+ },
+
+ // Determines if an XMLHttpRequest was successful or not
+ httpSuccess: function( xhr ) {
+ try {
+ // IE error sometimes returns 1223 when it should be 204 so treat it as success, see #1450
+ return !xhr.status && location.protocol === "file:" ||
+ xhr.status >= 200 && xhr.status < 300 ||
+ xhr.status === 304 || xhr.status === 1223;
+ } catch(e) {}
+
+ return false;
+ },
+
+ // Determines if an XMLHttpRequest returns NotModified
+ httpNotModified: function( xhr, url ) {
+ var lastModified = xhr.getResponseHeader("Last-Modified"),
+ etag = xhr.getResponseHeader("Etag");
+
+ if ( lastModified ) {
+ jQuery.lastModified[url] = lastModified;
+ }
+
+ if ( etag ) {
+ jQuery.etag[url] = etag;
+ }
+
+ return xhr.status === 304;
+ },
+
+ httpData: function( xhr, type, s ) {
+ var ct = xhr.getResponseHeader("content-type") || "",
+ xml = type === "xml" || !type && ct.indexOf("xml") >= 0,
+ data = xml ? xhr.responseXML : xhr.responseText;
+
+ if ( xml && data.documentElement.nodeName === "parsererror" ) {
+ jQuery.error( "parsererror" );
+ }
+
+ // Allow a pre-filtering function to sanitize the response
+ // s is checked to keep backwards compatibility
+ if ( s && s.dataFilter ) {
+ data = s.dataFilter( data, type );
+ }
+
+ // The filter can actually parse the response
+ if ( typeof data === "string" ) {
+ // Get the JavaScript object, if JSON is used.
+ if ( type === "json" || !type && ct.indexOf("json") >= 0 ) {
+ data = jQuery.parseJSON( data );
+
+ // If the type is "script", eval it in global context
+ } else if ( type === "script" || !type && ct.indexOf("javascript") >= 0 ) {
+ jQuery.globalEval( data );
+ }
+ }
+
+ return data;
+ }
+
+});
+
+/*
+ * Create the request object; Microsoft failed to properly
+ * implement the XMLHttpRequest in IE7 (can't request local files),
+ * so we use the ActiveXObject when it is available
+ * Additionally XMLHttpRequest can be disabled in IE7/IE8 so
+ * we need a fallback.
+ */
+if ( window.ActiveXObject ) {
+ jQuery.ajaxSettings.xhr = function() {
+ if ( window.location.protocol !== "file:" ) {
+ try {
+ return new window.XMLHttpRequest();
+ } catch(xhrError) {}
+ }
+
+ try {
+ return new window.ActiveXObject("Microsoft.XMLHTTP");
+ } catch(activeError) {}
+ };
+}
+
+// Does this browser support XHR requests?
+jQuery.support.ajax = !!jQuery.ajaxSettings.xhr();
+
+
+
+
+var elemdisplay = {},
+ rfxtypes = /^(?:toggle|show|hide)$/,
+ rfxnum = /^([+\-]=)?([\d+.\-]+)(.*)$/,
+ timerId,
+ fxAttrs = [
+ // height animations
+ [ "height", "marginTop", "marginBottom", "paddingTop", "paddingBottom" ],
+ // width animations
+ [ "width", "marginLeft", "marginRight", "paddingLeft", "paddingRight" ],
+ // opacity animations
+ [ "opacity" ]
+ ];
+
+jQuery.fn.extend({
+ show: function( speed, easing, callback ) {
+ var elem, display;
+
+ if ( speed || speed === 0 ) {
+ return this.animate( genFx("show", 3), speed, easing, callback);
+
+ } else {
+ for ( var i = 0, j = this.length; i < j; i++ ) {
+ elem = this[i];
+ display = elem.style.display;
+
+ // Reset the inline display of this element to learn if it is
+ // being hidden by cascaded rules or not
+ if ( !jQuery.data(elem, "olddisplay") && display === "none" ) {
+ display = elem.style.display = "";
+ }
+
+ // Set elements which have been overridden with display: none
+ // in a stylesheet to whatever the default browser style is
+ // for such an element
+ if ( display === "" && jQuery.css( elem, "display" ) === "none" ) {
+ jQuery.data(elem, "olddisplay", defaultDisplay(elem.nodeName));
+ }
+ }
+
+ // Set the display of most of the elements in a second loop
+ // to avoid the constant reflow
+ for ( i = 0; i < j; i++ ) {
+ elem = this[i];
+ display = elem.style.display;
+
+ if ( display === "" || display === "none" ) {
+ elem.style.display = jQuery.data(elem, "olddisplay") || "";
+ }
+ }
+
+ return this;
+ }
+ },
+
+ hide: function( speed, easing, callback ) {
+ if ( speed || speed === 0 ) {
+ return this.animate( genFx("hide", 3), speed, easing, callback);
+
+ } else {
+ for ( var i = 0, j = this.length; i < j; i++ ) {
+ var display = jQuery.css( this[i], "display" );
+
+ if ( display !== "none" ) {
+ jQuery.data( this[i], "olddisplay", display );
+ }
+ }
+
+ // Set the display of the elements in a second loop
+ // to avoid the constant reflow
+ for ( i = 0; i < j; i++ ) {
+ this[i].style.display = "none";
+ }
+
+ return this;
+ }
+ },
+
+ // Save the old toggle function
+ _toggle: jQuery.fn.toggle,
+
+ toggle: function( fn, fn2, callback ) {
+ var bool = typeof fn === "boolean";
+
+ if ( jQuery.isFunction(fn) && jQuery.isFunction(fn2) ) {
+ this._toggle.apply( this, arguments );
+
+ } else if ( fn == null || bool ) {
+ this.each(function() {
+ var state = bool ? fn : jQuery(this).is(":hidden");
+ jQuery(this)[ state ? "show" : "hide" ]();
+ });
+
+ } else {
+ this.animate(genFx("toggle", 3), fn, fn2, callback);
+ }
+
+ return this;
+ },
+
+ fadeTo: function( speed, to, easing, callback ) {
+ return this.filter(":hidden").css("opacity", 0).show().end()
+ .animate({opacity: to}, speed, easing, callback);
+ },
+
+ animate: function( prop, speed, easing, callback ) {
+ var optall = jQuery.speed(speed, easing, callback);
+
+ if ( jQuery.isEmptyObject( prop ) ) {
+ return this.each( optall.complete );
+ }
+
+ return this[ optall.queue === false ? "each" : "queue" ](function() {
+ // XXX 'this' does not always have a nodeName when running the
+ // test suite
+
+ var opt = jQuery.extend({}, optall), p,
+ isElement = this.nodeType === 1,
+ hidden = isElement && jQuery(this).is(":hidden"),
+ self = this;
+
+ for ( p in prop ) {
+ var name = jQuery.camelCase( p );
+
+ if ( p !== name ) {
+ prop[ name ] = prop[ p ];
+ delete prop[ p ];
+ p = name;
+ }
+
+ if ( prop[p] === "hide" && hidden || prop[p] === "show" && !hidden ) {
+ return opt.complete.call(this);
+ }
+
+ if ( isElement && ( p === "height" || p === "width" ) ) {
+ // Make sure that nothing sneaks out
+ // Record all 3 overflow attributes because IE does not
+ // change the overflow attribute when overflowX and
+ // overflowY are set to the same value
+ opt.overflow = [ this.style.overflow, this.style.overflowX, this.style.overflowY ];
+
+ // Set display property to inline-block for height/width
+ // animations on inline elements that are having width/height
+ // animated
+ if ( jQuery.css( this, "display" ) === "inline" &&
+ jQuery.css( this, "float" ) === "none" ) {
+ if ( !jQuery.support.inlineBlockNeedsLayout ) {
+ this.style.display = "inline-block";
+
+ } else {
+ var display = defaultDisplay(this.nodeName);
+
+ // inline-level elements accept inline-block;
+ // block-level elements need to be inline with layout
+ if ( display === "inline" ) {
+ this.style.display = "inline-block";
+
+ } else {
+ this.style.display = "inline";
+ this.style.zoom = 1;
+ }
+ }
+ }
+ }
+
+ if ( jQuery.isArray( prop[p] ) ) {
+ // Create (if needed) and add to specialEasing
+ (opt.specialEasing = opt.specialEasing || {})[p] = prop[p][1];
+ prop[p] = prop[p][0];
+ }
+ }
+
+ if ( opt.overflow != null ) {
+ this.style.overflow = "hidden";
+ }
+
+ opt.curAnim = jQuery.extend({}, prop);
+
+ jQuery.each( prop, function( name, val ) {
+ var e = new jQuery.fx( self, opt, name );
+
+ if ( rfxtypes.test(val) ) {
+ e[ val === "toggle" ? hidden ? "show" : "hide" : val ]( prop );
+
+ } else {
+ var parts = rfxnum.exec(val),
+ start = e.cur() || 0;
+
+ if ( parts ) {
+ var end = parseFloat( parts[2] ),
+ unit = parts[3] || "px";
+
+ // We need to compute starting value
+ if ( unit !== "px" ) {
+ jQuery.style( self, name, (end || 1) + unit);
+ start = ((end || 1) / e.cur()) * start;
+ jQuery.style( self, name, start + unit);
+ }
+
+ // If a +=/-= token was provided, we're doing a relative animation
+ if ( parts[1] ) {
+ end = ((parts[1] === "-=" ? -1 : 1) * end) + start;
+ }
+
+ e.custom( start, end, unit );
+
+ } else {
+ e.custom( start, val, "" );
+ }
+ }
+ });
+
+ // For JS strict compliance
+ return true;
+ });
+ },
+
+ stop: function( clearQueue, gotoEnd ) {
+ var timers = jQuery.timers;
+
+ if ( clearQueue ) {
+ this.queue([]);
+ }
+
+ this.each(function() {
+ // go in reverse order so anything added to the queue during the loop is ignored
+ for ( var i = timers.length - 1; i >= 0; i-- ) {
+ if ( timers[i].elem === this ) {
+ if (gotoEnd) {
+ // force the next step to be the last
+ timers[i](true);
+ }
+
+ timers.splice(i, 1);
+ }
+ }
+ });
+
+ // start the next in the queue if the last step wasn't forced
+ if ( !gotoEnd ) {
+ this.dequeue();
+ }
+
+ return this;
+ }
+
+});
+
+function genFx( type, num ) {
+ var obj = {};
+
+ jQuery.each( fxAttrs.concat.apply([], fxAttrs.slice(0,num)), function() {
+ obj[ this ] = type;
+ });
+
+ return obj;
+}
+
+// Generate shortcuts for custom animations
+jQuery.each({
+ slideDown: genFx("show", 1),
+ slideUp: genFx("hide", 1),
+ slideToggle: genFx("toggle", 1),
+ fadeIn: { opacity: "show" },
+ fadeOut: { opacity: "hide" },
+ fadeToggle: { opacity: "toggle" }
+}, function( name, props ) {
+ jQuery.fn[ name ] = function( speed, easing, callback ) {
+ return this.animate( props, speed, easing, callback );
+ };
+});
+
+jQuery.extend({
+ speed: function( speed, easing, fn ) {
+ var opt = speed && typeof speed === "object" ? jQuery.extend({}, speed) : {
+ complete: fn || !fn && easing ||
+ jQuery.isFunction( speed ) && speed,
+ duration: speed,
+ easing: fn && easing || easing && !jQuery.isFunction(easing) && easing
+ };
+
+ opt.duration = jQuery.fx.off ? 0 : typeof opt.duration === "number" ? opt.duration :
+ opt.duration in jQuery.fx.speeds ? jQuery.fx.speeds[opt.duration] : jQuery.fx.speeds._default;
+
+ // Queueing
+ opt.old = opt.complete;
+ opt.complete = function() {
+ if ( opt.queue !== false ) {
+ jQuery(this).dequeue();
+ }
+ if ( jQuery.isFunction( opt.old ) ) {
+ opt.old.call( this );
+ }
+ };
+
+ return opt;
+ },
+
+ easing: {
+ linear: function( p, n, firstNum, diff ) {
+ return firstNum + diff * p;
+ },
+ swing: function( p, n, firstNum, diff ) {
+ return ((-Math.cos(p*Math.PI)/2) + 0.5) * diff + firstNum;
+ }
+ },
+
+ timers: [],
+
+ fx: function( elem, options, prop ) {
+ this.options = options;
+ this.elem = elem;
+ this.prop = prop;
+
+ if ( !options.orig ) {
+ options.orig = {};
+ }
+ }
+
+});
+
+jQuery.fx.prototype = {
+ // Simple function for setting a style value
+ update: function() {
+ if ( this.options.step ) {
+ this.options.step.call( this.elem, this.now, this );
+ }
+
+ (jQuery.fx.step[this.prop] || jQuery.fx.step._default)( this );
+ },
+
+ // Get the current size
+ cur: function() {
+ if ( this.elem[this.prop] != null && (!this.elem.style || this.elem.style[this.prop] == null) ) {
+ return this.elem[ this.prop ];
+ }
+
+ var r = parseFloat( jQuery.css( this.elem, this.prop ) );
+ return r && r > -10000 ? r : 0;
+ },
+
+ // Start an animation from one number to another
+ custom: function( from, to, unit ) {
+ var self = this,
+ fx = jQuery.fx;
+
+ this.startTime = jQuery.now();
+ this.start = from;
+ this.end = to;
+ this.unit = unit || this.unit || "px";
+ this.now = this.start;
+ this.pos = this.state = 0;
+
+ function t( gotoEnd ) {
+ return self.step(gotoEnd);
+ }
+
+ t.elem = this.elem;
+
+ if ( t() && jQuery.timers.push(t) && !timerId ) {
+ timerId = setInterval(fx.tick, fx.interval);
+ }
+ },
+
+ // Simple 'show' function
+ show: function() {
+ // Remember where we started, so that we can go back to it later
+ this.options.orig[this.prop] = jQuery.style( this.elem, this.prop );
+ this.options.show = true;
+
+ // Begin the animation
+ // Make sure that we start at a small width/height to avoid any
+ // flash of content
+ this.custom(this.prop === "width" || this.prop === "height" ? 1 : 0, this.cur());
+
+ // Start by showing the element
+ jQuery( this.elem ).show();
+ },
+
+ // Simple 'hide' function
+ hide: function() {
+ // Remember where we started, so that we can go back to it later
+ this.options.orig[this.prop] = jQuery.style( this.elem, this.prop );
+ this.options.hide = true;
+
+ // Begin the animation
+ this.custom(this.cur(), 0);
+ },
+
+ // Each step of an animation
+ step: function( gotoEnd ) {
+ var t = jQuery.now(), done = true;
+
+ if ( gotoEnd || t >= this.options.duration + this.startTime ) {
+ this.now = this.end;
+ this.pos = this.state = 1;
+ this.update();
+
+ this.options.curAnim[ this.prop ] = true;
+
+ for ( var i in this.options.curAnim ) {
+ if ( this.options.curAnim[i] !== true ) {
+ done = false;
+ }
+ }
+
+ if ( done ) {
+ // Reset the overflow
+ if ( this.options.overflow != null && !jQuery.support.shrinkWrapBlocks ) {
+ var elem = this.elem,
+ options = this.options;
+
+ jQuery.each( [ "", "X", "Y" ], function (index, value) {
+ elem.style[ "overflow" + value ] = options.overflow[index];
+ } );
+ }
+
+ // Hide the element if the "hide" operation was done
+ if ( this.options.hide ) {
+ jQuery(this.elem).hide();
+ }
+
+ // Reset the properties, if the item has been hidden or shown
+ if ( this.options.hide || this.options.show ) {
+ for ( var p in this.options.curAnim ) {
+ jQuery.style( this.elem, p, this.options.orig[p] );
+ }
+ }
+
+ // Execute the complete function
+ this.options.complete.call( this.elem );
+ }
+
+ return false;
+
+ } else {
+ var n = t - this.startTime;
+ this.state = n / this.options.duration;
+
+ // Perform the easing function, defaults to swing
+ var specialEasing = this.options.specialEasing && this.options.specialEasing[this.prop];
+ var defaultEasing = this.options.easing || (jQuery.easing.swing ? "swing" : "linear");
+ this.pos = jQuery.easing[specialEasing || defaultEasing](this.state, n, 0, 1, this.options.duration);
+ this.now = this.start + ((this.end - this.start) * this.pos);
+
+ // Perform the next step of the animation
+ this.update();
+ }
+
+ return true;
+ }
+};
+
+jQuery.extend( jQuery.fx, {
+ tick: function() {
+ var timers = jQuery.timers;
+
+ for ( var i = 0; i < timers.length; i++ ) {
+ if ( !timers[i]() ) {
+ timers.splice(i--, 1);
+ }
+ }
+
+ if ( !timers.length ) {
+ jQuery.fx.stop();
+ }
+ },
+
+ interval: 13,
+
+ stop: function() {
+ clearInterval( timerId );
+ timerId = null;
+ },
+
+ speeds: {
+ slow: 600,
+ fast: 200,
+ // Default speed
+ _default: 400
+ },
+
+ step: {
+ opacity: function( fx ) {
+ jQuery.style( fx.elem, "opacity", fx.now );
+ },
+
+ _default: function( fx ) {
+ if ( fx.elem.style && fx.elem.style[ fx.prop ] != null ) {
+ fx.elem.style[ fx.prop ] = (fx.prop === "width" || fx.prop === "height" ? Math.max(0, fx.now) : fx.now) + fx.unit;
+ } else {
+ fx.elem[ fx.prop ] = fx.now;
+ }
+ }
+ }
+});
+
+if ( jQuery.expr && jQuery.expr.filters ) {
+ jQuery.expr.filters.animated = function( elem ) {
+ return jQuery.grep(jQuery.timers, function( fn ) {
+ return elem === fn.elem;
+ }).length;
+ };
+}
+
+function defaultDisplay( nodeName ) {
+ if ( !elemdisplay[ nodeName ] ) {
+ var elem = jQuery("<" + nodeName + ">").appendTo("body"),
+ display = elem.css("display");
+
+ elem.remove();
+
+ if ( display === "none" || display === "" ) {
+ display = "block";
+ }
+
+ elemdisplay[ nodeName ] = display;
+ }
+
+ return elemdisplay[ nodeName ];
+}
+
+
+
+
+var rtable = /^t(?:able|d|h)$/i,
+ rroot = /^(?:body|html)$/i;
+
+if ( "getBoundingClientRect" in document.documentElement ) {
+ jQuery.fn.offset = function( options ) {
+ var elem = this[0], box;
+
+ if ( options ) {
+ return this.each(function( i ) {
+ jQuery.offset.setOffset( this, options, i );
+ });
+ }
+
+ if ( !elem || !elem.ownerDocument ) {
+ return null;
+ }
+
+ if ( elem === elem.ownerDocument.body ) {
+ return jQuery.offset.bodyOffset( elem );
+ }
+
+ try {
+ box = elem.getBoundingClientRect();
+ } catch(e) {}
+
+ var doc = elem.ownerDocument,
+ docElem = doc.documentElement;
+
+ // Make sure we're not dealing with a disconnected DOM node
+ if ( !box || !jQuery.contains( docElem, elem ) ) {
+ return box || { top: 0, left: 0 };
+ }
+
+ var body = doc.body,
+ win = getWindow(doc),
+ clientTop = docElem.clientTop || body.clientTop || 0,
+ clientLeft = docElem.clientLeft || body.clientLeft || 0,
+ scrollTop = (win.pageYOffset || jQuery.support.boxModel && docElem.scrollTop || body.scrollTop ),
+ scrollLeft = (win.pageXOffset || jQuery.support.boxModel && docElem.scrollLeft || body.scrollLeft),
+ top = box.top + scrollTop - clientTop,
+ left = box.left + scrollLeft - clientLeft;
+
+ return { top: top, left: left };
+ };
+
+} else {
+ jQuery.fn.offset = function( options ) {
+ var elem = this[0];
+
+ if ( options ) {
+ return this.each(function( i ) {
+ jQuery.offset.setOffset( this, options, i );
+ });
+ }
+
+ if ( !elem || !elem.ownerDocument ) {
+ return null;
+ }
+
+ if ( elem === elem.ownerDocument.body ) {
+ return jQuery.offset.bodyOffset( elem );
+ }
+
+ jQuery.offset.initialize();
+
+ var computedStyle,
+ offsetParent = elem.offsetParent,
+ prevOffsetParent = elem,
+ doc = elem.ownerDocument,
+ docElem = doc.documentElement,
+ body = doc.body,
+ defaultView = doc.defaultView,
+ prevComputedStyle = defaultView ? defaultView.getComputedStyle( elem, null ) : elem.currentStyle,
+ top = elem.offsetTop,
+ left = elem.offsetLeft;
+
+ while ( (elem = elem.parentNode) && elem !== body && elem !== docElem ) {
+ if ( jQuery.offset.supportsFixedPosition && prevComputedStyle.position === "fixed" ) {
+ break;
+ }
+
+ computedStyle = defaultView ? defaultView.getComputedStyle(elem, null) : elem.currentStyle;
+ top -= elem.scrollTop;
+ left -= elem.scrollLeft;
+
+ if ( elem === offsetParent ) {
+ top += elem.offsetTop;
+ left += elem.offsetLeft;
+
+ if ( jQuery.offset.doesNotAddBorder && !(jQuery.offset.doesAddBorderForTableAndCells && rtable.test(elem.nodeName)) ) {
+ top += parseFloat( computedStyle.borderTopWidth ) || 0;
+ left += parseFloat( computedStyle.borderLeftWidth ) || 0;
+ }
+
+ prevOffsetParent = offsetParent;
+ offsetParent = elem.offsetParent;
+ }
+
+ if ( jQuery.offset.subtractsBorderForOverflowNotVisible && computedStyle.overflow !== "visible" ) {
+ top += parseFloat( computedStyle.borderTopWidth ) || 0;
+ left += parseFloat( computedStyle.borderLeftWidth ) || 0;
+ }
+
+ prevComputedStyle = computedStyle;
+ }
+
+ if ( prevComputedStyle.position === "relative" || prevComputedStyle.position === "static" ) {
+ top += body.offsetTop;
+ left += body.offsetLeft;
+ }
+
+ if ( jQuery.offset.supportsFixedPosition && prevComputedStyle.position === "fixed" ) {
+ top += Math.max( docElem.scrollTop, body.scrollTop );
+ left += Math.max( docElem.scrollLeft, body.scrollLeft );
+ }
+
+ return { top: top, left: left };
+ };
+}
+
+jQuery.offset = {
+ initialize: function() {
+ var body = document.body, container = document.createElement("div"), innerDiv, checkDiv, table, td, bodyMarginTop = parseFloat( jQuery.css(body, "marginTop") ) || 0,
+ html = "<div style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;'><div></div></div><table style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;' cellpadding='0' cellspacing='0'><tr><td></td></tr></table>";
+
+ jQuery.extend( container.style, { position: "absolute", top: 0, left: 0, margin: 0, border: 0, width: "1px", height: "1px", visibility: "hidden" } );
+
+ container.innerHTML = html;
+ body.insertBefore( container, body.firstChild );
+ innerDiv = container.firstChild;
+ checkDiv = innerDiv.firstChild;
+ td = innerDiv.nextSibling.firstChild.firstChild;
+
+ this.doesNotAddBorder = (checkDiv.offsetTop !== 5);
+ this.doesAddBorderForTableAndCells = (td.offsetTop === 5);
+
+ checkDiv.style.position = "fixed";
+ checkDiv.style.top = "20px";
+
+ // safari subtracts parent border width here which is 5px
+ this.supportsFixedPosition = (checkDiv.offsetTop === 20 || checkDiv.offsetTop === 15);
+ checkDiv.style.position = checkDiv.style.top = "";
+
+ innerDiv.style.overflow = "hidden";
+ innerDiv.style.position = "relative";
+
+ this.subtractsBorderForOverflowNotVisible = (checkDiv.offsetTop === -5);
+
+ this.doesNotIncludeMarginInBodyOffset = (body.offsetTop !== bodyMarginTop);
+
+ body.removeChild( container );
+ body = container = innerDiv = checkDiv = table = td = null;
+ jQuery.offset.initialize = jQuery.noop;
+ },
+
+ bodyOffset: function( body ) {
+ var top = body.offsetTop,
+ left = body.offsetLeft;
+
+ jQuery.offset.initialize();
+
+ if ( jQuery.offset.doesNotIncludeMarginInBodyOffset ) {
+ top += parseFloat( jQuery.css(body, "marginTop") ) || 0;
+ left += parseFloat( jQuery.css(body, "marginLeft") ) || 0;
+ }
+
+ return { top: top, left: left };
+ },
+
+ setOffset: function( elem, options, i ) {
+ var position = jQuery.css( elem, "position" );
+
+ // set position first, in-case top/left are set even on static elem
+ if ( position === "static" ) {
+ elem.style.position = "relative";
+ }
+
+ var curElem = jQuery( elem ),
+ curOffset = curElem.offset(),
+ curCSSTop = jQuery.css( elem, "top" ),
+ curCSSLeft = jQuery.css( elem, "left" ),
+ calculatePosition = (position === "absolute" && jQuery.inArray('auto', [curCSSTop, curCSSLeft]) > -1),
+ props = {}, curPosition = {}, curTop, curLeft;
+
+ // need to be able to calculate position if either top or left is auto and position is absolute
+ if ( calculatePosition ) {
+ curPosition = curElem.position();
+ }
+
+ curTop = calculatePosition ? curPosition.top : parseInt( curCSSTop, 10 ) || 0;
+ curLeft = calculatePosition ? curPosition.left : parseInt( curCSSLeft, 10 ) || 0;
+
+ if ( jQuery.isFunction( options ) ) {
+ options = options.call( elem, i, curOffset );
+ }
+
+ if (options.top != null) {
+ props.top = (options.top - curOffset.top) + curTop;
+ }
+ if (options.left != null) {
+ props.left = (options.left - curOffset.left) + curLeft;
+ }
+
+ if ( "using" in options ) {
+ options.using.call( elem, props );
+ } else {
+ curElem.css( props );
+ }
+ }
+};
+
+
+jQuery.fn.extend({
+ position: function() {
+ if ( !this[0] ) {
+ return null;
+ }
+
+ var elem = this[0],
+
+ // Get *real* offsetParent
+ offsetParent = this.offsetParent(),
+
+ // Get correct offsets
+ offset = this.offset(),
+ parentOffset = rroot.test(offsetParent[0].nodeName) ? { top: 0, left: 0 } : offsetParent.offset();
+
+ // Subtract element margins
+ // note: when an element has margin: auto the offsetLeft and marginLeft
+ // are the same in Safari causing offset.left to incorrectly be 0
+ offset.top -= parseFloat( jQuery.css(elem, "marginTop") ) || 0;
+ offset.left -= parseFloat( jQuery.css(elem, "marginLeft") ) || 0;
+
+ // Add offsetParent borders
+ parentOffset.top += parseFloat( jQuery.css(offsetParent[0], "borderTopWidth") ) || 0;
+ parentOffset.left += parseFloat( jQuery.css(offsetParent[0], "borderLeftWidth") ) || 0;
+
+ // Subtract the two offsets
+ return {
+ top: offset.top - parentOffset.top,
+ left: offset.left - parentOffset.left
+ };
+ },
+
+ offsetParent: function() {
+ return this.map(function() {
+ var offsetParent = this.offsetParent || document.body;
+ while ( offsetParent && (!rroot.test(offsetParent.nodeName) && jQuery.css(offsetParent, "position") === "static") ) {
+ offsetParent = offsetParent.offsetParent;
+ }
+ return offsetParent;
+ });
+ }
+});
+
+
+// Create scrollLeft and scrollTop methods
+jQuery.each( ["Left", "Top"], function( i, name ) {
+ var method = "scroll" + name;
+
+ jQuery.fn[ method ] = function(val) {
+ var elem = this[0], win;
+
+ if ( !elem ) {
+ return null;
+ }
+
+ if ( val !== undefined ) {
+ // Set the scroll offset
+ return this.each(function() {
+ win = getWindow( this );
+
+ if ( win ) {
+ win.scrollTo(
+ !i ? val : jQuery(win).scrollLeft(),
+ i ? val : jQuery(win).scrollTop()
+ );
+
+ } else {
+ this[ method ] = val;
+ }
+ });
+ } else {
+ win = getWindow( elem );
+
+ // Return the scroll offset
+ return win ? ("pageXOffset" in win) ? win[ i ? "pageYOffset" : "pageXOffset" ] :
+ jQuery.support.boxModel && win.document.documentElement[ method ] ||
+ win.document.body[ method ] :
+ elem[ method ];
+ }
+ };
+});
+
+function getWindow( elem ) {
+ return jQuery.isWindow( elem ) ?
+ elem :
+ elem.nodeType === 9 ?
+ elem.defaultView || elem.parentWindow :
+ false;
+}
+
+
+
+
+// Create innerHeight, innerWidth, outerHeight and outerWidth methods
+jQuery.each([ "Height", "Width" ], function( i, name ) {
+
+ var type = name.toLowerCase();
+
+ // innerHeight and innerWidth
+ jQuery.fn["inner" + name] = function() {
+ return this[0] ?
+ parseFloat( jQuery.css( this[0], type, "padding" ) ) :
+ null;
+ };
+
+ // outerHeight and outerWidth
+ jQuery.fn["outer" + name] = function( margin ) {
+ return this[0] ?
+ parseFloat( jQuery.css( this[0], type, margin ? "margin" : "border" ) ) :
+ null;
+ };
+
+ jQuery.fn[ type ] = function( size ) {
+ // Get window width or height
+ var elem = this[0];
+ if ( !elem ) {
+ return size == null ? null : this;
+ }
+
+ if ( jQuery.isFunction( size ) ) {
+ return this.each(function( i ) {
+ var self = jQuery( this );
+ self[ type ]( size.call( this, i, self[ type ]() ) );
+ });
+ }
+
+ if ( jQuery.isWindow( elem ) ) {
+ // Everyone else use document.documentElement or document.body depending on Quirks vs Standards mode
+ return elem.document.compatMode === "CSS1Compat" && elem.document.documentElement[ "client" + name ] ||
+ elem.document.body[ "client" + name ];
+
+ // Get document width or height
+ } else if ( elem.nodeType === 9 ) {
+ // Either scroll[Width/Height] or offset[Width/Height], whichever is greater
+ return Math.max(
+ elem.documentElement["client" + name],
+ elem.body["scroll" + name], elem.documentElement["scroll" + name],
+ elem.body["offset" + name], elem.documentElement["offset" + name]
+ );
+
+ // Get or set width or height on the element
+ } else if ( size === undefined ) {
+ var orig = jQuery.css( elem, type ),
+ ret = parseFloat( orig );
+
+ return jQuery.isNaN( ret ) ? orig : ret;
+
+ // Set the width or height on the element (default to pixels if value is unitless)
+ } else {
+ return this.css( type, typeof size === "string" ? size : size + "px" );
+ }
+ };
+
+});
+
+
+})(window);
diff --git a/y2015/http_status/www/lib/reconnecting-websocket.min.js b/y2015/http_status/www/lib/reconnecting-websocket.min.js
new file mode 100644
index 0000000..f40b6c7
--- /dev/null
+++ b/y2015/http_status/www/lib/reconnecting-websocket.min.js
@@ -0,0 +1,96 @@
+// MIT License:
+//
+// Copyright (c) 2010-2012, Joe Walnes
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+/**
+ * This behaves like a WebSocket in every way, except if it fails to connect,
+ * or it gets disconnected, it will repeatedly poll until it successfully connects
+ * again.
+ *
+ * It is API compatible, so when you have:
+ * ws = new WebSocket('ws://....');
+ * you can replace with:
+ * ws = new ReconnectingWebSocket('ws://....');
+ *
+ * The event stream will typically look like:
+ * onconnecting
+ * onopen
+ * onmessage
+ * onmessage
+ * onclose // lost connection
+ * onconnecting
+ * onopen // sometime later...
+ * onmessage
+ * onmessage
+ * etc...
+ *
+ * It is API compatible with the standard WebSocket API, apart from the following members:
+ *
+ * - `bufferedAmount`
+ * - `extensions`
+ * - `binaryType`
+ *
+ * Latest version: https://github.com/joewalnes/reconnecting-websocket/
+ * - Joe Walnes
+ *
+ * Syntax
+ * ======
+ * var socket = new ReconnectingWebSocket(url, protocols, options);
+ *
+ * Parameters
+ * ==========
+ * url - The url you are connecting to.
+ * protocols - Optional string or array of protocols.
+ * options - See below
+ *
+ * Options
+ * =======
+ * Options can either be passed upon instantiation or set after instantiation:
+ *
+ * var socket = new ReconnectingWebSocket(url, null, { debug: true, reconnectInterval: 4000 });
+ *
+ * or
+ *
+ * var socket = new ReconnectingWebSocket(url);
+ * socket.debug = true;
+ * socket.reconnectInterval = 4000;
+ *
+ * debug
+ * - Whether this instance should log debug messages. Accepts true or false. Default: false.
+ *
+ * automaticOpen
+ * - Whether or not the websocket should attempt to connect immediately upon instantiation. The socket can be manually opened or closed at any time using ws.open() and ws.close().
+ *
+ * reconnectInterval
+ * - The number of milliseconds to delay before attempting to reconnect. Accepts integer. Default: 1000.
+ *
+ * maxReconnectInterval
+ * - The maximum number of milliseconds to delay a reconnection attempt. Accepts integer. Default: 30000.
+ *
+ * reconnectDecay
+ * - The rate of increase of the reconnect delay. Allows reconnect attempts to back off when problems persist. Accepts integer or float. Default: 1.5.
+ *
+ * timeoutInterval
+ * - The maximum time in milliseconds to wait for a connection to succeed before closing and retrying. Accepts integer. Default: 2000.
+ *
+ */
+
+!function(a,b){"function"==typeof define&&define.amd?define([],b):"undefined"!=typeof module&&module.exports?module.exports=b():a.ReconnectingWebSocket=b()}(this,function(){function a(b,c,d){function l(a,b){var c=document.createEvent("CustomEvent");return c.initCustomEvent(a,!1,!1,b),c}var e={debug:!1,automaticOpen:!0,reconnectInterval:1e3,maxReconnectInterval:3e4,reconnectDecay:1.5,timeoutInterval:2e3};d||(d={});for(var f in e)this[f]="undefined"!=typeof d[f]?d[f]:e[f];this.url=b,this.reconnectAttempts=0,this.readyState=WebSocket.CONNECTING,this.protocol=null;var h,g=this,i=!1,j=!1,k=document.createElement("div");k.addEventListener("open",function(a){g.onopen(a)}),k.addEventListener("close",function(a){g.onclose(a)}),k.addEventListener("connecting",function(a){g.onconnecting(a)}),k.addEventListener("message",function(a){g.onmessage(a)}),k.addEventListener("error",function(a){g.onerror(a)}),this.addEventListener=k.addEventListener.bind(k),this.removeEventListener=k.removeEventListener.bind(k),this.dispatchEvent=k.dispatchEvent.bind(k),this.open=function(b){h=new WebSocket(g.url,c||[]),b||k.dispatchEvent(l("connecting")),(g.debug||a.debugAll)&&console.debug("ReconnectingWebSocket","attempt-connect",g.url);var d=h,e=setTimeout(function(){(g.debug||a.debugAll)&&console.debug("ReconnectingWebSocket","connection-timeout",g.url),j=!0,d.close(),j=!1},g.timeoutInterval);h.onopen=function(){clearTimeout(e),(g.debug||a.debugAll)&&console.debug("ReconnectingWebSocket","onopen",g.url),g.protocol=h.protocol,g.readyState=WebSocket.OPEN,g.reconnectAttempts=0;var d=l("open");d.isReconnect=b,b=!1,k.dispatchEvent(d)},h.onclose=function(c){if(clearTimeout(e),h=null,i)g.readyState=WebSocket.CLOSED,k.dispatchEvent(l("close"));else{g.readyState=WebSocket.CONNECTING;var d=l("connecting");d.code=c.code,d.reason=c.reason,d.wasClean=c.wasClean,k.dispatchEvent(d),b||j||((g.debug||a.debugAll)&&console.debug("ReconnectingWebSocket","onclose",g.url),k.dispatchEvent(l("close")));var e=g.reconnectInterval*Math.pow(g.reconnectDecay,g.reconnectAttempts);setTimeout(function(){g.reconnectAttempts++,g.open(!0)},e>g.maxReconnectInterval?g.maxReconnectInterval:e)}},h.onmessage=function(b){(g.debug||a.debugAll)&&console.debug("ReconnectingWebSocket","onmessage",g.url,b.data);var c=l("message");c.data=b.data,k.dispatchEvent(c)},h.onerror=function(b){(g.debug||a.debugAll)&&console.debug("ReconnectingWebSocket","onerror",g.url,b),k.dispatchEvent(l("error"))}},1==this.automaticOpen&&this.open(!1),this.send=function(b){if(h)return(g.debug||a.debugAll)&&console.debug("ReconnectingWebSocket","send",g.url,b),h.send(b);throw"INVALID_STATE_ERR : Pausing to reconnect websocket"},this.close=function(a,b){"undefined"==typeof a&&(a=1e3),i=!0,h&&h.close(a,b)},this.refresh=function(){h&&h.close()}}return a.prototype.onopen=function(){},a.prototype.onclose=function(){},a.prototype.onconnecting=function(){},a.prototype.onmessage=function(){},a.prototype.onerror=function(){},a.debugAll=!1,a.CONNECTING=WebSocket.CONNECTING,a.OPEN=WebSocket.OPEN,a.CLOSING=WebSocket.CLOSING,a.CLOSED=WebSocket.CLOSED,a});
diff --git a/y2015/http_status/www_defaults/_404.png b/y2015/http_status/www_defaults/_404.png
new file mode 100644
index 0000000..8a43cb8
--- /dev/null
+++ b/y2015/http_status/www_defaults/_404.png
Binary files differ
diff --git a/y2015/http_status/www_defaults/_error.css b/y2015/http_status/www_defaults/_error.css
new file mode 100644
index 0000000..565d0a4
--- /dev/null
+++ b/y2015/http_status/www_defaults/_error.css
@@ -0,0 +1,60 @@
+/*
+From Seasocks version 1.1.2, under /src/main/web
+
+Copyright (c) 2013, Matt Godbolt
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+Redistributions of source code must retain the above copyright notice, this
+list of conditions and the following disclaimer.
+Redistributions in binary form must reproduce the above copyright notice, this
+list of conditions and the following disclaimer in the documentation and/or
+other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+body {
+ font-family: segoe ui, tahoma, arial, sans-serif;
+ color: #ffffff;
+ background-color: #c21e29;
+ text-align: center;
+}
+
+a {
+ color: #ffff00;
+}
+
+.footer {
+ font-style: italic;
+}
+
+.message {
+ display: inline-block;
+ border: 1px solid white;
+ padding: 50px;
+ font-size: 20px;
+}
+
+.headline {
+ padding: 50px;
+ font-weight: bold;
+ font-size: 32px;
+}
+
+.footer {
+ padding-top: 50px;
+ font-size: 12px;
+}
+
diff --git a/y2015/http_status/www_defaults/_error.html b/y2015/http_status/www_defaults/_error.html
new file mode 100644
index 0000000..e3d422d
--- /dev/null
+++ b/y2015/http_status/www_defaults/_error.html
@@ -0,0 +1,42 @@
+<!--
+From Seasocks version 1.1.2, under /src/main/web
+
+Copyright (c) 2013, Matt Godbolt
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+Redistributions of source code must retain the above copyright notice, this
+list of conditions and the following disclaimer.
+Redistributions in binary form must reproduce the above copyright notice, this
+list of conditions and the following disclaimer in the documentation and/or
+other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<html DOCTYPE=html>
+<head>
+ <title>%%ERRORCODE%% - %%MESSAGE%% - Keep Calm And Carry On!</title>
+ <link href="/_error.css" rel="stylesheet">
+</head>
+<body>
+ <div class="message">
+ <img src="/_404.png" height="200" width="107">
+ <div class="headline">HTTP_STATUS: %%ERRORCODE%% — %%MESSAGE%%</div>
+ <div class="info">%%BODY%%</div>
+ </div>
+
+ <div class="footer">http_status developed by 971 Spartan Robotics. Server powered by <a href="https://github.com/mattgodbolt/seasocks">SeaSocks</a>. Contact Comran Morshed(comranmorsh@gmail.com) if this 404 error should not be happening...</div>
+</body>
+</html>
diff --git a/y2015/http_status/www_defaults/_seasocks.css b/y2015/http_status/www_defaults/_seasocks.css
new file mode 100644
index 0000000..5f0b655
--- /dev/null
+++ b/y2015/http_status/www_defaults/_seasocks.css
@@ -0,0 +1,49 @@
+/*
+From Seasocks version 1.1.2, under /src/main/web
+
+Copyright (c) 2013, Matt Godbolt
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+Redistributions of source code must retain the above copyright notice, this
+list of conditions and the following disclaimer.
+Redistributions in binary form must reproduce the above copyright notice, this
+list of conditions and the following disclaimer in the documentation and/or
+other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+body {
+ font-family: segoe ui, tahoma, arial, sans-serif;
+ font-size: 12px;
+ color: #ffffff;
+ background-color: #333333;
+ margin: 0;
+}
+
+a {
+ color: #ffff00;
+}
+
+table {
+ border-collapse: collapse;
+ width: 100%;
+ text-align: center;
+}
+
+.template {
+ display: none;
+}
+
diff --git a/y2015/http_status/www_defaults/_stats.html b/y2015/http_status/www_defaults/_stats.html
new file mode 100644
index 0000000..2390334
--- /dev/null
+++ b/y2015/http_status/www_defaults/_stats.html
@@ -0,0 +1,88 @@
+<!--
+From Seasocks version 1.1.2, under /src/main/web
+
+Copyright (c) 2013, Matt Godbolt
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+Redistributions of source code must retain the above copyright notice, this
+list of conditions and the following disclaimer.
+Redistributions in binary form must reproduce the above copyright notice, this
+list of conditions and the following disclaimer in the documentation and/or
+other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<html DOCTYPE=html>
+<head>
+ <title>SeaSocks Stats</title>
+ <link href="/_seasocks.css" rel="stylesheet">
+ <script src="/_jquery.min.js" type="text/javascript"></script>
+ <script>
+ function clear() {
+ $('#cx tbody tr:visible').remove();
+ }
+ function connection(stats) {
+ c = $('#cx .template').clone().removeClass('template').appendTo('#cx');
+ for (stat in stats) {
+ c.find('.' + stat).text(stats[stat]);
+ }
+ }
+ function refresh() {
+ var stats = new XMLHttpRequest();
+ stats.open("GET", "/_livestats.js", false);
+ stats.send(null);
+ eval(stats.responseText);
+ }
+ $(function() {
+ setInterval(refresh, 1000);
+ refresh();
+ });
+ </script>
+</head>
+<body><h1>SeaSocks Stats</h1></body>
+
+<h2>Connections</h2>
+<table id="cx">
+ <thead>
+ <tr>
+ <th>Connection time</th>
+ <th>Fd</th>
+ <th>Addr</th>
+ <th>URI</th>
+ <th>Username</th>
+ <th>Pending read</th>
+ <th>Bytes read</th>
+ <th>Pending send</th>
+ <th>Bytes sent</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr class="template">
+ <td class="since"></td>
+ <td class="fd"></td>
+ <td class="addr"></td>
+ <td class="uri"></td>
+ <td class="user"></td>
+ <td class="input"></td>
+ <td class="read"></td>
+ <td class="output"></td>
+ <td class="written"></td>
+ </tr>
+ </tbody>
+</table>
+
+</body>
+</html>
diff --git a/y2015/http_status/www_defaults/favicon.ico b/y2015/http_status/www_defaults/favicon.ico
new file mode 100644
index 0000000..30a95b9
--- /dev/null
+++ b/y2015/http_status/www_defaults/favicon.ico
Binary files differ
diff --git a/y2015/joystick_reader.cc b/y2015/joystick_reader.cc
new file mode 100644
index 0000000..01b5431
--- /dev/null
+++ b/y2015/joystick_reader.cc
@@ -0,0 +1,549 @@
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <math.h>
+
+#include "aos/linux_code/init.h"
+#include "aos/prime/input/joystick_input.h"
+#include "aos/common/input/driver_station_data.h"
+#include "aos/common/logging/logging.h"
+#include "aos/common/util/log_interval.h"
+#include "aos/common/time.h"
+#include "aos/common/actions/actions.h"
+
+#include "y2015/control_loops/claw/claw.q.h"
+#include "y2015/control_loops/drivetrain/drivetrain.q.h"
+#include "y2015/control_loops/fridge/fridge.q.h"
+#include "y2015/constants.h"
+#include "frc971/queues/gyro.q.h"
+#include "frc971/autonomous/auto.q.h"
+#include "y2015/actors/pickup_actor.h"
+#include "y2015/actors/stack_actor.h"
+#include "y2015/actors/score_actor.h"
+#include "y2015/actors/stack_and_lift_actor.h"
+#include "y2015/actors/stack_and_hold_actor.h"
+#include "y2015/actors/held_to_lift_actor.h"
+#include "y2015/actors/lift_actor.h"
+#include "y2015/actors/can_pickup_actor.h"
+#include "y2015/actors/horizontal_can_pickup_actor.h"
+
+using ::frc971::control_loops::claw_queue;
+using ::frc971::control_loops::drivetrain_queue;
+using ::frc971::control_loops::fridge_queue;
+using ::frc971::sensors::gyro_reading;
+
+using ::aos::input::driver_station::ButtonLocation;
+using ::aos::input::driver_station::POVLocation;
+using ::aos::input::driver_station::JoystickAxis;
+using ::aos::input::driver_station::ControlBit;
+
+namespace frc971 {
+namespace input {
+namespace joysticks {
+
+// Actions needed.
+
+// Human Player Station Intake Button
+// Claw open
+// Claw close
+// Claw down
+
+// Stack + Lift (together)
+// Place
+
+// Hold stack
+
+// Horizontal can pickup
+// Vertical can pickup
+
+// TODO(austin): Pull a lot of the constants below out up here so they can be
+// globally changed easier.
+
+// preset motion limits
+constexpr actors::ProfileParams kArmMove{0.5, 1.0};
+constexpr actors::ProfileParams kElevatorMove{0.3, 1.0};
+
+const JoystickAxis kSteeringWheel(1, 1), kDriveThrottle(2, 2);
+const ButtonLocation kShiftHigh(2, 1), kShiftLow(2, 3);
+const ButtonLocation kQuickTurn(1, 5);
+
+//const ButtonLocation kClawClosed(3, 5);
+//const ButtonLocation kFridgeClosed(3, 1);
+
+
+const ButtonLocation kStackAndHold(3, 5);
+const ButtonLocation kRollersIn(3, 2);
+const ButtonLocation kHorizontalCanRollersIn(4, 5);
+const ButtonLocation kClawToggle(4, 1);
+
+const POVLocation kElevatorCanUp(4, 0);
+
+// POV stick 3, 180
+const POVLocation kCanPickup(4, 180);
+const ButtonLocation kToteChute(4, 6);
+const ButtonLocation kStackAndLift(4, 7);
+
+// Pull in the 6th tote.
+//const ButtonLocation kSixthTote(4, 10);
+const ButtonLocation kCanUp(4, 10);
+
+const ButtonLocation kHeldToLift(4, 11);
+const ButtonLocation kPickup(4, 9);
+
+const ButtonLocation kStack(4, 2);
+
+// Move the fridge out with the stack in preparation for scoring.
+const ButtonLocation kScore(4, 8);
+const ButtonLocation kCoopTop(3, 8);
+const ButtonLocation kCoopTopRetract(3, 7);
+const ButtonLocation kCoopBottom(3, 6);
+const ButtonLocation kCoopBottomRetract(4, 12);
+
+const ButtonLocation kCanReset(3, 9);
+
+const POVLocation kFridgeToggle(4, 270);
+const ButtonLocation kSpit(4, 3);
+
+// Set stack down in the bot.
+const POVLocation kSetStackDownAndHold(4, 90);
+
+const double kClawTotePackAngle = 0.90;
+const double kArmRaiseLowerClearance = -0.08;
+const double kClawStackClearance = 0.55;
+const double kHorizontalCanClawAngle = 0.12;
+
+const double kStackUpHeight = 0.60;
+const double kStackUpArm = 0.0;
+
+class Reader : public ::aos::input::JoystickInput {
+ public:
+ Reader() : was_running_(false) {}
+
+ static actors::ScoreParams MakeScoreParams() {
+ actors::ScoreParams r;
+ r.move_the_stack = r.place_the_stack = true;
+ r.upper_move_height = 0.14;
+ r.begin_horizontal_move_height = 0.13;
+ r.horizontal_move_target = -0.7;
+ r.horizontal_start_lowering = -0.65;
+ r.home_lift_horizontal_start_position = -0.60;
+ r.place_height = -0.10;
+ r.home_return_height = 0.05;
+ return r;
+ }
+
+ static actors::ScoreParams MakeCoopTopParams(bool place_the_stack) {
+ actors::ScoreParams r;
+ r.move_the_stack = !place_the_stack;
+ r.place_the_stack = place_the_stack;
+ r.upper_move_height = 0.52;
+ r.begin_horizontal_move_height = 0.5;
+ r.horizontal_move_target = -0.48;
+ r.horizontal_start_lowering = r.horizontal_move_target;
+ r.home_lift_horizontal_start_position = -0.3;
+ r.place_height = 0.35;
+ r.home_return_height = 0.1;
+ return r;
+ }
+
+ static actors::ScoreParams MakeCoopBottomParams(bool place_the_stack) {
+ actors::ScoreParams r;
+ r.move_the_stack = !place_the_stack;
+ r.place_the_stack = place_the_stack;
+ r.upper_move_height = 0.17;
+ r.begin_horizontal_move_height = 0.16;
+ r.horizontal_move_target = -0.7;
+ r.horizontal_start_lowering = r.horizontal_move_target;
+ r.home_lift_horizontal_start_position = -0.3;
+ r.place_height = 0.0;
+ r.home_return_height = 0.1;
+ return r;
+ }
+
+ virtual void RunIteration(const ::aos::input::driver_station::Data &data) {
+ bool last_auto_running = auto_running_;
+ auto_running_ = data.GetControlBit(ControlBit::kAutonomous) &&
+ data.GetControlBit(ControlBit::kEnabled);
+ if (auto_running_ != last_auto_running) {
+ if (auto_running_) {
+ StartAuto();
+ } else {
+ StopAuto();
+ }
+ }
+
+ if (!data.GetControlBit(ControlBit::kAutonomous)) {
+ HandleDrivetrain(data);
+ HandleTeleop(data);
+ }
+ }
+
+ void HandleDrivetrain(const ::aos::input::driver_station::Data &data) {
+ const double wheel = -data.GetAxis(kSteeringWheel);
+ const double throttle = -data.GetAxis(kDriveThrottle);
+
+ if (!drivetrain_queue.goal.MakeWithBuilder()
+ .steering(wheel)
+ .throttle(throttle)
+ .quickturn(data.IsPressed(kQuickTurn))
+ .control_loop_driving(false)
+ .Send()) {
+ LOG(WARNING, "sending stick values failed\n");
+ }
+ }
+
+ void HandleTeleop(const ::aos::input::driver_station::Data &data) {
+ double intake_power = 0.0;
+ if (!data.GetControlBit(ControlBit::kEnabled)) {
+ action_queue_.CancelAllActions();
+ LOG(DEBUG, "Canceling\n");
+ }
+
+ if (data.IsPressed(kRollersIn)) {
+ intake_power = 10.0;
+ claw_goal_ = 0.0;
+ }
+ if (data.IsPressed(kHorizontalCanRollersIn)) {
+ intake_power = 10.0;
+ claw_goal_ = kHorizontalCanClawAngle;
+ }
+ if (data.IsPressed(kSpit)) {
+ intake_power = -12.0;
+ action_queue_.CancelAllActions();
+ }
+
+ // Toggle button for the claw
+ if (data.PosEdge(kClawToggle)) {
+ claw_rollers_closed_ = !claw_rollers_closed_;
+ }
+
+ /*
+ if (data.IsPressed(kClawClosed)) {
+ claw_rollers_closed_ = true;
+ }
+ */
+
+ // Horizontal can pickup.
+ if (data.PosEdge(kElevatorCanUp)) {
+ actors::HorizontalCanPickupParams params;
+ params.elevator_height = 0.3;
+ params.pickup_angle = 0.80 + kHorizontalCanClawAngle;
+ params.spit_time = 0.01;
+ params.spit_power = -8.0;
+
+ params.suck_time = 0.10;
+ params.suck_power = 10.0;
+
+ params.claw_settle_time = 0.05;
+ params.claw_settle_power = 5.0;
+ params.claw_full_lift_angle = 1.35;
+ params.claw_end_angle = 0.5;
+
+ // End low so we don't drop it.
+ params.elevator_end_height = 0.28;
+ params.arm_end_angle = 0.0;
+ action_queue_.EnqueueAction(
+ actors::MakeHorizontalCanPickupAction(params));
+ fridge_closed_ = true;
+ }
+
+ // -0.942503 arm, 0.374752 elevator
+ // Vertical can pickup.
+ if (data.PosEdge(kCanPickup)) {
+ actors::CanPickupParams params;
+ params.pickup_x = 0.6;
+ params.pickup_y = 0.1;
+ params.lift_height = 0.25;
+ params.pickup_goal_before_move_height = 0.3;
+ params.start_lowering_x = 0.1;
+ // End low so the can is supported.
+ params.before_place_height = 0.4;
+ params.end_height = 0.28;
+ params.end_angle = 0.0;
+ action_queue_.EnqueueAction(actors::MakeCanPickupAction(params));
+ fridge_closed_ = true;
+ }
+
+ if (data.PosEdge(kCanReset)) {
+ action_queue_.CancelAllActions();
+ elevator_goal_ = 0.28;
+ arm_goal_ = 0.0;
+ }
+
+ // Tote chute pull in when button is pressed, pack when done.
+ if (data.IsPressed(kToteChute)) {
+ claw_goal_ = 0.8;
+ intake_power = 6.0;
+ }
+ if (data.NegEdge(kToteChute)) {
+ claw_goal_ = kClawTotePackAngle;
+ }
+
+ if (data.PosEdge(kStackAndLift)) {
+ actors::StackAndLiftParams params;
+ params.stack_params.claw_out_angle = kClawTotePackAngle;
+ params.stack_params.bottom = 0.020;
+ params.stack_params.over_box_before_place_height = 0.39;
+ params.stack_params.arm_clearance = kArmRaiseLowerClearance;
+
+ params.grab_after_stack = true;
+ params.clamp_pause_time = 0.0;
+ params.lift_params.lift_height = kStackUpHeight;
+ params.lift_params.lift_arm = kStackUpArm;
+ params.lift_params.second_lift = false;
+ params.grab_after_lift = true;
+ fridge_closed_ = true;
+
+ action_queue_.EnqueueAction(actors::MakeStackAndLiftAction(params));
+ }
+
+ if (data.PosEdge(kStackAndHold) || data.PosEdge(kSetStackDownAndHold)) {
+ actors::StackAndHoldParams params;
+ params.bottom = 0.020;
+ params.over_box_before_place_height = 0.39;
+
+ params.arm_clearance = kArmRaiseLowerClearance;
+ params.clamp_pause_time = 0.25;
+ params.claw_clamp_angle = kClawTotePackAngle;
+
+ params.hold_height = 0.68;
+ params.claw_out_angle = kClawTotePackAngle;
+
+ if (data.PosEdge(kSetStackDownAndHold)) {
+ params.place_not_stack = true;
+ params.clamp_pause_time = 0.1;
+ //params.claw_clamp_angle = kClawTotePackAngle - 0.5;
+ } else {
+ params.place_not_stack = false;
+ }
+
+ action_queue_.EnqueueAction(actors::MakeStackAndHoldAction(params));
+ fridge_closed_ = true;
+ }
+
+ // TODO(austin): Figure out what action needed to pull the 6th tote into the
+ // claw.
+
+ // Lower the fridge from holding the stack, grab the stack, and then lift.
+ if (data.PosEdge(kHeldToLift)) {
+ actors::HeldToLiftParams params;
+ params.arm_clearance = kArmRaiseLowerClearance;
+ params.clamp_pause_time = 0.1;
+ params.before_lift_settle_time = 0.1;
+ params.bottom_height = 0.020;
+ params.claw_out_angle = kClawStackClearance;
+ params.lift_params.lift_height = kStackUpHeight;
+ params.lift_params.lift_arm = kStackUpArm;
+ params.lift_params.second_lift = false;
+ fridge_closed_ = true;
+
+ action_queue_.EnqueueAction(actors::MakeHeldToLiftAction(params));
+ }
+
+ // Lift the can up.
+ if (data.PosEdge(kCanUp)) {
+ actors::LiftParams params;
+ params.lift_height = 0.68;
+ params.lift_arm = 0.3;
+ params.pack_claw = false;
+ params.pack_claw_angle = 0;
+ params.intermediate_lift_height = 0.37;
+ params.second_lift = true;
+ fridge_closed_ = true;
+
+ action_queue_.EnqueueAction(actors::MakeLiftAction(params));
+ }
+
+ // Pick up a tote from the ground and put it in the bottom of the bot.
+ if (data.PosEdge(kPickup)) {
+ actors::PickupParams params;
+ // TODO(austin): These parameters are coppied into auto right now, need
+ // to dedup...
+
+ // Lift to here initially.
+ params.pickup_angle = 0.9;
+ // Start sucking here
+ params.suck_angle = 0.8;
+ // Go back down to here to finish sucking.
+ params.suck_angle_finish = 0.4;
+ // Pack the box back in here.
+ params.pickup_finish_angle = kClawTotePackAngle;
+ params.intake_time = 0.8;
+ params.intake_voltage = 7.0;
+ action_queue_.EnqueueAction(actors::MakePickupAction(params));
+ }
+
+ // Place stack on a tote in the tray, and grab it.
+ if (data.PosEdge(kStack)) {
+ actors::StackParams params;
+ params.claw_out_angle = kClawTotePackAngle;
+ params.bottom = 0.020;
+ params.only_place = false;
+ params.arm_clearance = kArmRaiseLowerClearance;
+ params.over_box_before_place_height = 0.39;
+ action_queue_.EnqueueAction(actors::MakeStackAction(params));
+ claw_rollers_closed_ = true;
+ fridge_closed_ = true;
+ }
+
+ if (data.PosEdge(kScore)) {
+ action_queue_.EnqueueAction(
+ actors::MakeScoreAction(MakeScoreParams()));
+ fridge_closed_ = false;
+ }
+
+ if (data.PosEdge(kCoopTop)) {
+ action_queue_.EnqueueAction(
+ actors::MakeScoreAction(MakeCoopTopParams(false)));
+ }
+ if (data.PosEdge(kCoopTopRetract)) {
+ action_queue_.EnqueueAction(
+ actors::MakeScoreAction(MakeCoopTopParams(true)));
+ fridge_closed_ = false;
+ }
+
+ if (data.PosEdge(kCoopBottom)) {
+ action_queue_.EnqueueAction(
+ actors::MakeScoreAction(MakeCoopBottomParams(false)));
+ }
+ if (data.PosEdge(kCoopBottomRetract)) {
+ action_queue_.EnqueueAction(
+ actors::MakeScoreAction(MakeCoopBottomParams(true)));
+ fridge_closed_ = false;
+ }
+
+ if (data.PosEdge(kFridgeToggle)) {
+ fridge_closed_ = !fridge_closed_;
+ }
+
+ if (data.PosEdge(ControlBit::kEnabled)) {
+ // If we got enabled, wait for everything to zero.
+ LOG(INFO, "Waiting for zero.\n");
+ waiting_for_zero_ = true;
+ }
+
+ claw_queue.status.FetchLatest();
+ fridge_queue.status.FetchLatest();
+ if (!claw_queue.status.get()) {
+ LOG(ERROR, "Got no claw status packet.\n");
+ }
+ if (!fridge_queue.status.get()) {
+ LOG(ERROR, "Got no fridge status packet.\n");
+ }
+
+ if (claw_queue.status.get() && fridge_queue.status.get() &&
+ claw_queue.status->zeroed && fridge_queue.status->zeroed) {
+ if (waiting_for_zero_) {
+ LOG(INFO, "Zeroed! Starting teleop mode.\n");
+ waiting_for_zero_ = false;
+
+ // Set the initial goals to where we are now.
+ elevator_goal_ = 0.3;
+ arm_goal_ = 0.0;
+ claw_goal_ = 0.6;
+ }
+ } else {
+ waiting_for_zero_ = true;
+ }
+
+ if (!waiting_for_zero_) {
+ if (!action_queue_.Running()) {
+ auto new_fridge_goal = fridge_queue.goal.MakeMessage();
+ new_fridge_goal->max_velocity = elevator_params_.velocity;
+ new_fridge_goal->max_acceleration = elevator_params_.acceleration;
+ new_fridge_goal->profiling_type = 0;
+ new_fridge_goal->height = elevator_goal_;
+ new_fridge_goal->velocity = 0.0;
+ new_fridge_goal->max_angular_velocity = arm_params_.velocity;
+ new_fridge_goal->max_angular_acceleration = arm_params_.acceleration;
+ new_fridge_goal->angle = arm_goal_;
+ new_fridge_goal->angular_velocity = 0.0;
+ new_fridge_goal->grabbers.top_front = fridge_closed_;
+ new_fridge_goal->grabbers.top_back = fridge_closed_;
+ new_fridge_goal->grabbers.bottom_front = fridge_closed_;
+ new_fridge_goal->grabbers.bottom_back = fridge_closed_;
+
+ if (!new_fridge_goal.Send()) {
+ LOG(ERROR, "Sending fridge goal failed.\n");
+ } else {
+ LOG(DEBUG, "sending goals: elevator: %f, arm: %f\n", elevator_goal_,
+ arm_goal_);
+ }
+ if (!claw_queue.goal.MakeWithBuilder()
+ .angle(claw_goal_)
+ .rollers_closed(claw_rollers_closed_)
+ .max_velocity(5.0)
+ .max_acceleration(6.0)
+ .intake(intake_power)
+ .Send()) {
+ LOG(ERROR, "Sending claw goal failed.\n");
+ }
+ }
+ }
+
+ if (action_queue_.Running()) {
+ // If we are running an action, update our goals to the current goals.
+ control_loops::fridge_queue.status.FetchLatest();
+ if (control_loops::fridge_queue.status.get()) {
+ arm_goal_ = control_loops::fridge_queue.status->goal_angle;
+ elevator_goal_ = control_loops::fridge_queue.status->goal_height;
+ } else {
+ LOG(ERROR, "No fridge status!\n");
+ }
+
+ // If we are running an action, update our goals to the current goals.
+ control_loops::claw_queue.status.FetchLatest();
+ if (control_loops::claw_queue.status.get()) {
+ claw_goal_ = control_loops::claw_queue.status->goal_angle;
+ } else {
+ LOG(ERROR, "No claw status!\n");
+ }
+ }
+ action_queue_.Tick();
+ was_running_ = action_queue_.Running();
+ }
+
+ private:
+ void StartAuto() {
+ LOG(INFO, "Starting auto mode\n");
+ ::frc971::autonomous::autonomous.MakeWithBuilder().run_auto(true).Send();
+ }
+
+ void StopAuto() {
+ LOG(INFO, "Stopping auto mode\n");
+ ::frc971::autonomous::autonomous.MakeWithBuilder().run_auto(false).Send();
+ }
+
+ bool was_running_;
+
+ // Previous goals for systems.
+ double elevator_goal_ = 0.2;
+ double arm_goal_ = 0.0;
+ double claw_goal_ = 0.0;
+ bool claw_rollers_closed_ = false;
+ bool fridge_closed_ = false;
+ actors::ProfileParams arm_params_ = kArmMove;
+ actors::ProfileParams elevator_params_ = kElevatorMove;
+
+ // If we're waiting for the subsystems to zero.
+ bool waiting_for_zero_ = true;
+
+ bool auto_running_ = false;
+
+ ::aos::common::actions::ActionQueue action_queue_;
+
+ ::aos::util::SimpleLogInterval no_drivetrain_status_ =
+ ::aos::util::SimpleLogInterval(::aos::time::Time::InSeconds(0.2), WARNING,
+ "no drivetrain status");
+};
+
+} // namespace joysticks
+} // namespace input
+} // namespace frc971
+
+int main() {
+ ::aos::Init();
+ ::frc971::input::joysticks::Reader reader;
+ reader.Run();
+ ::aos::Cleanup();
+}
diff --git a/y2015/prime/build.sh b/y2015/prime/build.sh
new file mode 100755
index 0000000..46cff40
--- /dev/null
+++ b/y2015/prime/build.sh
@@ -0,0 +1,5 @@
+#!/bin/bash
+
+cd $(dirname $0)
+
+exec ../../aos/build/build.py $0 prime y2015 prime.gyp "$@"
diff --git a/y2015/prime/compile_loop.sh b/y2015/prime/compile_loop.sh
new file mode 100755
index 0000000..de6b0d3
--- /dev/null
+++ b/y2015/prime/compile_loop.sh
@@ -0,0 +1,16 @@
+#!/bin/bash
+
+# Runs `build.sh all` and then waits for a file to be modified in a loop.
+# Useful for making changes to the code while continuously making sure they
+# compile.
+# Requires the util-linux and inotify-tools packages.
+
+chrt -i -p 0 $$
+ionice -c 3 -p $$
+
+while true; do
+ $(dirname $0)/build.sh all
+ echo 'compile_loop.sh: Waiting for a file modification...' 1>&2
+ inotifywait -e close_write -r aos frc971 bbb_cape
+ echo 'compile_loop.sh: Done waiting for a file modification' 1>&2
+done
diff --git a/y2015/prime/prime.gyp b/y2015/prime/prime.gyp
new file mode 100644
index 0000000..5a8b006
--- /dev/null
+++ b/y2015/prime/prime.gyp
@@ -0,0 +1,41 @@
+{
+ 'targets': [
+ {
+ 'target_name': 'All',
+ 'type': 'none',
+ 'dependencies': [
+ '../../frc971/frc971.gyp:All',
+
+ '../control_loops/drivetrain/drivetrain.gyp:drivetrain',
+ '../control_loops/drivetrain/drivetrain.gyp:drivetrain_lib_test',
+ '../control_loops/drivetrain/drivetrain.gyp:replay_drivetrain',
+ '../control_loops/fridge/fridge.gyp:fridge',
+ '../control_loops/fridge/fridge.gyp:fridge_lib_test',
+ '../control_loops/fridge/fridge.gyp:replay_fridge',
+ '../control_loops/claw/claw.gyp:claw',
+ '../control_loops/claw/claw.gyp:claw_lib_test',
+ '../control_loops/claw/claw.gyp:replay_claw',
+ '../autonomous/autonomous.gyp:auto',
+ '../y2015.gyp:joystick_reader',
+ '../http_status/http_status.gyp:http_status',
+ '../util/util.gyp:kinematics_test',
+ '../actors/actors.gyp:binaries',
+ ],
+ 'copies': [
+ {
+ 'destination': '<(rsync_dir)',
+ 'files': [
+ 'start_list.txt',
+ ],
+ },
+ ],
+ 'conditions': [
+ ['ARCHITECTURE=="arm_frc"', {
+ 'dependencies': [
+ '../wpilib/wpilib.gyp:wpilib_interface',
+ ],
+ }],
+ ],
+ },
+ ],
+}
diff --git a/y2015/prime/start_list.txt b/y2015/prime/start_list.txt
new file mode 100644
index 0000000..e4f0a8a
--- /dev/null
+++ b/y2015/prime/start_list.txt
@@ -0,0 +1,18 @@
+can_pickup_action
+joystick_reader
+pickup_action
+score_action
+stack_action
+wpilib_interface
+binary_log_writer
+claw
+fridge
+horizontal_can_pickup_action
+lift_action
+stack_and_lift_action
+stack_and_hold_action
+held_to_lift_action
+drivetrain
+auto
+drivetrain_action
+http_status
diff --git a/y2015/util/kinematics.h b/y2015/util/kinematics.h
new file mode 100644
index 0000000..611012e
--- /dev/null
+++ b/y2015/util/kinematics.h
@@ -0,0 +1,388 @@
+#ifndef Y2015_UTIL_KINEMATICS_H_
+#define Y2015_UTIL_KINEMATICS_H_
+
+#include <cmath>
+#include "Eigen/Dense"
+#include "y2015/constants.h"
+
+namespace aos {
+namespace util {
+
+// A class for performing forward and inverse kinematics on the elevator-arm
+// system. It can calculate where the fridge grabbers will be if the arm and
+// elevator are at a given position, as well as where the arm and elevator
+// should go in order to get the grabbers to a specific location.
+class ElevatorArmKinematics {
+ public:
+ typedef enum {
+ // These specify the particular region that an invalid request was in. Right
+ // is toward the front of the robot, left is toward the back.
+
+ // Request is valid.
+ REGION_VALID = 0,
+ // Request is farther right than the arm can extend.
+ REGION_RIGHT = 1 << 0,
+ // Request is towards the front of the robot but higher than we can extend
+ // with the elevator and the arm.
+ REGION_UPPER_RIGHT = 1 << 1,
+ // We can get the x part of the request, which is towards the front of the
+ // robot, but not the h part, which is too high.
+ REGION_INSIDE_UPPER_RIGHT = 1 << 2,
+ // We can get the x part of the request, which is towards the front of the
+ // robot, but not the h part, which is too low.
+ REGION_INSIDE_LOWER_RIGHT = 1 << 3,
+ // Request is towards the front of the robot but lower than we can extend
+ // with the elevator and the arm.
+ REGION_LOWER_RIGHT = 1 << 4,
+ // Request is farther left than the arm can extend.
+ REGION_LEFT = 1 << 5,
+ // Request is towards the back of the robot but higher than we can extend
+ // with the elevator and the arm.
+ REGION_UPPER_LEFT = 1 << 6,
+ // We can get the x part of the request, which is towards the front of the
+ // robot, but not the h part, which is too high.
+ REGION_INSIDE_UPPER_LEFT = 1 << 7,
+ // We can get the x part of the request, which is towards the back of the
+ // robot, but not the h part, which is too low.
+ REGION_INSIDE_LOWER_LEFT = 1 << 8,
+ // Request is towards the back of the robot but lower than we can extend
+ // with the elevator and the arm.
+ REGION_LOWER_LEFT = 1 << 9,
+ // Request is invalid, but don't know where it is out of range.
+ REGION_UNKNOWN = 1 << 10,
+ } Region;
+
+ class KinematicResult {
+ public:
+ // The elevator height result from an inverse kinematic.
+ double elevator_height;
+ // The arm angle result from an inverse kinematic.
+ double arm_angle;
+ // Resulting velocity of the elevator given x,y velocities.
+ double elevator_velocity;
+ // Resulting velocity of the arm given x,y velocities.
+ double arm_velocity;
+ // The fridge height value from a forward kinematic.
+ double fridge_h;
+ // The fridge x value from a forward kinematic.
+ double fridge_x;
+ // Resulting velocity of the fridge height given arm and elevator
+ // velocities.
+ double fridge_h_velocity;
+ // Resulting velocity of the fridge x given arm and elevator velocities.
+ double fridge_x_velocity;
+ };
+
+ // If we use the default constructor we wil just always not be able to move.
+ ElevatorArmKinematics()
+ : length_arm_(1.0),
+ elevator_max_(0.0),
+ elevator_min_(0.0),
+ upper_angle_limit_(0.0),
+ lower_angle_limit_(0.0) {}
+
+ ElevatorArmKinematics(double length_arm, double height_max, double height_min,
+ double angle_max, double angle_min)
+ : length_arm_(length_arm),
+ elevator_max_(height_max),
+ elevator_min_(height_min),
+ upper_angle_limit_(angle_max),
+ lower_angle_limit_(angle_min),
+ geometry_(frc971::constants::GetValues().clawGeometry) {}
+
+ ~ElevatorArmKinematics() {}
+
+ // Limit a number to the speed of light. The loops should handle this a lot
+ // better than overflow.
+ void LimitLightSpeed(double* num) {
+ if (*num > 299792458.0) {
+ *num = 299792458.0;
+ }
+ if (*num < -299792458.0) {
+ *num = -299792458.0;
+ }
+ if (!::std::isfinite(*num)) {
+ *num = 0.0;
+ }
+ }
+
+ // Calculates the arm angle in radians and the elevator height in meters for
+ // a desired Fridge grabber height and x location. x is positive going
+ // toward the front of the robot.
+ // h is positive going up. x=0 and h=0 is the location of the top fridge
+ // grabber when the elevator is at 0 height and the arm angle is 0 (vertical).
+ // Both the x and h values are given in meters.
+ // Returns the region of the request.
+ // Result is:
+ // the angle of the arm in radians
+ // the height of the elevator in meters
+ // the resulting x
+ // and the resulting h
+ // If an impossible location is requested, the arm angle and elevator height
+ // returned are the closest possible for the requested fridge grabber height.
+ // If the requested height is above the max possible height, the angle
+ // will be 0 and the height will be the max possible height of the elevator.
+ int InverseKinematic(double request_x, double request_h,
+ double request_x_velocity, double request_y_velocity,
+ KinematicResult* result) {
+ int valid_or_invalid = REGION_VALID;
+
+ double square_arm = length_arm_ * length_arm_;
+ double term = ::std::sqrt(square_arm - request_x * request_x);
+
+ // Check to see if the x location can be satisfied. If the requested x
+ // location
+ // is further out than the arm can go, it is not possible for any elevator
+ // location.
+ if (request_x > length_arm_) {
+ result->arm_angle = -M_PI * 0.5;
+ valid_or_invalid |= REGION_RIGHT;
+ } else if (request_x < -length_arm_) {
+ result->arm_angle = M_PI * 0.5;
+ valid_or_invalid |= REGION_LEFT;
+ } else {
+ result->arm_angle = ::std::asin(-request_x / length_arm_);
+ result->arm_velocity = (-1.0 / term) * request_x_velocity;
+ LimitLightSpeed(&result->arm_velocity);
+ }
+
+ result->elevator_height =
+ request_h + length_arm_ * (1.0 - ::std::cos(result->arm_angle));
+ result->elevator_velocity =
+ (request_x / (square_arm * term)) * request_x_velocity +
+ request_y_velocity;
+ LimitLightSpeed(&result->elevator_velocity);
+
+ // Check to see if the requested elevator height is possible
+ if (request_h > elevator_max_) {
+ // The elevator cannot go high enough with any arm angle to satisfy this
+ // request. So position the elevator at the top and the arm angle set to
+ // vertical.
+ result->elevator_height = elevator_max_;
+ result->arm_angle = 0.0;
+ if (request_x >= 0) {
+ valid_or_invalid |= REGION_UPPER_RIGHT;
+ } else {
+ valid_or_invalid |= REGION_UPPER_LEFT;
+ }
+ } else if (request_h < -length_arm_ + elevator_min_) {
+ // The elevator cannot go low enough with any arm angle to satisfy this
+ // request. So position the elevator at the bottom and the arm angle to
+ // satisfy the x request The elevator will move up as the grabber moves to
+ // the center of the robot when in this part of the motion space.
+ result->elevator_height = elevator_min_;
+ if (request_x >= 0) {
+ valid_or_invalid |= REGION_LOWER_RIGHT;
+ } else {
+ valid_or_invalid |= REGION_LOWER_LEFT;
+ }
+ } else if (result->elevator_height > elevator_max_) {
+ // Impossibly high request. So get as close to the x request with the
+ // elevator at the top of its range.
+ result->elevator_height = elevator_max_;
+ if (request_x >= 0) {
+ result->arm_angle =
+ -::std::acos((length_arm_ + request_h - elevator_max_) /
+ length_arm_);
+ valid_or_invalid |= REGION_INSIDE_UPPER_RIGHT;
+ } else {
+ result->arm_angle = ::std::acos(
+ (length_arm_ + request_h - elevator_max_) / length_arm_);
+ valid_or_invalid |= REGION_INSIDE_UPPER_LEFT;
+ }
+ } else if (result->elevator_height < elevator_min_) {
+ // Impossibly low request. So satisfy the x request with the elevator
+ // at the bottom of its range.
+ // The elevator will move up as the grabber moves to the center of the
+ // robot
+ // when in this part of the motion space.
+ result->elevator_height = elevator_min_;
+ if (request_x >= 0) {
+ valid_or_invalid |= REGION_INSIDE_LOWER_RIGHT;
+ } else {
+ valid_or_invalid |= REGION_INSIDE_LOWER_LEFT;
+ }
+ }
+
+ // if we are not in a valid region we will zero the velocity for now
+ if (valid_or_invalid != REGION_VALID) {
+ result->arm_velocity = 0.0;
+ result->elevator_velocity = 0.0;
+ }
+
+ if (ForwardKinematic(result->elevator_height, result->arm_angle,
+ result->elevator_velocity, result->arm_velocity,
+ result) == REGION_UNKNOWN) {
+ return REGION_UNKNOWN;
+ }
+ return valid_or_invalid;
+ }
+
+ // Takes an elevator height and arm angle and projects these to the resulting
+ // fridge height and x offset. Returns REGION_UNKNOWN if the values are
+ // outside
+ // limits. This will result in the height/angle being bounded and the
+ // resulting position will be returned.
+ Region ForwardKinematic(double elevator_height, double arm_angle,
+ double elevator_velocity, double arm_velocity,
+ KinematicResult* result) {
+ result->elevator_height = elevator_height;
+ result->arm_angle = arm_angle;
+
+ Region valid = REGION_VALID;
+ if (elevator_height < elevator_min_) {
+ LOG(WARNING, "elevator %.2f limited at %.2f\n", result->elevator_height,
+ elevator_min_);
+ result->elevator_height = elevator_min_;
+ valid = REGION_UNKNOWN;
+ }
+ if (elevator_height > elevator_max_) {
+ LOG(WARNING, "elevator %.2f limited at %.2f\n", result->elevator_height,
+ elevator_max_);
+ result->elevator_height = elevator_max_;
+ valid = REGION_UNKNOWN;
+ }
+ if (arm_angle < lower_angle_limit_) {
+ LOG(WARNING, "arm %.2f limited at %.2f\n", result->arm_angle,
+ lower_angle_limit_);
+ result->arm_angle = lower_angle_limit_;
+ valid = REGION_UNKNOWN;
+ }
+ if (arm_angle > upper_angle_limit_) {
+ result->arm_angle = upper_angle_limit_;
+ LOG(WARNING, "arm %.2f limited at %.2f\n", result->arm_angle,
+ upper_angle_limit_);
+ valid = REGION_UNKNOWN;
+ }
+ // Compute the fridge grabber height and x location using the computed
+ // elevator height and arm angle.
+ result->fridge_h = result->elevator_height +
+ (::std::cos(result->arm_angle) - 1.0) * length_arm_;
+ result->fridge_x = -::std::sin(result->arm_angle) * length_arm_;
+ // velocity based on joacobian
+ result->fridge_x_velocity =
+ -length_arm_ * ::std::cos(result->arm_angle) * arm_velocity;
+ LimitLightSpeed(&result->fridge_x_velocity);
+ result->fridge_h_velocity =
+ -length_arm_ * ::std::sin(result->arm_angle) * arm_velocity +
+ elevator_velocity;
+ LimitLightSpeed(&result->fridge_h_velocity);
+ return valid;
+ }
+
+ // Same as ForwardKinematic but without any checking.
+ Eigen::Vector2d ForwardKinematicNoChecking(double elevator_height,
+ double arm_angle) {
+ // Compute the fridge grabber height and x location using the computed
+ // elevator height and arm angle.
+ Eigen::Vector2d grabber_location;
+ grabber_location.y() =
+ elevator_height + (::std::cos(arm_angle) - 1.0) * length_arm_;
+ grabber_location.x() = -::std::sin(arm_angle) * length_arm_;
+ return grabber_location;
+ }
+
+ // 2 dimensional version of cross product
+ double Cross(Eigen::Vector2d a, Eigen::Vector2d b) {
+ double crossProduct = a.x() * b.y() - a.y() * b.x();
+ return crossProduct;
+ }
+
+ // Tell whether or not it is safe to move the grabber to a position.
+ // Returns true if the current move is safe.
+ // If it returns false then a safe_claw_angle that is greater than zero is
+ // acceptable otherwise if safe_claw_angle is less than zero there will be no
+ // valid solution.
+ bool GrabberArmIntersectionCheck(double elevator_height, double arm_angle,
+ double claw_angle, double* safe_claw_angle) {
+ Eigen::Vector2d grabber_location =
+ ForwardKinematicNoChecking(elevator_height, arm_angle);
+ if (grabber_location.x() < geometry_.grabber_always_safe_x_max ||
+ grabber_location.y() > geometry_.grabber_always_safe_h_min) {
+ *safe_claw_angle = claw_angle;
+ return true;
+ }
+ Eigen::Vector2d grabber_bottom_end;
+ Eigen::Vector2d claw_i_unit_direction(::std::cos(claw_angle),
+ sin(claw_angle));
+ Eigen::Vector2d claw_j_unit_direction(-::std::sin(claw_angle),
+ cos(claw_angle));
+
+ // Vector from the center of the arm rotation axis to front bottom
+ // corner of the grabber.
+ Eigen::Vector2d grabber_end_location_from_arm_axis(
+ geometry_.grabber_half_length, -geometry_.grabber_delta_y);
+
+ // Bottom front corner of the grabber. This is what will usually hit the
+ // claw first.
+ grabber_bottom_end = grabber_location + grabber_end_location_from_arm_axis;
+
+ // Location of the claw horizontal axis of rotation relative to the
+ // arm axis of rotation with the elevator at 0 and the arm angle of 0
+ // The horizontal axis is the up and down motion axis.
+ Eigen::Vector2d claw_updown_axis(geometry_.grabber_arm_horz_separation,
+ -geometry_.grabber_arm_vert_separation);
+
+ // This point is used to make a cross product with the bottom end of the
+ // grabber
+ // The result of the cross product tells if the parts intersect or not.
+ Eigen::Vector2d claw_top_ref_point =
+ claw_updown_axis + geometry_.claw_top_thickness * claw_j_unit_direction;
+
+ Eigen::Vector2d claw_top_ref_point_to_grabber_bottom_end =
+ grabber_bottom_end - claw_top_ref_point;
+ double claw_grabber_check =
+ Cross(claw_i_unit_direction, claw_top_ref_point_to_grabber_bottom_end);
+
+ // Now set the safe claw angle.
+ if (claw_grabber_check > 0.0) {
+ *safe_claw_angle = claw_angle;
+ return true;
+ } else if (grabber_bottom_end.y() <
+ claw_updown_axis.y() +
+ geometry_.claw_top_thickness) { // grabber is too close
+ *safe_claw_angle = -1.0;
+ return false;
+ } else {
+ // To find the safe angle for the claw, draw a line between the claw
+ // rotation axis and the lower front corner of the grabber. The angle of
+ // this line is used with the angle between the edge of the claw and the
+ // center line of the claw to determine the angle of the claw.
+ Eigen::Vector2d claw_axis_to_grabber_bottom_end =
+ grabber_bottom_end - claw_updown_axis;
+ double hypot = claw_axis_to_grabber_bottom_end.norm();
+ double angleDiff = ::std::asin(geometry_.claw_top_thickness / hypot);
+ *safe_claw_angle = ::std::atan2(claw_axis_to_grabber_bottom_end.y(),
+ claw_axis_to_grabber_bottom_end.x()) -
+ angleDiff;
+ return false;
+ }
+ }
+
+ double get_elevator_min() { return elevator_min_; }
+
+ double get_elevator_max() { return elevator_max_; }
+
+ double get_upper_angle_limit() { return upper_angle_limit_; }
+
+ double get_lower_angle_limit() { return lower_angle_limit_; }
+
+ private:
+ // length of the arm
+ double length_arm_;
+ // max height the elevator can go.
+ double elevator_max_;
+ // min height the elevator can go.
+ double elevator_min_;
+ // arm angle upper limit
+ double upper_angle_limit_;
+ // arm angle lower limit
+ double lower_angle_limit_;
+ // Geometry of the arm + fridge
+ frc971::constants::Values::ClawGeometry geometry_;
+};
+
+} // namespace util
+} // namespace aos
+
+#endif // Y2015_UTIL_KINEMATICS_H_
diff --git a/y2015/util/kinematics_test.cc b/y2015/util/kinematics_test.cc
new file mode 100644
index 0000000..be1645e
--- /dev/null
+++ b/y2015/util/kinematics_test.cc
@@ -0,0 +1,242 @@
+#include <cmath>
+
+#include "gtest/gtest.h"
+
+#include "aos/common/logging/logging.h"
+#include "aos/common/queue_testutils.h"
+#include "y2015/util/kinematics.h"
+#include "frc971/control_loops/team_number_test_environment.h"
+
+namespace aos {
+namespace util {
+namespace testing {
+
+// For verifcation against Mr. Schuh's internal tests.
+// Please do not comment on this in a code review. We can remove this after the
+// season to satisfy any OCD.
+bool k_internal_debug = false;
+
+class KinematicsTest : public ::testing::Test {
+ public:
+ KinematicsTest()
+ : lower_height_limit_(0.01),
+ upper_height_limit_(0.65),
+ lower_angle_limit_(-M_PI / 2.0),
+ upper_angle_limit_(M_PI / 2.0) {}
+
+ void SetUp() {
+ ::aos::common::testing::EnableTestLogging();
+ kinematics_ = ElevatorArmKinematics(arm_length_, upper_height_limit_, lower_height_limit_,
+ upper_angle_limit_, lower_angle_limit_);
+ }
+
+ protected:
+ double lower_height_limit_;
+ double upper_height_limit_;
+ double lower_angle_limit_;
+ double upper_angle_limit_;
+ double arm_length_ = 0.7366;
+
+ ElevatorArmKinematics kinematics_;
+};
+
+// Used for internal debugging and verification only not acctual testing.
+// Please do not comment on this in a code review. We can remove this after
+// the season to satisfy any OCD.
+TEST_F(KinematicsTest,
+ PrintPointsSoWeCanHandVerifyThemUntilABetterTestIsMadeAndBrianIsHappy) {
+ if (k_internal_debug) {
+ for (double y = -1.0; y <= 1.0; y += 0.2) {
+ for (double x = -1.0; x <= 1.0; x += 0.2) {
+ ElevatorArmKinematics::KinematicResult res;
+ int region = kinematics_.InverseKinematic(x, y, 0.0, 0.0, &res);
+ printf(
+ " %12.3f %12.3f %8.3f %9.3f %8.2f %12d %12.4f %10.4f "
+ "%15.4f %16.4f\n",
+ x, y, res.elevator_height, res.arm_angle,
+ res.arm_angle * 180.0 / M_PI, region, res.fridge_x, res.fridge_h,
+ res.fridge_x - x, res.fridge_h - y);
+ }
+ }
+
+ // Make a set of calls to test the grabber arm intersection test code.
+ printf(
+ "# ArmAngle (degrees) ElevatorHeight ClawAngle (degrees) GrabberX "
+ "GrabberH intersectReturn SafeClawAngle (degrees)\n");
+ for (double elevator_height = kinematics_.get_elevator_min();
+ elevator_height <= kinematics_.get_elevator_max();
+ elevator_height += 0.10) {
+ for (double arm_angle = kinematics_.get_lower_angle_limit();
+ arm_angle <= kinematics_.get_upper_angle_limit() + 0.01;
+ arm_angle += M_PI * 0.5 / 9.0) {
+ double claw_angle = M_PI * 0.25;
+ double safe_claw_angle;
+ double intersectReturnValue = kinematics_.GrabberArmIntersectionCheck(
+ elevator_height, arm_angle, claw_angle, &safe_claw_angle);
+ Eigen::Vector2d grabber_location =
+ kinematics_.ForwardKinematicNoChecking(elevator_height, arm_angle);
+
+ printf(
+ " %8.4f %8.2f %8.2f %14.4f %9.2f %9.2f %9.2f %10.3f %13.4f "
+ "%12.3f\n",
+ arm_angle, arm_angle * 180.0 / M_PI, elevator_height, claw_angle,
+ claw_angle * 180.0 / M_PI, grabber_location.x(),
+ grabber_location.y(), intersectReturnValue, safe_claw_angle,
+ safe_claw_angle * 180.0 / M_PI);
+ }
+ printf("\n");
+ }
+ }
+}
+
+TEST_F(KinematicsTest, ValidIntersectCheckPointAtBottomOfElevatorRange) {
+ double safe_claw_angle;
+ double elevator_height = 0.01;
+ double arm_angle = 30.0 * M_PI / 180.0;
+ double claw_angle = M_PI * 0.25;
+ bool intersectReturnValue = kinematics_.GrabberArmIntersectionCheck(
+ elevator_height, arm_angle, claw_angle, &safe_claw_angle);
+ EXPECT_TRUE(intersectReturnValue);
+ EXPECT_EQ(safe_claw_angle, claw_angle);
+}
+
+TEST_F(KinematicsTest, ValidIntersectCheckPointAtMiddleOfElevatorRange) {
+ double safe_claw_angle;
+ double elevator_height = 0.4;
+ double arm_angle = 30.0 * M_PI / 180.0;
+ double claw_angle = M_PI * 0.25;
+ bool intersectReturnValue = kinematics_.GrabberArmIntersectionCheck(
+ elevator_height, arm_angle, claw_angle, &safe_claw_angle);
+ EXPECT_TRUE(intersectReturnValue);
+ EXPECT_EQ(safe_claw_angle, claw_angle);
+}
+
+TEST_F(KinematicsTest,
+ invalidIntersectCheckPointAtBottomOfElevatorRangeWithSafeClawAngle) {
+ double safe_claw_angle;
+ double elevator_height = 0.01;
+ double arm_angle = -20.0 * M_PI / 180.0;
+ double claw_angle = M_PI * 0.25;
+ bool intersectReturnValue = kinematics_.GrabberArmIntersectionCheck(
+ elevator_height, arm_angle, claw_angle, &safe_claw_angle);
+ EXPECT_FALSE(intersectReturnValue);
+ EXPECT_NEAR(safe_claw_angle, 0.0435733, 0.000001);
+}
+
+TEST_F(KinematicsTest,
+ invalidIntersectCheckPointAtMiddleOfElevatorRangeWithSafeClawAngle) {
+ double safe_claw_angle;
+ double elevator_height = 0.41;
+ double arm_angle = -60.0 * M_PI / 180.0;
+ double claw_angle = M_PI * 0.25;
+ bool intersectReturnValue = kinematics_.GrabberArmIntersectionCheck(
+ elevator_height, arm_angle, claw_angle, &safe_claw_angle);
+ EXPECT_FALSE(intersectReturnValue);
+ EXPECT_NEAR(safe_claw_angle, 0.12655341, 0.000001);
+}
+
+TEST_F(KinematicsTest,
+ invalidIntersectCheckPointAtBottomOfElevatorRangeNoSafeClawAngle) {
+ double safe_claw_angle;
+ double elevator_height = 0.01;
+ double arm_angle = -30.0 * M_PI / 180.0;
+ double claw_angle = M_PI * 0.25;
+ bool intersectReturnValue = kinematics_.GrabberArmIntersectionCheck(
+ elevator_height, arm_angle, claw_angle, &safe_claw_angle);
+ EXPECT_FALSE(intersectReturnValue);
+ EXPECT_EQ(safe_claw_angle, -1.0);
+}
+
+TEST_F(KinematicsTest,
+ invalidIntersectCheckPointAtMiddleOfElevatorRangeNoSafeClawAngle) {
+ double safe_claw_angle;
+ double elevator_height = 0.41;
+ double arm_angle = -70.0 * M_PI / 180.0;
+ double claw_angle = M_PI * 0.25;
+ bool intersectReturnValue = kinematics_.GrabberArmIntersectionCheck(
+ elevator_height, arm_angle, claw_angle, &safe_claw_angle);
+ EXPECT_FALSE(intersectReturnValue);
+ EXPECT_EQ(safe_claw_angle, -1.0);
+}
+
+// Tests that velocity calulations are correct
+TEST_F(KinematicsTest, InverseKinematicVelocity) {
+ ElevatorArmKinematics::KinematicResult result;
+ // move striaght up and verify that only hieght changes
+ EXPECT_EQ(0, kinematics_.InverseKinematic(0.0, 0.2, 0.0, 0.7, &result));
+ EXPECT_NEAR(0.0, result.arm_velocity, 0.00001);
+ EXPECT_NEAR(0.7, result.elevator_velocity, 0.00001);
+ // check the negative
+ EXPECT_EQ(0, kinematics_.InverseKinematic(0.0, 0.2, 0.0, -0.7, &result));
+ EXPECT_NEAR(0.0, result.arm_velocity, 0.00001);
+ EXPECT_NEAR(-0.7, result.elevator_velocity, 0.00001);
+ // even with the arm out we should still just move up
+ EXPECT_EQ(0, kinematics_.InverseKinematic(M_PI / 6, 0.2, 0.0, 0.7, &result));
+ EXPECT_NEAR(0.0, result.arm_velocity, 0.00001);
+ EXPECT_NEAR(0.7, result.elevator_velocity, 0.00001);
+ // even with the arm back we should still just move up
+ EXPECT_EQ(0, kinematics_.InverseKinematic(-M_PI / 6, 0.2, 0.0, 0.7, &result));
+ EXPECT_NEAR(0.0, result.arm_velocity, 0.00001);
+ EXPECT_NEAR(0.7, result.elevator_velocity, 0.00001);
+
+ // should move only angle forward
+ EXPECT_EQ(0, kinematics_.InverseKinematic(0.0, 0.2, 1.0, 0.0, &result));
+ EXPECT_NEAR(-1.35759, result.arm_velocity, 0.00001);
+ EXPECT_NEAR(0.0, result.elevator_velocity, 0.00001);
+ // check the negative
+ EXPECT_EQ(0, kinematics_.InverseKinematic(0.0, 0.2, -1.0, 0.0, &result));
+ EXPECT_NEAR(1.35759, result.arm_velocity, 0.00001);
+ EXPECT_NEAR(0.0, result.elevator_velocity, 0.00001);
+ // with the arm out a change in x should make arm angle greater and
+ // bring the evevator down.
+ EXPECT_EQ(0, kinematics_.InverseKinematic(0.2, 0.2, 1.0, 0.0, &result));
+ EXPECT_GT(0.0, result.arm_velocity);
+ EXPECT_LT(0.0, result.elevator_velocity);
+ // with the arm out a change in x should make arm angle greater and
+ // bring the evevator down.
+ EXPECT_EQ(0, kinematics_.InverseKinematic(-0.2, 0.2, 1.0, 0.0, &result));
+ EXPECT_GT(0.0, result.arm_velocity);
+ EXPECT_GT(0.0, result.elevator_velocity);
+}
+
+// Tests that velocity calulations are correct
+TEST_F(KinematicsTest, ForwardKinematicVelocity) {
+ ElevatorArmKinematics::KinematicResult result;
+
+ // moving the arm forward at zero should result in x velocity
+ EXPECT_EQ(0, kinematics_.ForwardKinematic(0.2, 0.0, 0.0, 1.35759, &result));
+ EXPECT_NEAR(-1.0, result.fridge_x_velocity, 0.00001);
+ EXPECT_NEAR(0.0, result.fridge_h_velocity, 0.00001);
+ // check the negative
+ EXPECT_EQ(0, kinematics_.ForwardKinematic(0.2, 0.0, 0.0, -1.35759, &result));
+ EXPECT_NEAR(1.0, result.fridge_x_velocity, 0.00001);
+ EXPECT_NEAR(0.0, result.fridge_h_velocity, 0.00001);
+ // moving the arm up at zero should result in h velocity
+ EXPECT_EQ(0, kinematics_.ForwardKinematic(0.2, 0.0, 1.0, 0.0, &result));
+ EXPECT_NEAR(0.0, result.fridge_x_velocity, 0.00001);
+ EXPECT_NEAR(1.0, result.fridge_h_velocity, 0.00001);
+ // check the negative
+ EXPECT_EQ(0, kinematics_.ForwardKinematic(0.2, 0.0, -1.0, 0.0, &result));
+ EXPECT_NEAR(0.0, result.fridge_x_velocity, 0.00001);
+ EXPECT_NEAR(-1.0, result.fridge_h_velocity, 0.00001);
+ // arm is forward a negative angle should make x head forward and y head down.
+ EXPECT_EQ(0, kinematics_.ForwardKinematic(0.5, -0.2, 0.0, -1.0, &result));
+ EXPECT_GT(result.fridge_x_velocity, 0.0);
+ EXPECT_LT(result.fridge_h_velocity, 0.0);
+ // arm is forward a positive angle should make x head backwardward and y head up.
+ EXPECT_EQ(0, kinematics_.ForwardKinematic(0.5, -0.2, 0.0, 1.0, &result));
+ EXPECT_LT(result.fridge_x_velocity, 0.0);
+ EXPECT_GT(result.fridge_h_velocity, 0.0);
+ // arm is backward a negative angle should make x head forward and y head down.
+ EXPECT_EQ(0, kinematics_.ForwardKinematic(0.5, 0.2, 0.0, -1.0, &result));
+ EXPECT_GT(result.fridge_x_velocity, 0.0);
+ EXPECT_GT(result.fridge_h_velocity, 0.0);
+ // arm is backward a negative angle should make x head forward and y head down.
+ EXPECT_EQ(0, kinematics_.ForwardKinematic(0.5, 0.2, 0.0, 1.0, &result));
+ EXPECT_LT(result.fridge_x_velocity, 0.0);
+ EXPECT_LT(result.fridge_h_velocity, 0.0);
+}
+
+} // namespace testing
+} // namespace util
+} // namespace aos
diff --git a/y2015/util/util.gyp b/y2015/util/util.gyp
new file mode 100644
index 0000000..00079c1
--- /dev/null
+++ b/y2015/util/util.gyp
@@ -0,0 +1,33 @@
+{
+ 'targets': [
+ {
+ 'target_name': 'kinematics',
+ 'type': 'static_library',
+ 'sources': [
+ #'kinematics.h',
+ ],
+ 'dependencies': [
+ '<(EXTERNALS):eigen',
+ '<(DEPTH)/y2015/y2015.gyp:constants',
+ ],
+ 'export_dependent_settings': [
+ '<(EXTERNALS):eigen',
+ '<(DEPTH)/y2015/y2015.gyp:constants',
+ ],
+ },
+ {
+ 'target_name': 'kinematics_test',
+ 'type': 'executable',
+ 'sources': [
+ 'kinematics_test.cc',
+ ],
+ 'dependencies': [
+ '<(EXTERNALS):gtest',
+ '<(AOS)/common/common.gyp:queue_testutils',
+ '<(AOS)/build/aos.gyp:logging',
+ '<(DEPTH)/frc971/control_loops/control_loops.gyp:team_number_test_environment',
+ 'kinematics'
+ ],
+ },
+ ],
+}
diff --git a/y2015/wpilib/wpilib.gyp b/y2015/wpilib/wpilib.gyp
new file mode 100644
index 0000000..8c1be39
--- /dev/null
+++ b/y2015/wpilib/wpilib.gyp
@@ -0,0 +1,38 @@
+{
+ 'targets': [
+ {
+ 'target_name': 'wpilib_interface',
+ 'type': 'executable',
+ 'sources': [
+ 'wpilib_interface.cc'
+ ],
+ 'dependencies': [
+ '<(AOS)/linux_code/linux_code.gyp:init',
+ '<(AOS)/common/common.gyp:stl_mutex',
+ '<(AOS)/build/aos.gyp:logging',
+ '<(EXTERNALS):WPILib',
+ '<(DEPTH)/y2015/y2015.gyp:constants',
+ '<(DEPTH)/y2015/control_loops/drivetrain/drivetrain.gyp:drivetrain_queue',
+ '<(DEPTH)/y2015/control_loops/fridge/fridge.gyp:fridge_queue',
+ '<(DEPTH)/y2015/control_loops/claw/claw.gyp:claw_queue',
+ '<(AOS)/common/controls/controls.gyp:control_loop',
+ '<(AOS)/common/util/util.gyp:log_interval',
+ '<(AOS)/common/common.gyp:time',
+ '<(AOS)/common/logging/logging.gyp:queue_logging',
+ '<(AOS)/common/messages/messages.gyp:robot_state',
+ '<(AOS)/common/util/util.gyp:phased_loop',
+ '<(AOS)/common/messages/messages.gyp:robot_state',
+ '<(DEPTH)/frc971/wpilib/wpilib.gyp:hall_effect',
+ '<(DEPTH)/frc971/wpilib/wpilib.gyp:joystick_sender',
+ '<(DEPTH)/frc971/wpilib/wpilib.gyp:loop_output_handler',
+ '<(DEPTH)/frc971/wpilib/wpilib.gyp:buffered_pcm',
+ '<(DEPTH)/frc971/wpilib/wpilib.gyp:gyro_sender',
+ '<(DEPTH)/frc971/wpilib/wpilib.gyp:dma_edge_counting',
+ '<(DEPTH)/frc971/wpilib/wpilib.gyp:interrupt_edge_counting',
+ '<(DEPTH)/frc971/wpilib/wpilib.gyp:encoder_and_potentiometer',
+ '<(DEPTH)/frc971/control_loops/control_loops.gyp:queues',
+ '<(DEPTH)/frc971/wpilib/wpilib.gyp:logging_queue',
+ ],
+ },
+ ],
+}
diff --git a/y2015/wpilib/wpilib_interface.cc b/y2015/wpilib/wpilib_interface.cc
new file mode 100644
index 0000000..ff836a1
--- /dev/null
+++ b/y2015/wpilib/wpilib_interface.cc
@@ -0,0 +1,716 @@
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <inttypes.h>
+
+#include <thread>
+#include <mutex>
+#include <functional>
+
+#include "aos/common/logging/logging.h"
+#include "aos/common/logging/queue_logging.h"
+#include "aos/common/time.h"
+#include "aos/common/util/log_interval.h"
+#include "aos/common/util/phased_loop.h"
+#include "aos/common/util/wrapping_counter.h"
+#include "aos/common/stl_mutex.h"
+#include "aos/linux_code/init.h"
+#include "aos/common/messages/robot_state.q.h"
+
+#include "y2015/constants.h"
+#include "frc971/control_loops/control_loops.q.h"
+#include "y2015/control_loops/drivetrain/drivetrain.q.h"
+#include "y2015/control_loops/fridge/fridge.q.h"
+#include "y2015/control_loops/claw/claw.q.h"
+
+#include "frc971/wpilib/hall_effect.h"
+#include "frc971/wpilib/joystick_sender.h"
+#include "frc971/wpilib/loop_output_handler.h"
+#include "frc971/wpilib/buffered_solenoid.h"
+#include "frc971/wpilib/buffered_pcm.h"
+#include "frc971/wpilib/gyro_sender.h"
+#include "frc971/wpilib/dma_edge_counting.h"
+#include "frc971/wpilib/interrupt_edge_counting.h"
+#include "frc971/wpilib/encoder_and_potentiometer.h"
+#include "frc971/wpilib/logging.q.h"
+
+#include "Encoder.h"
+#include "Talon.h"
+#include "DriverStation.h"
+#include "AnalogInput.h"
+#include "Compressor.h"
+#include "Relay.h"
+#include "RobotBase.h"
+#include "dma.h"
+#include "ControllerPower.h"
+
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif
+
+using ::aos::util::SimpleLogInterval;
+using ::frc971::control_loops::drivetrain_queue;
+using ::frc971::control_loops::fridge_queue;
+using ::frc971::control_loops::claw_queue;
+
+namespace frc971 {
+namespace wpilib {
+
+double drivetrain_translate(int32_t in) {
+ return static_cast<double>(in) /
+ (256.0 /*cpr*/ * 4.0 /*4x*/) *
+ constants::GetValues().drivetrain_encoder_ratio *
+ (4 /*wheel diameter*/ * 2.54 / 100.0 * M_PI);
+}
+
+double arm_translate(int32_t in) {
+ return -static_cast<double>(in) /
+ (512.0 /*cpr*/ * 4.0 /*4x*/) *
+ constants::GetValues().arm_encoder_ratio *
+ (2 * M_PI /*radians*/);
+}
+
+double arm_potentiometer_translate(double voltage) {
+ return voltage *
+ constants::GetValues().arm_pot_ratio *
+ (5.0 /*turns*/ / 5.0 /*volts*/) *
+ (2 * M_PI /*radians*/);
+}
+
+double elevator_translate(int32_t in) {
+ return static_cast<double>(in) /
+ (512.0 /*cpr*/ * 4.0 /*4x*/) *
+ constants::GetValues().elev_encoder_ratio *
+ (2 * M_PI /*radians*/) *
+ constants::GetValues().elev_distance_per_radian;
+}
+
+double elevator_potentiometer_translate(double voltage) {
+ return -voltage *
+ constants::GetValues().elev_pot_ratio *
+ (2 * M_PI /*radians*/) *
+ constants::GetValues().elev_distance_per_radian *
+ (5.0 /*turns*/ / 5.0 /*volts*/);
+}
+
+double claw_translate(int32_t in) {
+ return static_cast<double>(in) /
+ (512.0 /*cpr*/ * 4.0 /*4x*/) *
+ constants::GetValues().claw_encoder_ratio *
+ (2 * M_PI /*radians*/);
+}
+
+double claw_potentiometer_translate(double voltage) {
+ return -voltage *
+ constants::GetValues().claw_pot_ratio *
+ (5.0 /*turns*/ / 5.0 /*volts*/) *
+ (2 * M_PI /*radians*/);
+}
+
+static const double kMaximumEncoderPulsesPerSecond =
+ 19500.0 /* free speed RPM */ * 12.0 / 56.0 /* belt reduction */ /
+ 60.0 /* seconds / minute */ * 256.0 /* CPR */ *
+ 4.0 /* index pulse = 1/4 cycle */;
+
+class SensorReader {
+ public:
+ SensorReader() {
+ // Set it to filter out anything shorter than 1/4 of the minimum pulse width
+ // we should ever see.
+ filter_.SetPeriodNanoSeconds(
+ static_cast<int>(1 / 4.0 / kMaximumEncoderPulsesPerSecond * 1e9 + 0.5));
+ }
+
+ void set_arm_left_encoder(::std::unique_ptr<Encoder> encoder) {
+ filter_.Add(encoder.get());
+ arm_left_encoder_.set_encoder(::std::move(encoder));
+ }
+
+ void set_arm_left_index(::std::unique_ptr<DigitalSource> index) {
+ filter_.Add(index.get());
+ arm_left_encoder_.set_index(::std::move(index));
+ }
+
+ void set_arm_left_potentiometer(
+ ::std::unique_ptr<AnalogInput> potentiometer) {
+ arm_left_encoder_.set_potentiometer(::std::move(potentiometer));
+ }
+
+ void set_arm_right_encoder(::std::unique_ptr<Encoder> encoder) {
+ filter_.Add(encoder.get());
+ arm_right_encoder_.set_encoder(::std::move(encoder));
+ }
+
+ void set_arm_right_index(::std::unique_ptr<DigitalSource> index) {
+ filter_.Add(index.get());
+ arm_right_encoder_.set_index(::std::move(index));
+ }
+
+ void set_arm_right_potentiometer(
+ ::std::unique_ptr<AnalogInput> potentiometer) {
+ arm_right_encoder_.set_potentiometer(::std::move(potentiometer));
+ }
+
+ void set_elevator_left_encoder(::std::unique_ptr<Encoder> encoder) {
+ filter_.Add(encoder.get());
+ elevator_left_encoder_.set_encoder(::std::move(encoder));
+ }
+
+ void set_elevator_left_index(::std::unique_ptr<DigitalSource> index) {
+ filter_.Add(index.get());
+ elevator_left_encoder_.set_index(::std::move(index));
+ }
+
+ void set_elevator_left_potentiometer(
+ ::std::unique_ptr<AnalogInput> potentiometer) {
+ elevator_left_encoder_.set_potentiometer(::std::move(potentiometer));
+ }
+
+ void set_elevator_right_encoder(::std::unique_ptr<Encoder> encoder) {
+ filter_.Add(encoder.get());
+ elevator_right_encoder_.set_encoder(::std::move(encoder));
+ }
+
+ void set_elevator_right_index(::std::unique_ptr<DigitalSource> index) {
+ filter_.Add(index.get());
+ elevator_right_encoder_.set_index(::std::move(index));
+ }
+
+ void set_elevator_right_potentiometer(
+ ::std::unique_ptr<AnalogInput> potentiometer) {
+ elevator_right_encoder_.set_potentiometer(::std::move(potentiometer));
+ }
+
+ void set_wrist_encoder(::std::unique_ptr<Encoder> encoder) {
+ filter_.Add(encoder.get());
+ wrist_encoder_.set_encoder(::std::move(encoder));
+ }
+
+ void set_wrist_index(::std::unique_ptr<DigitalSource> index) {
+ filter_.Add(index.get());
+ wrist_encoder_.set_index(::std::move(index));
+ }
+
+ void set_wrist_potentiometer(::std::unique_ptr<AnalogInput> potentiometer) {
+ wrist_encoder_.set_potentiometer(::std::move(potentiometer));
+ }
+
+ void set_left_encoder(::std::unique_ptr<Encoder> left_encoder) {
+ left_encoder_ = ::std::move(left_encoder);
+ }
+
+ void set_right_encoder(::std::unique_ptr<Encoder> right_encoder) {
+ right_encoder_ = ::std::move(right_encoder);
+ }
+
+ // All of the DMA-related set_* calls must be made before this, and it doesn't
+ // hurt to do all of them.
+ void set_dma(::std::unique_ptr<DMA> dma) {
+ dma_synchronizer_.reset(new DMASynchronizer(::std::move(dma)));
+ dma_synchronizer_->Add(&arm_left_encoder_);
+ dma_synchronizer_->Add(&elevator_left_encoder_);
+ dma_synchronizer_->Add(&arm_right_encoder_);
+ dma_synchronizer_->Add(&elevator_right_encoder_);
+ }
+
+ void operator()() {
+ LOG(INFO, "In sensor reader thread\n");
+ ::aos::SetCurrentThreadName("SensorReader");
+
+ my_pid_ = getpid();
+ ds_ = DriverStation::GetInstance();
+
+ wrist_encoder_.Start();
+ dma_synchronizer_->Start();
+ LOG(INFO, "Things are now started\n");
+
+ ::aos::SetCurrentThreadRealtimePriority(kPriority);
+ while (run_) {
+ ::aos::time::PhasedLoopXMS(5, 4000);
+ RunIteration();
+ }
+
+ wrist_encoder_.Stop();
+ }
+
+ void RunIteration() {
+ {
+ auto new_state = ::aos::robot_state.MakeMessage();
+
+ new_state->reader_pid = my_pid_;
+ new_state->outputs_enabled = ds_->IsSysActive();
+ new_state->browned_out = ds_->IsSysBrownedOut();
+
+ new_state->is_3v3_active = ControllerPower::GetEnabled3V3();
+ new_state->is_5v_active = ControllerPower::GetEnabled5V();
+ new_state->voltage_3v3 = ControllerPower::GetVoltage3V3();
+ new_state->voltage_5v = ControllerPower::GetVoltage5V();
+
+ new_state->voltage_roborio_in = ControllerPower::GetInputVoltage();
+ new_state->voltage_battery = ds_->GetBatteryVoltage();
+
+ LOG_STRUCT(DEBUG, "robot_state", *new_state);
+
+ new_state.Send();
+ }
+
+ {
+ auto drivetrain_message = drivetrain_queue.position.MakeMessage();
+ drivetrain_message->right_encoder =
+ -drivetrain_translate(right_encoder_->GetRaw());
+ drivetrain_message->left_encoder =
+ drivetrain_translate(left_encoder_->GetRaw());
+
+ drivetrain_message.Send();
+ }
+
+ dma_synchronizer_->RunIteration();
+
+ const auto &values = constants::GetValues();
+
+ {
+ auto fridge_message = fridge_queue.position.MakeMessage();
+ CopyPotAndIndexPosition(arm_left_encoder_, &fridge_message->arm.left,
+ arm_translate, arm_potentiometer_translate, false,
+ values.fridge.left_arm_potentiometer_offset);
+ CopyPotAndIndexPosition(
+ arm_right_encoder_, &fridge_message->arm.right, arm_translate,
+ arm_potentiometer_translate, true,
+ values.fridge.right_arm_potentiometer_offset);
+ CopyPotAndIndexPosition(
+ elevator_left_encoder_, &fridge_message->elevator.left,
+ elevator_translate, elevator_potentiometer_translate, false,
+ values.fridge.left_elevator_potentiometer_offset);
+ CopyPotAndIndexPosition(
+ elevator_right_encoder_, &fridge_message->elevator.right,
+ elevator_translate, elevator_potentiometer_translate, true,
+ values.fridge.right_elevator_potentiometer_offset);
+ fridge_message.Send();
+ }
+
+ {
+ auto claw_message = claw_queue.position.MakeMessage();
+ CopyPotAndIndexPosition(wrist_encoder_, &claw_message->joint,
+ claw_translate, claw_potentiometer_translate,
+ false, values.claw.potentiometer_offset);
+ claw_message.Send();
+ }
+ }
+
+ void Quit() { run_ = false; }
+
+ private:
+ static const int kPriority = 30;
+ static const int kInterruptPriority = 55;
+
+ int32_t my_pid_;
+ DriverStation *ds_;
+
+ void CopyPotAndIndexPosition(
+ const DMAEncoderAndPotentiometer &encoder, PotAndIndexPosition *position,
+ ::std::function<double(int32_t)> encoder_translate,
+ ::std::function<double(double)> potentiometer_translate, bool reverse,
+ double potentiometer_offset) {
+ const double multiplier = reverse ? -1.0 : 1.0;
+ position->encoder =
+ multiplier * encoder_translate(encoder.polled_encoder_value());
+ position->pot = multiplier * potentiometer_translate(
+ encoder.polled_potentiometer_voltage()) +
+ potentiometer_offset;
+ position->latched_encoder =
+ multiplier * encoder_translate(encoder.last_encoder_value());
+ position->latched_pot =
+ multiplier *
+ potentiometer_translate(encoder.last_potentiometer_voltage()) +
+ potentiometer_offset;
+ position->index_pulses = encoder.index_posedge_count();
+ }
+
+ void CopyPotAndIndexPosition(
+ const InterruptEncoderAndPotentiometer &encoder,
+ PotAndIndexPosition *position,
+ ::std::function<double(int32_t)> encoder_translate,
+ ::std::function<double(double)> potentiometer_translate, bool reverse,
+ double potentiometer_offset) {
+ const double multiplier = reverse ? -1.0 : 1.0;
+ position->encoder =
+ multiplier * encoder_translate(encoder.encoder()->GetRaw());
+ position->pot = multiplier * potentiometer_translate(
+ encoder.potentiometer()->GetVoltage()) +
+ potentiometer_offset;
+ position->latched_encoder =
+ multiplier * encoder_translate(encoder.last_encoder_value());
+ position->latched_pot =
+ multiplier *
+ potentiometer_translate(encoder.last_potentiometer_voltage()) +
+ potentiometer_offset;
+ position->index_pulses = encoder.index_posedge_count();
+ }
+
+ ::std::unique_ptr<DMASynchronizer> dma_synchronizer_;
+
+ DMAEncoderAndPotentiometer arm_left_encoder_, arm_right_encoder_,
+ elevator_left_encoder_, elevator_right_encoder_;
+
+ InterruptEncoderAndPotentiometer wrist_encoder_{kInterruptPriority};
+
+ ::std::unique_ptr<Encoder> left_encoder_;
+ ::std::unique_ptr<Encoder> right_encoder_;
+
+ ::std::atomic<bool> run_{true};
+ DigitalGlitchFilter filter_;
+};
+
+class SolenoidWriter {
+ public:
+ SolenoidWriter(const ::std::unique_ptr<BufferedPcm> &pcm)
+ : pcm_(pcm),
+ fridge_(".frc971.control_loops.fridge_queue.output"),
+ claw_(".frc971.control_loops.claw_queue.output") {}
+
+ void set_pressure_switch(::std::unique_ptr<DigitalSource> pressure_switch) {
+ pressure_switch_ = ::std::move(pressure_switch);
+ }
+
+ void set_compressor_relay(::std::unique_ptr<Relay> compressor_relay) {
+ compressor_relay_ = ::std::move(compressor_relay);
+ }
+
+ void set_fridge_grabbers_top_front(::std::unique_ptr<BufferedSolenoid> s) {
+ fridge_grabbers_top_front_ = ::std::move(s);
+ }
+
+ void set_fridge_grabbers_top_back(::std::unique_ptr<BufferedSolenoid> s) {
+ fridge_grabbers_top_back_ = ::std::move(s);
+ }
+
+ void set_fridge_grabbers_bottom_front(
+ ::std::unique_ptr<BufferedSolenoid> s) {
+ fridge_grabbers_bottom_front_ = ::std::move(s);
+ }
+
+ void set_fridge_grabbers_bottom_back(
+ ::std::unique_ptr<BufferedSolenoid> s) {
+ fridge_grabbers_bottom_back_ = ::std::move(s);
+ }
+
+ void set_claw_pinchers(::std::unique_ptr<BufferedSolenoid> s) {
+ claw_pinchers_ = ::std::move(s);
+ }
+
+ void set_grabber_latch_release(::std::unique_ptr<BufferedSolenoid> s) {
+ grabber_latch_release_ = ::std::move(s);
+ }
+
+ void set_grabber_fold_up(::std::unique_ptr<BufferedSolenoid> s) {
+ grabber_fold_up_ = ::std::move(s);
+ }
+
+ void operator()() {
+ ::aos::SetCurrentThreadName("Solenoids");
+ ::aos::SetCurrentThreadRealtimePriority(30);
+
+ while (run_) {
+ ::aos::time::PhasedLoopXMS(20, 1000);
+
+ {
+ fridge_.FetchLatest();
+ if (fridge_.get()) {
+ LOG_STRUCT(DEBUG, "solenoids", *fridge_);
+ fridge_grabbers_top_front_->Set(!fridge_->grabbers.top_front);
+ fridge_grabbers_top_back_->Set(!fridge_->grabbers.top_back);
+ fridge_grabbers_bottom_front_->Set(!fridge_->grabbers.bottom_front);
+ fridge_grabbers_bottom_back_->Set(!fridge_->grabbers.bottom_back);
+ }
+ }
+
+ {
+ claw_.FetchLatest();
+ if (claw_.get()) {
+ LOG_STRUCT(DEBUG, "solenoids", *claw_);
+ claw_pinchers_->Set(claw_->rollers_closed);
+ }
+ }
+
+ ::aos::joystick_state.FetchLatest();
+ grabber_latch_release_->Set(::aos::joystick_state.get() != nullptr &&
+ ::aos::joystick_state->autonomous);
+ grabber_fold_up_->Set(::aos::joystick_state.get() != nullptr &&
+ ::aos::joystick_state->joysticks[1].buttons & 1);
+
+ {
+ PneumaticsToLog to_log;
+ {
+ const bool compressor_on = !pressure_switch_->Get();
+ to_log.compressor_on = compressor_on;
+ if (compressor_on) {
+ compressor_relay_->Set(Relay::kForward);
+ } else {
+ compressor_relay_->Set(Relay::kOff);
+ }
+ }
+
+ pcm_->Flush();
+ to_log.read_solenoids = pcm_->GetAll();
+ LOG_STRUCT(DEBUG, "pneumatics info", to_log);
+ }
+ }
+ }
+
+ void Quit() { run_ = false; }
+
+ private:
+ const ::std::unique_ptr<BufferedPcm> &pcm_;
+ ::std::unique_ptr<BufferedSolenoid> fridge_grabbers_top_front_;
+ ::std::unique_ptr<BufferedSolenoid> fridge_grabbers_top_back_;
+ ::std::unique_ptr<BufferedSolenoid> fridge_grabbers_bottom_front_;
+ ::std::unique_ptr<BufferedSolenoid> fridge_grabbers_bottom_back_;
+ ::std::unique_ptr<BufferedSolenoid> claw_pinchers_;
+ ::std::unique_ptr<BufferedSolenoid> grabber_latch_release_;
+ ::std::unique_ptr<BufferedSolenoid> grabber_fold_up_;
+ ::std::unique_ptr<DigitalSource> pressure_switch_;
+ ::std::unique_ptr<Relay> compressor_relay_;
+
+ ::aos::Queue<::frc971::control_loops::FridgeQueue::Output> fridge_;
+ ::aos::Queue<::frc971::control_loops::ClawQueue::Output> claw_;
+
+ ::std::atomic<bool> run_{true};
+};
+
+class DrivetrainWriter : public LoopOutputHandler {
+ public:
+ void set_left_drivetrain_talon(::std::unique_ptr<Talon> t) {
+ left_drivetrain_talon_ = ::std::move(t);
+ }
+
+ void set_right_drivetrain_talon(::std::unique_ptr<Talon> t) {
+ right_drivetrain_talon_ = ::std::move(t);
+ }
+
+ private:
+ virtual void Read() override {
+ ::frc971::control_loops::drivetrain_queue.output.FetchAnother();
+ }
+
+ virtual void Write() override {
+ auto &queue = ::frc971::control_loops::drivetrain_queue.output;
+ LOG_STRUCT(DEBUG, "will output", *queue);
+ left_drivetrain_talon_->Set(queue->left_voltage / 12.0);
+ right_drivetrain_talon_->Set(-queue->right_voltage / 12.0);
+ }
+
+ virtual void Stop() override {
+ LOG(WARNING, "drivetrain output too old\n");
+ left_drivetrain_talon_->Disable();
+ right_drivetrain_talon_->Disable();
+ }
+
+ ::std::unique_ptr<Talon> left_drivetrain_talon_;
+ ::std::unique_ptr<Talon> right_drivetrain_talon_;
+};
+
+class FridgeWriter : public LoopOutputHandler {
+ public:
+ void set_left_arm_talon(::std::unique_ptr<Talon> t) {
+ left_arm_talon_ = ::std::move(t);
+ }
+
+ void set_right_arm_talon(::std::unique_ptr<Talon> t) {
+ right_arm_talon_ = ::std::move(t);
+ }
+
+ void set_left_elevator_talon(::std::unique_ptr<Talon> t) {
+ left_elevator_talon_ = ::std::move(t);
+ }
+
+ void set_right_elevator_talon(::std::unique_ptr<Talon> t) {
+ right_elevator_talon_ = ::std::move(t);
+ }
+
+ private:
+ virtual void Read() override {
+ ::frc971::control_loops::fridge_queue.output.FetchAnother();
+ }
+
+ virtual void Write() override {
+ auto &queue = ::frc971::control_loops::fridge_queue.output;
+ LOG_STRUCT(DEBUG, "will output", *queue);
+ left_arm_talon_->Set(queue->left_arm / 12.0);
+ right_arm_talon_->Set(-queue->right_arm / 12.0);
+ left_elevator_talon_->Set(queue->left_elevator / 12.0);
+ right_elevator_talon_->Set(-queue->right_elevator / 12.0);
+ }
+
+ virtual void Stop() override {
+ LOG(WARNING, "Fridge output too old.\n");
+ left_arm_talon_->Disable();
+ right_arm_talon_->Disable();
+ left_elevator_talon_->Disable();
+ right_elevator_talon_->Disable();
+ }
+
+ ::std::unique_ptr<Talon> left_arm_talon_;
+ ::std::unique_ptr<Talon> right_arm_talon_;
+ ::std::unique_ptr<Talon> left_elevator_talon_;
+ ::std::unique_ptr<Talon> right_elevator_talon_;
+};
+
+class ClawWriter : public LoopOutputHandler {
+ public:
+ void set_left_intake_talon(::std::unique_ptr<Talon> t) {
+ left_intake_talon_ = ::std::move(t);
+ }
+
+ void set_right_intake_talon(::std::unique_ptr<Talon> t) {
+ right_intake_talon_ = ::std::move(t);
+ }
+
+ void set_wrist_talon(::std::unique_ptr<Talon> t) {
+ wrist_talon_ = ::std::move(t);
+ }
+
+ private:
+ virtual void Read() override {
+ ::frc971::control_loops::claw_queue.output.FetchAnother();
+ }
+
+ virtual void Write() override {
+ auto &queue = ::frc971::control_loops::claw_queue.output;
+ LOG_STRUCT(DEBUG, "will output", *queue);
+ left_intake_talon_->Set(queue->intake_voltage / 12.0);
+ right_intake_talon_->Set(-queue->intake_voltage / 12.0);
+ wrist_talon_->Set(-queue->voltage / 12.0);
+ }
+
+ virtual void Stop() override {
+ LOG(WARNING, "Claw output too old.\n");
+ left_intake_talon_->Disable();
+ right_intake_talon_->Disable();
+ wrist_talon_->Disable();
+ }
+
+ ::std::unique_ptr<Talon> left_intake_talon_;
+ ::std::unique_ptr<Talon> right_intake_talon_;
+ ::std::unique_ptr<Talon> wrist_talon_;
+};
+
+// TODO(brian): Replace this with ::std::make_unique once all our toolchains
+// have support.
+template <class T, class... U>
+std::unique_ptr<T> make_unique(U &&... u) {
+ return std::unique_ptr<T>(new T(std::forward<U>(u)...));
+}
+
+class WPILibRobot : public RobotBase {
+ public:
+ ::std::unique_ptr<Encoder> encoder(int index) {
+ return make_unique<Encoder>(10 + index * 2, 11 + index * 2, false,
+ Encoder::k4X);
+ }
+ virtual void StartCompetition() {
+ ::aos::InitNRT();
+ ::aos::SetCurrentThreadName("StartCompetition");
+
+ JoystickSender joystick_sender;
+ ::std::thread joystick_thread(::std::ref(joystick_sender));
+ // TODO(austin): Compressor needs to use a spike.
+
+ SensorReader reader;
+ LOG(INFO, "Creating the reader\n");
+ reader.set_arm_left_encoder(encoder(1));
+ reader.set_arm_left_index(make_unique<DigitalInput>(1));
+ reader.set_arm_left_potentiometer(make_unique<AnalogInput>(1));
+
+ reader.set_arm_right_encoder(encoder(5));
+ reader.set_arm_right_index(make_unique<DigitalInput>(5));
+ reader.set_arm_right_potentiometer(make_unique<AnalogInput>(5));
+
+ reader.set_elevator_left_encoder(encoder(0));
+ reader.set_elevator_left_index(make_unique<DigitalInput>(0));
+ reader.set_elevator_left_potentiometer(make_unique<AnalogInput>(0));
+
+ reader.set_elevator_right_encoder(encoder(4));
+ reader.set_elevator_right_index(make_unique<DigitalInput>(4));
+ reader.set_elevator_right_potentiometer(make_unique<AnalogInput>(4));
+
+ reader.set_wrist_encoder(encoder(6));
+ reader.set_wrist_index(make_unique<DigitalInput>(6));
+ reader.set_wrist_potentiometer(make_unique<AnalogInput>(6));
+
+ reader.set_left_encoder(encoder(2));
+ reader.set_right_encoder(encoder(3));
+ reader.set_dma(make_unique<DMA>());
+ ::std::thread reader_thread(::std::ref(reader));
+ GyroSender gyro_sender;
+ ::std::thread gyro_thread(::std::ref(gyro_sender));
+
+ DrivetrainWriter drivetrain_writer;
+ drivetrain_writer.set_left_drivetrain_talon(
+ ::std::unique_ptr<Talon>(new Talon(8)));
+ drivetrain_writer.set_right_drivetrain_talon(
+ ::std::unique_ptr<Talon>(new Talon(0)));
+ ::std::thread drivetrain_writer_thread(::std::ref(drivetrain_writer));
+
+ // TODO(sensors): Get real PWM output and relay numbers for the fridge and
+ // claw.
+ FridgeWriter fridge_writer;
+ fridge_writer.set_left_arm_talon(
+ ::std::unique_ptr<Talon>(new Talon(6)));
+ fridge_writer.set_right_arm_talon(
+ ::std::unique_ptr<Talon>(new Talon(2)));
+ fridge_writer.set_left_elevator_talon(
+ ::std::unique_ptr<Talon>(new Talon(7)));
+ fridge_writer.set_right_elevator_talon(
+ ::std::unique_ptr<Talon>(new Talon(1)));
+ ::std::thread fridge_writer_thread(::std::ref(fridge_writer));
+
+ ClawWriter claw_writer;
+ claw_writer.set_left_intake_talon(
+ ::std::unique_ptr<Talon>(new Talon(5)));
+ claw_writer.set_right_intake_talon(
+ ::std::unique_ptr<Talon>(new Talon(3)));
+ claw_writer.set_wrist_talon(
+ ::std::unique_ptr<Talon>(new Talon(4)));
+ ::std::thread claw_writer_thread(::std::ref(claw_writer));
+
+ ::std::unique_ptr<::frc971::wpilib::BufferedPcm> pcm(
+ new ::frc971::wpilib::BufferedPcm());
+ SolenoidWriter solenoid_writer(pcm);
+ solenoid_writer.set_fridge_grabbers_top_front(pcm->MakeSolenoid(0));
+ solenoid_writer.set_fridge_grabbers_top_back(pcm->MakeSolenoid(0));
+ solenoid_writer.set_fridge_grabbers_bottom_front(pcm->MakeSolenoid(2));
+ solenoid_writer.set_fridge_grabbers_bottom_back(pcm->MakeSolenoid(1));
+ solenoid_writer.set_claw_pinchers(pcm->MakeSolenoid(4));
+ solenoid_writer.set_grabber_latch_release(pcm->MakeSolenoid(7));
+ solenoid_writer.set_grabber_fold_up(pcm->MakeSolenoid(5));
+
+ solenoid_writer.set_pressure_switch(make_unique<DigitalInput>(9));
+ solenoid_writer.set_compressor_relay(make_unique<Relay>(0));
+ ::std::thread solenoid_thread(::std::ref(solenoid_writer));
+
+ // Wait forever. Not much else to do...
+ PCHECK(select(0, nullptr, nullptr, nullptr, nullptr));
+
+ LOG(ERROR, "Exiting WPILibRobot\n");
+
+ joystick_sender.Quit();
+ joystick_thread.join();
+ reader.Quit();
+ reader_thread.join();
+ gyro_sender.Quit();
+ gyro_thread.join();
+
+ drivetrain_writer.Quit();
+ drivetrain_writer_thread.join();
+ solenoid_writer.Quit();
+ solenoid_thread.join();
+
+ ::aos::Cleanup();
+ }
+};
+
+} // namespace wpilib
+} // namespace frc971
+
+
+START_ROBOT_CLASS(::frc971::wpilib::WPILibRobot);
diff --git a/y2015/y2015.gyp b/y2015/y2015.gyp
new file mode 100644
index 0000000..7f929d9
--- /dev/null
+++ b/y2015/y2015.gyp
@@ -0,0 +1,53 @@
+{
+ 'targets': [
+ {
+ 'target_name': 'constants',
+ 'type': 'static_library',
+ 'sources': [
+ 'constants.cc',
+ ],
+ 'dependencies': [
+ '<(AOS)/build/aos.gyp:logging',
+ '<(AOS)/common/common.gyp:once',
+ '<(AOS)/common/network/network.gyp:team_number',
+ '<(DEPTH)/frc971/control_loops/control_loops.gyp:state_feedback_loop',
+ '<(DEPTH)/y2015/control_loops/drivetrain/drivetrain.gyp:polydrivetrain_plants',
+ ],
+ 'export_dependent_settings': [
+ '<(DEPTH)/frc971/control_loops/control_loops.gyp:state_feedback_loop',
+ ],
+ },
+ {
+ 'target_name': 'joystick_reader',
+ 'type': 'executable',
+ 'sources': [
+ 'joystick_reader.cc',
+ ],
+ 'dependencies': [
+ '<(AOS)/prime/input/input.gyp:joystick_input',
+ '<(AOS)/linux_code/linux_code.gyp:init',
+ '<(AOS)/build/aos.gyp:logging',
+ '<(AOS)/common/common.gyp:time',
+ '<(AOS)/common/util/util.gyp:log_interval',
+ '<(AOS)/common/actions/actions.gyp:action_lib',
+
+ '<(DEPTH)/frc971/queues/queues.gyp:gyro',
+ '<(DEPTH)/y2015/control_loops/claw/claw.gyp:claw_queue',
+ '<(DEPTH)/y2015/control_loops/drivetrain/drivetrain.gyp:drivetrain_queue',
+ '<(DEPTH)/y2015/control_loops/fridge/fridge.gyp:fridge_queue',
+ '<(DEPTH)/y2015/y2015.gyp:constants',
+ '<(DEPTH)/frc971/autonomous/autonomous.gyp:auto_queue',
+ '<(DEPTH)/y2015/actors/actors.gyp:stack_action_lib',
+ '<(DEPTH)/y2015/actors/actors.gyp:stack_and_lift_action_lib',
+ '<(DEPTH)/y2015/actors/actors.gyp:stack_and_hold_action_lib',
+ '<(DEPTH)/y2015/actors/actors.gyp:pickup_action_lib',
+ '<(DEPTH)/y2015/actors/actors.gyp:lift_action_lib',
+ '<(DEPTH)/y2015/actors/actors.gyp:held_to_lift_action_lib',
+ '<(DEPTH)/y2015/actors/actors.gyp:can_pickup_action_lib',
+ '<(DEPTH)/y2015/actors/actors.gyp:score_action_lib',
+ '<(DEPTH)/y2015/actors/actors.gyp:horizontal_can_pickup_action_lib',
+ '<(DEPTH)/y2015/actors/actors.gyp:fridge_profile_lib',
+ ],
+ },
+ ],
+}